aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-08-20 20:51:32 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-08-20 20:51:32 +0000
commitf1e1c239e31b467e17f1648b1f524fc9ab5b431a (patch)
treea855e7a2a8808555da60e6aa9601d6867eb23bac
parent7d6988fdd2aee0e033034e147f16fe05594a60e4 (diff)
downloadsrc-f1e1c239e31b467e17f1648b1f524fc9ab5b431a.tar.gz
src-f1e1c239e31b467e17f1648b1f524fc9ab5b431a.zip
Vendor import of stripped lld trunk r366426 (just before the release_90vendor/lld/lld-trunk-r366426
Notes
Notes: svn path=/vendor/lld/dist/; revision=351288 svn path=/vendor/lld/lld-trunk-r366426/; revision=351289; tag=vendor/lld/lld-trunk-r366426
-rw-r--r--COFF/CMakeLists.txt1
-rw-r--r--COFF/Chunks.cpp855
-rw-r--r--COFF/Chunks.h574
-rw-r--r--COFF/Config.h241
-rw-r--r--COFF/DLL.cpp703
-rw-r--r--COFF/DLL.h60
-rw-r--r--COFF/DebugTypes.cpp268
-rw-r--r--COFF/DebugTypes.h60
-rw-r--r--COFF/Driver.cpp1922
-rw-r--r--COFF/Driver.h111
-rw-r--r--COFF/DriverUtils.cpp965
-rw-r--r--COFF/ICF.cpp283
-rw-r--r--COFF/ICF.h9
-rw-r--r--COFF/InputFiles.cpp915
-rw-r--r--COFF/InputFiles.h209
-rw-r--r--COFF/LTO.cpp188
-rw-r--r--COFF/LTO.h19
-rw-r--r--COFF/MapFile.cpp115
-rw-r--r--COFF/MapFile.h9
-rw-r--r--COFF/MarkLive.cpp65
-rw-r--r--COFF/MarkLive.h11
-rw-r--r--COFF/MinGW.cpp160
-rw-r--r--COFF/MinGW.h25
-rw-r--r--COFF/Options.td65
-rw-r--r--COFF/PDB.cpp2035
-rw-r--r--COFF/PDB.h19
-rw-r--r--COFF/SymbolTable.cpp729
-rw-r--r--COFF/SymbolTable.h88
-rw-r--r--COFF/Symbols.cpp106
-rw-r--r--COFF/Symbols.h243
-rw-r--r--COFF/TypeMerger.h65
-rw-r--r--COFF/Writer.cpp2044
-rw-r--r--COFF/Writer.h62
-rw-r--r--Common/Args.cpp87
-rw-r--r--Common/CMakeLists.txt27
-rw-r--r--Common/ErrorHandler.cpp160
-rw-r--r--Common/Filesystem.cpp (renamed from ELF/Filesystem.cpp)55
-rw-r--r--Common/Memory.cpp19
-rw-r--r--Common/Reproduce.cpp67
-rw-r--r--Common/Strings.cpp99
-rw-r--r--Common/TargetOptionsCommandFlags.cpp18
-rw-r--r--Common/Threads.cpp9
-rw-r--r--Common/Timer.cpp61
-rw-r--r--Common/Version.cpp42
-rw-r--r--ELF/AArch64ErrataFix.cpp495
-rw-r--r--ELF/AArch64ErrataFix.h19
-rw-r--r--ELF/Arch/AArch64.cpp466
-rw-r--r--ELF/Arch/AMDGPU.cpp70
-rw-r--r--ELF/Arch/ARM.cpp462
-rw-r--r--ELF/Arch/AVR.cpp37
-rw-r--r--ELF/Arch/Hexagon.cpp195
-rw-r--r--ELF/Arch/MSP430.cpp49
-rw-r--r--ELF/Arch/Mips.cpp607
-rw-r--r--ELF/Arch/MipsArchTree.cpp239
-rw-r--r--ELF/Arch/PPC.cpp407
-rw-r--r--ELF/Arch/PPC64.cpp730
-rw-r--r--ELF/Arch/RISCV.cpp401
-rw-r--r--ELF/Arch/SPARCV9.cpp102
-rw-r--r--ELF/Arch/X86.cpp442
-rw-r--r--ELF/Arch/X86_64.cpp651
-rw-r--r--ELF/Bits.h35
-rw-r--r--ELF/CMakeLists.txt1
-rw-r--r--ELF/CallGraphSort.cpp185
-rw-r--r--ELF/CallGraphSort.h7
-rw-r--r--ELF/Config.h356
-rw-r--r--ELF/DWARF.cpp132
-rw-r--r--ELF/DWARF.h65
-rw-r--r--ELF/Driver.cpp1899
-rw-r--r--ELF/Driver.h42
-rw-r--r--ELF/DriverUtils.cpp195
-rw-r--r--ELF/EhFrame.cpp137
-rw-r--r--ELF/EhFrame.h11
-rw-r--r--ELF/Filesystem.h23
-rw-r--r--ELF/ICF.cpp337
-rw-r--r--ELF/ICF.h7
-rw-r--r--ELF/InputFiles.cpp1706
-rw-r--r--ELF/InputFiles.h358
-rw-r--r--ELF/InputSection.cpp1113
-rw-r--r--ELF/InputSection.h278
-rw-r--r--ELF/LTO.cpp327
-rw-r--r--ELF/LTO.h21
-rw-r--r--ELF/LinkerScript.cpp1018
-rw-r--r--ELF/LinkerScript.h222
-rw-r--r--ELF/MapFile.cpp234
-rw-r--r--ELF/MapFile.h7
-rw-r--r--ELF/MarkLive.cpp385
-rw-r--r--ELF/MarkLive.h7
-rw-r--r--ELF/Options.td60
-rw-r--r--ELF/OutputSections.cpp442
-rw-r--r--ELF/OutputSections.h122
-rw-r--r--ELF/Relocations.cpp1663
-rw-r--r--ELF/Relocations.h178
-rw-r--r--ELF/ScriptLexer.cpp224
-rw-r--r--ELF/ScriptLexer.h29
-rw-r--r--ELF/ScriptParser.cpp1313
-rw-r--r--ELF/ScriptParser.h17
-rw-r--r--ELF/SymbolTable.cpp835
-rw-r--r--ELF/SymbolTable.h98
-rw-r--r--ELF/Symbols.cpp627
-rw-r--r--ELF/Symbols.h461
-rw-r--r--ELF/SyntheticSections.cpp3698
-rw-r--r--ELF/SyntheticSections.h893
-rw-r--r--ELF/Target.cpp101
-rw-r--r--ELF/Target.h233
-rw-r--r--ELF/Thunks.cpp809
-rw-r--r--ELF/Thunks.h42
-rw-r--r--ELF/Writer.cpp2668
-rw-r--r--ELF/Writer.h29
-rw-r--r--LICENSE.TXT256
-rw-r--r--docs/NewLLD.rst8
-rw-r--r--docs/Partitions.rst116
-rw-r--r--docs/ReleaseNotes.rst63
-rw-r--r--docs/WebAssembly.rst78
-rw-r--r--docs/conf.py4
-rw-r--r--docs/getting_started.rst35
-rw-r--r--docs/index.rst4
-rw-r--r--docs/ld.lld.1119
-rw-r--r--docs/missingkeyfunction.rst35
-rw-r--r--docs/partitions.dot22
-rw-r--r--docs/partitions.svg110
-rw-r--r--docs/sphinx_intro.rst4
-rw-r--r--include/lld/Common/Args.h23
-rw-r--r--include/lld/Common/Driver.h27
-rw-r--r--include/lld/Common/ErrorHandler.h98
-rw-r--r--include/lld/Common/Filesystem.h20
-rw-r--r--include/lld/Common/LLVM.h9
-rw-r--r--include/lld/Common/Memory.h25
-rw-r--r--include/lld/Common/Reproduce.h17
-rw-r--r--include/lld/Common/Strings.h23
-rw-r--r--include/lld/Common/TargetOptionsCommandFlags.h15
-rw-r--r--include/lld/Common/Threads.h34
-rw-r--r--include/lld/Common/Timer.h29
-rw-r--r--include/lld/Common/Version.h7
-rw-r--r--include/lld/Core/AbsoluteAtom.h7
-rw-r--r--include/lld/Core/ArchiveLibraryFile.h7
-rw-r--r--include/lld/Core/Atom.h7
-rw-r--r--include/lld/Core/DefinedAtom.h7
-rw-r--r--include/lld/Core/Error.h7
-rw-r--r--include/lld/Core/File.h7
-rw-r--r--include/lld/Core/Instrumentation.h7
-rw-r--r--include/lld/Core/LinkingContext.h7
-rw-r--r--include/lld/Core/Node.h7
-rw-r--r--include/lld/Core/Pass.h7
-rw-r--r--include/lld/Core/PassManager.h7
-rw-r--r--include/lld/Core/Reader.h7
-rw-r--r--include/lld/Core/Reference.h7
-rw-r--r--include/lld/Core/Resolver.h7
-rw-r--r--include/lld/Core/SharedLibraryAtom.h7
-rw-r--r--include/lld/Core/SharedLibraryFile.h7
-rw-r--r--include/lld/Core/Simple.h7
-rw-r--r--include/lld/Core/SymbolTable.h7
-rw-r--r--include/lld/Core/UndefinedAtom.h7
-rw-r--r--include/lld/Core/Writer.h7
-rw-r--r--include/lld/ReaderWriter/MachOLinkingContext.h7
-rw-r--r--include/lld/ReaderWriter/YamlContext.h7
-rw-r--r--lib/Core/DefinedAtom.cpp7
-rw-r--r--lib/Core/Error.cpp7
-rw-r--r--lib/Core/File.cpp7
-rw-r--r--lib/Core/LinkingContext.cpp7
-rw-r--r--lib/Core/Reader.cpp7
-rw-r--r--lib/Core/Resolver.cpp7
-rw-r--r--lib/Core/SymbolTable.cpp7
-rw-r--r--lib/Core/Writer.cpp7
-rw-r--r--lib/Driver/DarwinLdDriver.cpp31
-rw-r--r--lib/ReaderWriter/FileArchive.cpp7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler.cpp7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler.h7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_arm.cpp7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_arm64.cpp7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_x86.cpp7
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp7
-rw-r--r--lib/ReaderWriter/MachO/Atoms.h7
-rw-r--r--lib/ReaderWriter/MachO/CompactUnwindPass.cpp19
-rw-r--r--lib/ReaderWriter/MachO/DebugInfo.h7
-rw-r--r--lib/ReaderWriter/MachO/ExecutableAtoms.h7
-rw-r--r--lib/ReaderWriter/MachO/File.h7
-rw-r--r--lib/ReaderWriter/MachO/FlatNamespaceFile.h7
-rw-r--r--lib/ReaderWriter/MachO/GOTPass.cpp7
-rw-r--r--lib/ReaderWriter/MachO/LayoutPass.cpp7
-rw-r--r--lib/ReaderWriter/MachO/LayoutPass.h7
-rw-r--r--lib/ReaderWriter/MachO/MachOLinkingContext.cpp19
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFile.h10
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp7
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h7
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp34
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp10
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp7
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp7
-rw-r--r--lib/ReaderWriter/MachO/MachOPasses.h7
-rw-r--r--lib/ReaderWriter/MachO/ObjCPass.cpp7
-rw-r--r--lib/ReaderWriter/MachO/SectCreateFile.h7
-rw-r--r--lib/ReaderWriter/MachO/ShimPass.cpp7
-rw-r--r--lib/ReaderWriter/MachO/StubsPass.cpp12
-rw-r--r--lib/ReaderWriter/MachO/TLVPass.cpp7
-rw-r--r--lib/ReaderWriter/MachO/WriterMachO.cpp7
-rw-r--r--lib/ReaderWriter/YAML/ReaderWriterYAML.cpp7
-rw-r--r--tools/lld/CMakeLists.txt1
-rw-r--r--tools/lld/lld.cpp107
198 files changed, 24846 insertions, 19860 deletions
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt
index bb241e788c19..c7ef7c47fea1 100644
--- a/COFF/CMakeLists.txt
+++ b/COFF/CMakeLists.txt
@@ -8,6 +8,7 @@ endif()
add_lld_library(lldCOFF
Chunks.cpp
+ DebugTypes.cpp
DLL.cpp
Driver.cpp
DriverUtils.cpp
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp
index 29131d7eb8db..0e43d2b478b4 100644
--- a/COFF/Chunks.cpp
+++ b/COFF/Chunks.cpp
@@ -1,9 +1,8 @@
//===- Chunks.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -30,203 +29,201 @@ using llvm::support::ulittle32_t;
namespace lld {
namespace coff {
-SectionChunk::SectionChunk(ObjFile *F, const coff_section *H)
- : Chunk(SectionKind), Repl(this), Header(H), File(F),
- Relocs(File->getCOFFObj()->getRelocations(Header)) {
- // Initialize SectionName.
- File->getCOFFObj()->getSectionName(Header, SectionName);
+SectionChunk::SectionChunk(ObjFile *f, const coff_section *h)
+ : Chunk(SectionKind), file(f), header(h), repl(this) {
+ // Initialize relocs.
+ setRelocs(file->getCOFFObj()->getRelocations(header));
+
+ // Initialize sectionName.
+ StringRef sectionName;
+ if (Expected<StringRef> e = file->getCOFFObj()->getSectionName(header))
+ sectionName = *e;
+ sectionNameData = sectionName.data();
+ sectionNameSize = sectionName.size();
- Alignment = Header->getAlignment();
+ setAlignment(header->getAlignment());
+
+ hasData = !(header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
// If linker GC is disabled, every chunk starts out alive. If linker GC is
// enabled, treat non-comdat sections as roots. Generally optimized object
// files will be built with -ffunction-sections or /Gy, so most things worth
// stripping will be in a comdat.
- Live = !Config->DoGC || !isCOMDAT();
-}
-
-// Initialize the RelocTargets vector, to allow redirecting certain relocations
-// to a thunk instead of the actual symbol the relocation's symbol table index
-// indicates.
-void SectionChunk::readRelocTargets() {
- assert(RelocTargets.empty());
- RelocTargets.reserve(Relocs.size());
- for (const coff_relocation &Rel : Relocs)
- RelocTargets.push_back(File->getSymbol(Rel.SymbolTableIndex));
+ live = !config->doGC || !isCOMDAT();
}
-// Reset RelocTargets to their original targets before thunks were added.
-void SectionChunk::resetRelocTargets() {
- for (size_t I = 0, E = Relocs.size(); I < E; ++I)
- RelocTargets[I] = File->getSymbol(Relocs[I].SymbolTableIndex);
-}
+// SectionChunk is one of the most frequently allocated classes, so it is
+// important to keep it as compact as possible. As of this writing, the number
+// below is the size of this class on x64 platforms.
+static_assert(sizeof(SectionChunk) <= 88, "SectionChunk grew unexpectedly");
-static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); }
-static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); }
-static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); }
-static void or16(uint8_t *P, uint16_t V) { write16le(P, read16le(P) | V); }
-static void or32(uint8_t *P, uint32_t V) { write32le(P, read32le(P) | V); }
+static void add16(uint8_t *p, int16_t v) { write16le(p, read16le(p) + v); }
+static void add32(uint8_t *p, int32_t v) { write32le(p, read32le(p) + v); }
+static void add64(uint8_t *p, int64_t v) { write64le(p, read64le(p) + v); }
+static void or16(uint8_t *p, uint16_t v) { write16le(p, read16le(p) | v); }
+static void or32(uint8_t *p, uint32_t v) { write32le(p, read32le(p) | v); }
// Verify that given sections are appropriate targets for SECREL
// relocations. This check is relaxed because unfortunately debug
// sections have section-relative relocations against absolute symbols.
-static bool checkSecRel(const SectionChunk *Sec, OutputSection *OS) {
- if (OS)
+static bool checkSecRel(const SectionChunk *sec, OutputSection *os) {
+ if (os)
return true;
- if (Sec->isCodeView())
+ if (sec->isCodeView())
return false;
error("SECREL relocation cannot be applied to absolute symbols");
return false;
}
-static void applySecRel(const SectionChunk *Sec, uint8_t *Off,
- OutputSection *OS, uint64_t S) {
- if (!checkSecRel(Sec, OS))
+static void applySecRel(const SectionChunk *sec, uint8_t *off,
+ OutputSection *os, uint64_t s) {
+ if (!checkSecRel(sec, os))
return;
- uint64_t SecRel = S - OS->getRVA();
- if (SecRel > UINT32_MAX) {
- error("overflow in SECREL relocation in section: " + Sec->getSectionName());
+ uint64_t secRel = s - os->getRVA();
+ if (secRel > UINT32_MAX) {
+ error("overflow in SECREL relocation in section: " + sec->getSectionName());
return;
}
- add32(Off, SecRel);
+ add32(off, secRel);
}
-static void applySecIdx(uint8_t *Off, OutputSection *OS) {
+static void applySecIdx(uint8_t *off, OutputSection *os) {
// Absolute symbol doesn't have section index, but section index relocation
// against absolute symbol should be resolved to one plus the last output
// section index. This is required for compatibility with MSVC.
- if (OS)
- add16(Off, OS->SectionIndex);
+ if (os)
+ add16(off, os->sectionIndex);
else
- add16(Off, DefinedAbsolute::NumOutputSections + 1);
-}
-
-void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS,
- uint64_t S, uint64_t P) const {
- switch (Type) {
- case IMAGE_REL_AMD64_ADDR32: add32(Off, S + Config->ImageBase); break;
- case IMAGE_REL_AMD64_ADDR64: add64(Off, S + Config->ImageBase); break;
- case IMAGE_REL_AMD64_ADDR32NB: add32(Off, S); break;
- case IMAGE_REL_AMD64_REL32: add32(Off, S - P - 4); break;
- case IMAGE_REL_AMD64_REL32_1: add32(Off, S - P - 5); break;
- case IMAGE_REL_AMD64_REL32_2: add32(Off, S - P - 6); break;
- case IMAGE_REL_AMD64_REL32_3: add32(Off, S - P - 7); break;
- case IMAGE_REL_AMD64_REL32_4: add32(Off, S - P - 8); break;
- case IMAGE_REL_AMD64_REL32_5: add32(Off, S - P - 9); break;
- case IMAGE_REL_AMD64_SECTION: applySecIdx(Off, OS); break;
- case IMAGE_REL_AMD64_SECREL: applySecRel(this, Off, OS, S); break;
+ add16(off, DefinedAbsolute::numOutputSections + 1);
+}
+
+void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
+ uint64_t s, uint64_t p) const {
+ switch (type) {
+ case IMAGE_REL_AMD64_ADDR32: add32(off, s + config->imageBase); break;
+ case IMAGE_REL_AMD64_ADDR64: add64(off, s + config->imageBase); break;
+ case IMAGE_REL_AMD64_ADDR32NB: add32(off, s); break;
+ case IMAGE_REL_AMD64_REL32: add32(off, s - p - 4); break;
+ case IMAGE_REL_AMD64_REL32_1: add32(off, s - p - 5); break;
+ case IMAGE_REL_AMD64_REL32_2: add32(off, s - p - 6); break;
+ case IMAGE_REL_AMD64_REL32_3: add32(off, s - p - 7); break;
+ case IMAGE_REL_AMD64_REL32_4: add32(off, s - p - 8); break;
+ case IMAGE_REL_AMD64_REL32_5: add32(off, s - p - 9); break;
+ case IMAGE_REL_AMD64_SECTION: applySecIdx(off, os); break;
+ case IMAGE_REL_AMD64_SECREL: applySecRel(this, off, os, s); break;
default:
- error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
- toString(File));
+ error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
+ toString(file));
}
}
-void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS,
- uint64_t S, uint64_t P) const {
- switch (Type) {
+void SectionChunk::applyRelX86(uint8_t *off, uint16_t type, OutputSection *os,
+ uint64_t s, uint64_t p) const {
+ switch (type) {
case IMAGE_REL_I386_ABSOLUTE: break;
- case IMAGE_REL_I386_DIR32: add32(Off, S + Config->ImageBase); break;
- case IMAGE_REL_I386_DIR32NB: add32(Off, S); break;
- case IMAGE_REL_I386_REL32: add32(Off, S - P - 4); break;
- case IMAGE_REL_I386_SECTION: applySecIdx(Off, OS); break;
- case IMAGE_REL_I386_SECREL: applySecRel(this, Off, OS, S); break;
+ case IMAGE_REL_I386_DIR32: add32(off, s + config->imageBase); break;
+ case IMAGE_REL_I386_DIR32NB: add32(off, s); break;
+ case IMAGE_REL_I386_REL32: add32(off, s - p - 4); break;
+ case IMAGE_REL_I386_SECTION: applySecIdx(off, os); break;
+ case IMAGE_REL_I386_SECREL: applySecRel(this, off, os, s); break;
default:
- error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
- toString(File));
+ error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
+ toString(file));
}
}
-static void applyMOV(uint8_t *Off, uint16_t V) {
- write16le(Off, (read16le(Off) & 0xfbf0) | ((V & 0x800) >> 1) | ((V >> 12) & 0xf));
- write16le(Off + 2, (read16le(Off + 2) & 0x8f00) | ((V & 0x700) << 4) | (V & 0xff));
+static void applyMOV(uint8_t *off, uint16_t v) {
+ write16le(off, (read16le(off) & 0xfbf0) | ((v & 0x800) >> 1) | ((v >> 12) & 0xf));
+ write16le(off + 2, (read16le(off + 2) & 0x8f00) | ((v & 0x700) << 4) | (v & 0xff));
}
-static uint16_t readMOV(uint8_t *Off, bool MOVT) {
- uint16_t Op1 = read16le(Off);
- if ((Op1 & 0xfbf0) != (MOVT ? 0xf2c0 : 0xf240))
- error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") +
+static uint16_t readMOV(uint8_t *off, bool movt) {
+ uint16_t op1 = read16le(off);
+ if ((op1 & 0xfbf0) != (movt ? 0xf2c0 : 0xf240))
+ error("unexpected instruction in " + Twine(movt ? "MOVT" : "MOVW") +
" instruction in MOV32T relocation");
- uint16_t Op2 = read16le(Off + 2);
- if ((Op2 & 0x8000) != 0)
- error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") +
+ uint16_t op2 = read16le(off + 2);
+ if ((op2 & 0x8000) != 0)
+ error("unexpected instruction in " + Twine(movt ? "MOVT" : "MOVW") +
" instruction in MOV32T relocation");
- return (Op2 & 0x00ff) | ((Op2 >> 4) & 0x0700) | ((Op1 << 1) & 0x0800) |
- ((Op1 & 0x000f) << 12);
+ return (op2 & 0x00ff) | ((op2 >> 4) & 0x0700) | ((op1 << 1) & 0x0800) |
+ ((op1 & 0x000f) << 12);
}
-void applyMOV32T(uint8_t *Off, uint32_t V) {
- uint16_t ImmW = readMOV(Off, false); // read MOVW operand
- uint16_t ImmT = readMOV(Off + 4, true); // read MOVT operand
- uint32_t Imm = ImmW | (ImmT << 16);
- V += Imm; // add the immediate offset
- applyMOV(Off, V); // set MOVW operand
- applyMOV(Off + 4, V >> 16); // set MOVT operand
+void applyMOV32T(uint8_t *off, uint32_t v) {
+ uint16_t immW = readMOV(off, false); // read MOVW operand
+ uint16_t immT = readMOV(off + 4, true); // read MOVT operand
+ uint32_t imm = immW | (immT << 16);
+ v += imm; // add the immediate offset
+ applyMOV(off, v); // set MOVW operand
+ applyMOV(off + 4, v >> 16); // set MOVT operand
}
-static void applyBranch20T(uint8_t *Off, int32_t V) {
- if (!isInt<21>(V))
+static void applyBranch20T(uint8_t *off, int32_t v) {
+ if (!isInt<21>(v))
error("relocation out of range");
- uint32_t S = V < 0 ? 1 : 0;
- uint32_t J1 = (V >> 19) & 1;
- uint32_t J2 = (V >> 18) & 1;
- or16(Off, (S << 10) | ((V >> 12) & 0x3f));
- or16(Off + 2, (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
+ uint32_t s = v < 0 ? 1 : 0;
+ uint32_t j1 = (v >> 19) & 1;
+ uint32_t j2 = (v >> 18) & 1;
+ or16(off, (s << 10) | ((v >> 12) & 0x3f));
+ or16(off + 2, (j1 << 13) | (j2 << 11) | ((v >> 1) & 0x7ff));
}
-void applyBranch24T(uint8_t *Off, int32_t V) {
- if (!isInt<25>(V))
+void applyBranch24T(uint8_t *off, int32_t v) {
+ if (!isInt<25>(v))
error("relocation out of range");
- uint32_t S = V < 0 ? 1 : 0;
- uint32_t J1 = ((~V >> 23) & 1) ^ S;
- uint32_t J2 = ((~V >> 22) & 1) ^ S;
- or16(Off, (S << 10) | ((V >> 12) & 0x3ff));
+ uint32_t s = v < 0 ? 1 : 0;
+ uint32_t j1 = ((~v >> 23) & 1) ^ s;
+ uint32_t j2 = ((~v >> 22) & 1) ^ s;
+ or16(off, (s << 10) | ((v >> 12) & 0x3ff));
// Clear out the J1 and J2 bits which may be set.
- write16le(Off + 2, (read16le(Off + 2) & 0xd000) | (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff));
+ write16le(off + 2, (read16le(off + 2) & 0xd000) | (j1 << 13) | (j2 << 11) | ((v >> 1) & 0x7ff));
}
-void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS,
- uint64_t S, uint64_t P) const {
+void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os,
+ uint64_t s, uint64_t p) const {
// Pointer to thumb code must have the LSB set.
- uint64_t SX = S;
- if (OS && (OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE))
- SX |= 1;
- switch (Type) {
- case IMAGE_REL_ARM_ADDR32: add32(Off, SX + Config->ImageBase); break;
- case IMAGE_REL_ARM_ADDR32NB: add32(Off, SX); break;
- case IMAGE_REL_ARM_MOV32T: applyMOV32T(Off, SX + Config->ImageBase); break;
- case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, SX - P - 4); break;
- case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, SX - P - 4); break;
- case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, SX - P - 4); break;
- case IMAGE_REL_ARM_SECTION: applySecIdx(Off, OS); break;
- case IMAGE_REL_ARM_SECREL: applySecRel(this, Off, OS, S); break;
+ uint64_t sx = s;
+ if (os && (os->header.Characteristics & IMAGE_SCN_MEM_EXECUTE))
+ sx |= 1;
+ switch (type) {
+ case IMAGE_REL_ARM_ADDR32: add32(off, sx + config->imageBase); break;
+ case IMAGE_REL_ARM_ADDR32NB: add32(off, sx); break;
+ case IMAGE_REL_ARM_MOV32T: applyMOV32T(off, sx + config->imageBase); break;
+ case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(off, sx - p - 4); break;
+ case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(off, sx - p - 4); break;
+ case IMAGE_REL_ARM_BLX23T: applyBranch24T(off, sx - p - 4); break;
+ case IMAGE_REL_ARM_SECTION: applySecIdx(off, os); break;
+ case IMAGE_REL_ARM_SECREL: applySecRel(this, off, os, s); break;
+ case IMAGE_REL_ARM_REL32: add32(off, sx - p - 4); break;
default:
- error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
- toString(File));
+ error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
+ toString(file));
}
}
// Interpret the existing immediate value as a byte offset to the
// target symbol, then update the instruction with the immediate as
// the page offset from the current instruction to the target.
-void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift) {
- uint32_t Orig = read32le(Off);
- uint64_t Imm = ((Orig >> 29) & 0x3) | ((Orig >> 3) & 0x1FFFFC);
- S += Imm;
- Imm = (S >> Shift) - (P >> Shift);
- uint32_t ImmLo = (Imm & 0x3) << 29;
- uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
- uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
- write32le(Off, (Orig & ~Mask) | ImmLo | ImmHi);
+void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift) {
+ uint32_t orig = read32le(off);
+ uint64_t imm = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC);
+ s += imm;
+ imm = (s >> shift) - (p >> shift);
+ uint32_t immLo = (imm & 0x3) << 29;
+ uint32_t immHi = (imm & 0x1FFFFC) << 3;
+ uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3);
+ write32le(off, (orig & ~mask) | immLo | immHi);
}
// Update the immediate field in a AARCH64 ldr, str, and add instruction.
// Optionally limit the range of the written immediate by one or more bits
-// (RangeLimit).
-void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) {
- uint32_t Orig = read32le(Off);
- Imm += (Orig >> 10) & 0xFFF;
- Orig &= ~(0xFFF << 10);
- write32le(Off, Orig | ((Imm & (0xFFF >> RangeLimit)) << 10));
+// (rangeLimit).
+void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit) {
+ uint32_t orig = read32le(off);
+ imm += (orig >> 10) & 0xFFF;
+ orig &= ~(0xFFF << 10);
+ write32le(off, orig | ((imm & (0xFFF >> rangeLimit)) << 10));
}
// Add the 12 bit page offset to the existing immediate.
@@ -237,171 +234,178 @@ void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) {
// Even if larger loads/stores have a larger range, limit the
// effective offset to 12 bit, since it is intended to be a
// page offset.
-static void applyArm64Ldr(uint8_t *Off, uint64_t Imm) {
- uint32_t Orig = read32le(Off);
- uint32_t Size = Orig >> 30;
+static void applyArm64Ldr(uint8_t *off, uint64_t imm) {
+ uint32_t orig = read32le(off);
+ uint32_t size = orig >> 30;
// 0x04000000 indicates SIMD/FP registers
// 0x00800000 indicates 128 bit
- if ((Orig & 0x4800000) == 0x4800000)
- Size += 4;
- if ((Imm & ((1 << Size) - 1)) != 0)
+ if ((orig & 0x4800000) == 0x4800000)
+ size += 4;
+ if ((imm & ((1 << size) - 1)) != 0)
error("misaligned ldr/str offset");
- applyArm64Imm(Off, Imm >> Size, Size);
+ applyArm64Imm(off, imm >> size, size);
}
-static void applySecRelLow12A(const SectionChunk *Sec, uint8_t *Off,
- OutputSection *OS, uint64_t S) {
- if (checkSecRel(Sec, OS))
- applyArm64Imm(Off, (S - OS->getRVA()) & 0xfff, 0);
+static void applySecRelLow12A(const SectionChunk *sec, uint8_t *off,
+ OutputSection *os, uint64_t s) {
+ if (checkSecRel(sec, os))
+ applyArm64Imm(off, (s - os->getRVA()) & 0xfff, 0);
}
-static void applySecRelHigh12A(const SectionChunk *Sec, uint8_t *Off,
- OutputSection *OS, uint64_t S) {
- if (!checkSecRel(Sec, OS))
+static void applySecRelHigh12A(const SectionChunk *sec, uint8_t *off,
+ OutputSection *os, uint64_t s) {
+ if (!checkSecRel(sec, os))
return;
- uint64_t SecRel = (S - OS->getRVA()) >> 12;
- if (0xfff < SecRel) {
+ uint64_t secRel = (s - os->getRVA()) >> 12;
+ if (0xfff < secRel) {
error("overflow in SECREL_HIGH12A relocation in section: " +
- Sec->getSectionName());
+ sec->getSectionName());
return;
}
- applyArm64Imm(Off, SecRel & 0xfff, 0);
+ applyArm64Imm(off, secRel & 0xfff, 0);
}
-static void applySecRelLdr(const SectionChunk *Sec, uint8_t *Off,
- OutputSection *OS, uint64_t S) {
- if (checkSecRel(Sec, OS))
- applyArm64Ldr(Off, (S - OS->getRVA()) & 0xfff);
+static void applySecRelLdr(const SectionChunk *sec, uint8_t *off,
+ OutputSection *os, uint64_t s) {
+ if (checkSecRel(sec, os))
+ applyArm64Ldr(off, (s - os->getRVA()) & 0xfff);
}
-void applyArm64Branch26(uint8_t *Off, int64_t V) {
- if (!isInt<28>(V))
+void applyArm64Branch26(uint8_t *off, int64_t v) {
+ if (!isInt<28>(v))
error("relocation out of range");
- or32(Off, (V & 0x0FFFFFFC) >> 2);
+ or32(off, (v & 0x0FFFFFFC) >> 2);
}
-static void applyArm64Branch19(uint8_t *Off, int64_t V) {
- if (!isInt<21>(V))
+static void applyArm64Branch19(uint8_t *off, int64_t v) {
+ if (!isInt<21>(v))
error("relocation out of range");
- or32(Off, (V & 0x001FFFFC) << 3);
+ or32(off, (v & 0x001FFFFC) << 3);
}
-static void applyArm64Branch14(uint8_t *Off, int64_t V) {
- if (!isInt<16>(V))
+static void applyArm64Branch14(uint8_t *off, int64_t v) {
+ if (!isInt<16>(v))
error("relocation out of range");
- or32(Off, (V & 0x0000FFFC) << 3);
-}
-
-void SectionChunk::applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS,
- uint64_t S, uint64_t P) const {
- switch (Type) {
- case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(Off, S, P, 12); break;
- case IMAGE_REL_ARM64_REL21: applyArm64Addr(Off, S, P, 0); break;
- case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(Off, S & 0xfff, 0); break;
- case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(Off, S & 0xfff); break;
- case IMAGE_REL_ARM64_BRANCH26: applyArm64Branch26(Off, S - P); break;
- case IMAGE_REL_ARM64_BRANCH19: applyArm64Branch19(Off, S - P); break;
- case IMAGE_REL_ARM64_BRANCH14: applyArm64Branch14(Off, S - P); break;
- case IMAGE_REL_ARM64_ADDR32: add32(Off, S + Config->ImageBase); break;
- case IMAGE_REL_ARM64_ADDR32NB: add32(Off, S); break;
- case IMAGE_REL_ARM64_ADDR64: add64(Off, S + Config->ImageBase); break;
- case IMAGE_REL_ARM64_SECREL: applySecRel(this, Off, OS, S); break;
- case IMAGE_REL_ARM64_SECREL_LOW12A: applySecRelLow12A(this, Off, OS, S); break;
- case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, Off, OS, S); break;
- case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, Off, OS, S); break;
- case IMAGE_REL_ARM64_SECTION: applySecIdx(Off, OS); break;
+ or32(off, (v & 0x0000FFFC) << 3);
+}
+
+void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
+ uint64_t s, uint64_t p) const {
+ switch (type) {
+ case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(off, s, p, 12); break;
+ case IMAGE_REL_ARM64_REL21: applyArm64Addr(off, s, p, 0); break;
+ case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(off, s & 0xfff, 0); break;
+ case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(off, s & 0xfff); break;
+ case IMAGE_REL_ARM64_BRANCH26: applyArm64Branch26(off, s - p); break;
+ case IMAGE_REL_ARM64_BRANCH19: applyArm64Branch19(off, s - p); break;
+ case IMAGE_REL_ARM64_BRANCH14: applyArm64Branch14(off, s - p); break;
+ case IMAGE_REL_ARM64_ADDR32: add32(off, s + config->imageBase); break;
+ case IMAGE_REL_ARM64_ADDR32NB: add32(off, s); break;
+ case IMAGE_REL_ARM64_ADDR64: add64(off, s + config->imageBase); break;
+ case IMAGE_REL_ARM64_SECREL: applySecRel(this, off, os, s); break;
+ case IMAGE_REL_ARM64_SECREL_LOW12A: applySecRelLow12A(this, off, os, s); break;
+ case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, off, os, s); break;
+ case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, off, os, s); break;
+ case IMAGE_REL_ARM64_SECTION: applySecIdx(off, os); break;
+ case IMAGE_REL_ARM64_REL32: add32(off, s - p - 4); break;
default:
- error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
- toString(File));
+ error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
+ toString(file));
}
}
-static void maybeReportRelocationToDiscarded(const SectionChunk *FromChunk,
- Defined *Sym,
- const coff_relocation &Rel) {
+static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk,
+ Defined *sym,
+ const coff_relocation &rel) {
// Don't report these errors when the relocation comes from a debug info
// section or in mingw mode. MinGW mode object files (built by GCC) can
// have leftover sections with relocations against discarded comdat
// sections. Such sections are left as is, with relocations untouched.
- if (FromChunk->isCodeView() || FromChunk->isDWARF() || Config->MinGW)
+ if (fromChunk->isCodeView() || fromChunk->isDWARF() || config->mingw)
return;
// Get the name of the symbol. If it's null, it was discarded early, so we
// have to go back to the object file.
- ObjFile *File = FromChunk->File;
- StringRef Name;
- if (Sym) {
- Name = Sym->getName();
+ ObjFile *file = fromChunk->file;
+ StringRef name;
+ if (sym) {
+ name = sym->getName();
} else {
- COFFSymbolRef COFFSym =
- check(File->getCOFFObj()->getSymbol(Rel.SymbolTableIndex));
- File->getCOFFObj()->getSymbolName(COFFSym, Name);
+ COFFSymbolRef coffSym =
+ check(file->getCOFFObj()->getSymbol(rel.SymbolTableIndex));
+ file->getCOFFObj()->getSymbolName(coffSym, name);
}
- error("relocation against symbol in discarded section: " + Name +
- getSymbolLocations(File, Rel.SymbolTableIndex));
+ std::vector<std::string> symbolLocations =
+ getSymbolLocations(file, rel.SymbolTableIndex);
+
+ std::string out;
+ llvm::raw_string_ostream os(out);
+ os << "relocation against symbol in discarded section: " + name;
+ for (const std::string &s : symbolLocations)
+ os << s;
+ error(os.str());
}
-void SectionChunk::writeTo(uint8_t *Buf) const {
- if (!hasData())
+void SectionChunk::writeTo(uint8_t *buf) const {
+ if (!hasData)
return;
// Copy section contents from source object file to output file.
- ArrayRef<uint8_t> A = getContents();
- if (!A.empty())
- memcpy(Buf + OutputSectionOff, A.data(), A.size());
+ ArrayRef<uint8_t> a = getContents();
+ if (!a.empty())
+ memcpy(buf, a.data(), a.size());
// Apply relocations.
- size_t InputSize = getSize();
- for (size_t I = 0, E = Relocs.size(); I < E; I++) {
- const coff_relocation &Rel = Relocs[I];
+ size_t inputSize = getSize();
+ for (size_t i = 0, e = relocsSize; i < e; i++) {
+ const coff_relocation &rel = relocsData[i];
// Check for an invalid relocation offset. This check isn't perfect, because
// we don't have the relocation size, which is only known after checking the
// machine and relocation type. As a result, a relocation may overwrite the
// beginning of the following input section.
- if (Rel.VirtualAddress >= InputSize) {
+ if (rel.VirtualAddress >= inputSize) {
error("relocation points beyond the end of its parent section");
continue;
}
- uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress;
+ uint8_t *off = buf + rel.VirtualAddress;
- // Use the potentially remapped Symbol instead of the one that the
- // relocation points to.
- auto *Sym = dyn_cast_or_null<Defined>(RelocTargets[I]);
+ auto *sym =
+ dyn_cast_or_null<Defined>(file->getSymbol(rel.SymbolTableIndex));
// Get the output section of the symbol for this relocation. The output
// section is needed to compute SECREL and SECTION relocations used in debug
// info.
- Chunk *C = Sym ? Sym->getChunk() : nullptr;
- OutputSection *OS = C ? C->getOutputSection() : nullptr;
+ Chunk *c = sym ? sym->getChunk() : nullptr;
+ OutputSection *os = c ? c->getOutputSection() : nullptr;
// Skip the relocation if it refers to a discarded section, and diagnose it
// as an error if appropriate. If a symbol was discarded early, it may be
// null. If it was discarded late, the output section will be null, unless
// it was an absolute or synthetic symbol.
- if (!Sym ||
- (!OS && !isa<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym))) {
- maybeReportRelocationToDiscarded(this, Sym, Rel);
+ if (!sym ||
+ (!os && !isa<DefinedAbsolute>(sym) && !isa<DefinedSynthetic>(sym))) {
+ maybeReportRelocationToDiscarded(this, sym, rel);
continue;
}
- uint64_t S = Sym->getRVA();
+ uint64_t s = sym->getRVA();
// Compute the RVA of the relocation for relative relocations.
- uint64_t P = RVA + Rel.VirtualAddress;
- switch (Config->Machine) {
+ uint64_t p = rva + rel.VirtualAddress;
+ switch (config->machine) {
case AMD64:
- applyRelX64(Off, Rel.Type, OS, S, P);
+ applyRelX64(off, rel.Type, os, s, p);
break;
case I386:
- applyRelX86(Off, Rel.Type, OS, S, P);
+ applyRelX86(off, rel.Type, os, s, p);
break;
case ARMNT:
- applyRelARM(Off, Rel.Type, OS, S, P);
+ applyRelARM(off, rel.Type, os, s, p);
break;
case ARM64:
- applyRelARM64(Off, Rel.Type, OS, S, P);
+ applyRelARM64(off, rel.Type, os, s, p);
break;
default:
llvm_unreachable("unknown machine type");
@@ -409,28 +413,32 @@ void SectionChunk::writeTo(uint8_t *Buf) const {
}
}
-void SectionChunk::addAssociative(SectionChunk *Child) {
- AssocChildren.push_back(Child);
+void SectionChunk::addAssociative(SectionChunk *child) {
+ // Insert this child at the head of the list.
+ assert(child->assocChildren == nullptr &&
+ "associated sections cannot have their own associated children");
+ child->assocChildren = assocChildren;
+ assocChildren = child;
}
-static uint8_t getBaserelType(const coff_relocation &Rel) {
- switch (Config->Machine) {
+static uint8_t getBaserelType(const coff_relocation &rel) {
+ switch (config->machine) {
case AMD64:
- if (Rel.Type == IMAGE_REL_AMD64_ADDR64)
+ if (rel.Type == IMAGE_REL_AMD64_ADDR64)
return IMAGE_REL_BASED_DIR64;
return IMAGE_REL_BASED_ABSOLUTE;
case I386:
- if (Rel.Type == IMAGE_REL_I386_DIR32)
+ if (rel.Type == IMAGE_REL_I386_DIR32)
return IMAGE_REL_BASED_HIGHLOW;
return IMAGE_REL_BASED_ABSOLUTE;
case ARMNT:
- if (Rel.Type == IMAGE_REL_ARM_ADDR32)
+ if (rel.Type == IMAGE_REL_ARM_ADDR32)
return IMAGE_REL_BASED_HIGHLOW;
- if (Rel.Type == IMAGE_REL_ARM_MOV32T)
+ if (rel.Type == IMAGE_REL_ARM_MOV32T)
return IMAGE_REL_BASED_ARM_MOV32T;
return IMAGE_REL_BASED_ABSOLUTE;
case ARM64:
- if (Rel.Type == IMAGE_REL_ARM64_ADDR64)
+ if (rel.Type == IMAGE_REL_ARM64_ADDR64)
return IMAGE_REL_BASED_DIR64;
return IMAGE_REL_BASED_ABSOLUTE;
default:
@@ -442,18 +450,16 @@ static uint8_t getBaserelType(const coff_relocation &Rel) {
// Collect all locations that contain absolute addresses, which need to be
// fixed by the loader if load-time relocation is needed.
// Only called when base relocation is enabled.
-void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
- for (size_t I = 0, E = Relocs.size(); I < E; I++) {
- const coff_relocation &Rel = Relocs[I];
- uint8_t Ty = getBaserelType(Rel);
- if (Ty == IMAGE_REL_BASED_ABSOLUTE)
+void SectionChunk::getBaserels(std::vector<Baserel> *res) {
+ for (size_t i = 0, e = relocsSize; i < e; i++) {
+ const coff_relocation &rel = relocsData[i];
+ uint8_t ty = getBaserelType(rel);
+ if (ty == IMAGE_REL_BASED_ABSOLUTE)
continue;
- // Use the potentially remapped Symbol instead of the one that the
- // relocation points to.
- Symbol *Target = RelocTargets[I];
- if (!Target || isa<DefinedAbsolute>(Target))
+ Symbol *target = file->getSymbol(rel.SymbolTableIndex);
+ if (!target || isa<DefinedAbsolute>(target))
continue;
- Res->emplace_back(RVA + Rel.VirtualAddress, Ty);
+ res->emplace_back(rva + rel.VirtualAddress, ty);
}
}
@@ -464,7 +470,7 @@ void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
// another DLL) This returns the size the relocation is supposed to update,
// in bits, or 0 if the relocation cannot be handled as a runtime pseudo
// relocation.
-static int getRuntimePseudoRelocSize(uint16_t Type) {
+static int getRuntimePseudoRelocSize(uint16_t type) {
// Relocations that either contain an absolute address, or a plain
// relative offset, since the runtime pseudo reloc implementation
// adds 8/16/32/64 bit values to a memory address.
@@ -490,9 +496,9 @@ static int getRuntimePseudoRelocSize(uint16_t Type) {
// the image, or temporarily changed at runtime with VirtualProtect.
// Since this only operates on direct address values, it doesn't work for
// ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations.
- switch (Config->Machine) {
+ switch (config->machine) {
case AMD64:
- switch (Type) {
+ switch (type) {
case IMAGE_REL_AMD64_ADDR64:
return 64;
case IMAGE_REL_AMD64_ADDR32:
@@ -507,7 +513,7 @@ static int getRuntimePseudoRelocSize(uint16_t Type) {
return 0;
}
case I386:
- switch (Type) {
+ switch (type) {
case IMAGE_REL_I386_DIR32:
case IMAGE_REL_I386_REL32:
return 32;
@@ -515,14 +521,14 @@ static int getRuntimePseudoRelocSize(uint16_t Type) {
return 0;
}
case ARMNT:
- switch (Type) {
+ switch (type) {
case IMAGE_REL_ARM_ADDR32:
return 32;
default:
return 0;
}
case ARM64:
- switch (Type) {
+ switch (type) {
case IMAGE_REL_ARM64_ADDR64:
return 64;
case IMAGE_REL_ARM64_ADDR32:
@@ -541,75 +547,106 @@ static int getRuntimePseudoRelocSize(uint16_t Type) {
// to a module local variable, which turned out to actually need to be
// imported from another DLL).
void SectionChunk::getRuntimePseudoRelocs(
- std::vector<RuntimePseudoReloc> &Res) {
- for (const coff_relocation &Rel : Relocs) {
- auto *Target =
- dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex));
- if (!Target || !Target->IsRuntimePseudoReloc)
+ std::vector<RuntimePseudoReloc> &res) {
+ for (const coff_relocation &rel : getRelocs()) {
+ auto *target =
+ dyn_cast_or_null<Defined>(file->getSymbol(rel.SymbolTableIndex));
+ if (!target || !target->isRuntimePseudoReloc)
continue;
- int SizeInBits = getRuntimePseudoRelocSize(Rel.Type);
- if (SizeInBits == 0) {
- error("unable to automatically import from " + Target->getName() +
+ int sizeInBits = getRuntimePseudoRelocSize(rel.Type);
+ if (sizeInBits == 0) {
+ error("unable to automatically import from " + target->getName() +
" with relocation type " +
- File->getCOFFObj()->getRelocationTypeName(Rel.Type) + " in " +
- toString(File));
+ file->getCOFFObj()->getRelocationTypeName(rel.Type) + " in " +
+ toString(file));
continue;
}
- // SizeInBits is used to initialize the Flags field; currently no
+ // sizeInBits is used to initialize the Flags field; currently no
// other flags are defined.
- Res.emplace_back(
- RuntimePseudoReloc(Target, this, Rel.VirtualAddress, SizeInBits));
+ res.emplace_back(
+ RuntimePseudoReloc(target, this, rel.VirtualAddress, sizeInBits));
}
}
-bool SectionChunk::hasData() const {
- return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
-}
-
-uint32_t SectionChunk::getOutputCharacteristics() const {
- return Header->Characteristics & (PermMask | TypeMask);
-}
-
bool SectionChunk::isCOMDAT() const {
- return Header->Characteristics & IMAGE_SCN_LNK_COMDAT;
+ return header->Characteristics & IMAGE_SCN_LNK_COMDAT;
}
void SectionChunk::printDiscardedMessage() const {
// Removed by dead-stripping. If it's removed by ICF, ICF already
// printed out the name, so don't repeat that here.
- if (Sym && this == Repl)
- message("Discarded " + Sym->getName());
+ if (sym && this == repl)
+ message("Discarded " + sym->getName());
}
-StringRef SectionChunk::getDebugName() {
- if (Sym)
- return Sym->getName();
+StringRef SectionChunk::getDebugName() const {
+ if (sym)
+ return sym->getName();
return "";
}
ArrayRef<uint8_t> SectionChunk::getContents() const {
- ArrayRef<uint8_t> A;
- File->getCOFFObj()->getSectionContents(Header, A);
- return A;
+ ArrayRef<uint8_t> a;
+ cantFail(file->getCOFFObj()->getSectionContents(header, a));
+ return a;
}
-void SectionChunk::replace(SectionChunk *Other) {
- Alignment = std::max(Alignment, Other->Alignment);
- Other->Repl = Repl;
- Other->Live = false;
+ArrayRef<uint8_t> SectionChunk::consumeDebugMagic() {
+ assert(isCodeView());
+ return consumeDebugMagic(getContents(), getSectionName());
+}
+
+ArrayRef<uint8_t> SectionChunk::consumeDebugMagic(ArrayRef<uint8_t> data,
+ StringRef sectionName) {
+ if (data.empty())
+ return {};
+
+ // First 4 bytes are section magic.
+ if (data.size() < 4)
+ fatal("the section is too short: " + sectionName);
+
+ if (!sectionName.startswith(".debug$"))
+ fatal("invalid section: " + sectionName);
+
+ uint32_t magic = support::endian::read32le(data.data());
+ uint32_t expectedMagic = sectionName == ".debug$H"
+ ? DEBUG_HASHES_SECTION_MAGIC
+ : DEBUG_SECTION_MAGIC;
+ if (magic != expectedMagic) {
+ warn("ignoring section " + sectionName + " with unrecognized magic 0x" +
+ utohexstr(magic));
+ return {};
+ }
+ return data.slice(4);
+}
+
+SectionChunk *SectionChunk::findByName(ArrayRef<SectionChunk *> sections,
+ StringRef name) {
+ for (SectionChunk *c : sections)
+ if (c->getSectionName() == name)
+ return c;
+ return nullptr;
+}
+
+void SectionChunk::replace(SectionChunk *other) {
+ p2Align = std::max(p2Align, other->p2Align);
+ other->repl = repl;
+ other->live = false;
}
uint32_t SectionChunk::getSectionNumber() const {
- DataRefImpl R;
- R.p = reinterpret_cast<uintptr_t>(Header);
- SectionRef S(R, File->getCOFFObj());
- return S.getIndex() + 1;
+ DataRefImpl r;
+ r.p = reinterpret_cast<uintptr_t>(header);
+ SectionRef s(r, file->getCOFFObj());
+ return s.getIndex() + 1;
}
-CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
- // Common symbols are aligned on natural boundaries up to 32 bytes.
+CommonChunk::CommonChunk(const COFFSymbolRef s) : sym(s) {
+ // The value of a common symbol is its size. Align all common symbols smaller
+ // than 32 bytes naturally, i.e. round the size up to the next power of two.
// This is what MSVC link.exe does.
- Alignment = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue()));
+ setAlignment(std::min(32U, uint32_t(PowerOf2Ceil(sym.getValue()))));
+ hasData = false;
}
uint32_t CommonChunk::getOutputCharacteristics() const {
@@ -617,119 +654,139 @@ uint32_t CommonChunk::getOutputCharacteristics() const {
IMAGE_SCN_MEM_WRITE;
}
-void StringChunk::writeTo(uint8_t *Buf) const {
- memcpy(Buf + OutputSectionOff, Str.data(), Str.size());
- Buf[OutputSectionOff + Str.size()] = '\0';
+void StringChunk::writeTo(uint8_t *buf) const {
+ memcpy(buf, str.data(), str.size());
+ buf[str.size()] = '\0';
}
-ImportThunkChunkX64::ImportThunkChunkX64(Defined *S) : ImpSymbol(S) {
+ImportThunkChunkX64::ImportThunkChunkX64(Defined *s) : ImportThunkChunk(s) {
// Intel Optimization Manual says that all branch targets
// should be 16-byte aligned. MSVC linker does this too.
- Alignment = 16;
+ setAlignment(16);
}
-void ImportThunkChunkX64::writeTo(uint8_t *Buf) const {
- memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86));
+void ImportThunkChunkX64::writeTo(uint8_t *buf) const {
+ memcpy(buf, importThunkX86, sizeof(importThunkX86));
// The first two bytes is a JMP instruction. Fill its operand.
- write32le(Buf + OutputSectionOff + 2, ImpSymbol->getRVA() - RVA - getSize());
+ write32le(buf + 2, impSymbol->getRVA() - rva - getSize());
}
-void ImportThunkChunkX86::getBaserels(std::vector<Baserel> *Res) {
- Res->emplace_back(getRVA() + 2);
+void ImportThunkChunkX86::getBaserels(std::vector<Baserel> *res) {
+ res->emplace_back(getRVA() + 2);
}
-void ImportThunkChunkX86::writeTo(uint8_t *Buf) const {
- memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86));
+void ImportThunkChunkX86::writeTo(uint8_t *buf) const {
+ memcpy(buf, importThunkX86, sizeof(importThunkX86));
// The first two bytes is a JMP instruction. Fill its operand.
- write32le(Buf + OutputSectionOff + 2,
- ImpSymbol->getRVA() + Config->ImageBase);
+ write32le(buf + 2,
+ impSymbol->getRVA() + config->imageBase);
}
-void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *Res) {
- Res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T);
+void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *res) {
+ res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T);
}
-void ImportThunkChunkARM::writeTo(uint8_t *Buf) const {
- memcpy(Buf + OutputSectionOff, ImportThunkARM, sizeof(ImportThunkARM));
+void ImportThunkChunkARM::writeTo(uint8_t *buf) const {
+ memcpy(buf, importThunkARM, sizeof(importThunkARM));
// Fix mov.w and mov.t operands.
- applyMOV32T(Buf + OutputSectionOff, ImpSymbol->getRVA() + Config->ImageBase);
+ applyMOV32T(buf, impSymbol->getRVA() + config->imageBase);
}
-void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const {
- int64_t Off = ImpSymbol->getRVA() & 0xfff;
- memcpy(Buf + OutputSectionOff, ImportThunkARM64, sizeof(ImportThunkARM64));
- applyArm64Addr(Buf + OutputSectionOff, ImpSymbol->getRVA(), RVA, 12);
- applyArm64Ldr(Buf + OutputSectionOff + 4, Off);
+void ImportThunkChunkARM64::writeTo(uint8_t *buf) const {
+ int64_t off = impSymbol->getRVA() & 0xfff;
+ memcpy(buf, importThunkARM64, sizeof(importThunkARM64));
+ applyArm64Addr(buf, impSymbol->getRVA(), rva, 12);
+ applyArm64Ldr(buf + 4, off);
}
// A Thumb2, PIC, non-interworking range extension thunk.
-const uint8_t ArmThunk[] = {
+const uint8_t armThunk[] = {
0x40, 0xf2, 0x00, 0x0c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
0xe7, 0x44, // L1: add pc, ip
};
-size_t RangeExtensionThunk::getSize() const {
- assert(Config->Machine == ARMNT);
- return sizeof(ArmThunk);
+size_t RangeExtensionThunkARM::getSize() const {
+ assert(config->machine == ARMNT);
+ return sizeof(armThunk);
+}
+
+void RangeExtensionThunkARM::writeTo(uint8_t *buf) const {
+ assert(config->machine == ARMNT);
+ uint64_t offset = target->getRVA() - rva - 12;
+ memcpy(buf, armThunk, sizeof(armThunk));
+ applyMOV32T(buf, uint32_t(offset));
+}
+
+// A position independent ARM64 adrp+add thunk, with a maximum range of
+// +/- 4 GB, which is enough for any PE-COFF.
+const uint8_t arm64Thunk[] = {
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Dest
+ 0x10, 0x02, 0x00, 0x91, // add x16, x16, :lo12:Dest
+ 0x00, 0x02, 0x1f, 0xd6, // br x16
+};
+
+size_t RangeExtensionThunkARM64::getSize() const {
+ assert(config->machine == ARM64);
+ return sizeof(arm64Thunk);
}
-void RangeExtensionThunk::writeTo(uint8_t *Buf) const {
- assert(Config->Machine == ARMNT);
- uint64_t Offset = Target->getRVA() - RVA - 12;
- memcpy(Buf + OutputSectionOff, ArmThunk, sizeof(ArmThunk));
- applyMOV32T(Buf + OutputSectionOff, uint32_t(Offset));
+void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const {
+ assert(config->machine == ARM64);
+ memcpy(buf, arm64Thunk, sizeof(arm64Thunk));
+ applyArm64Addr(buf + 0, target->getRVA(), rva, 12);
+ applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0);
}
-void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
- Res->emplace_back(getRVA());
+void LocalImportChunk::getBaserels(std::vector<Baserel> *res) {
+ res->emplace_back(getRVA());
}
-size_t LocalImportChunk::getSize() const { return Config->Wordsize; }
+size_t LocalImportChunk::getSize() const { return config->wordsize; }
-void LocalImportChunk::writeTo(uint8_t *Buf) const {
- if (Config->is64()) {
- write64le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase);
+void LocalImportChunk::writeTo(uint8_t *buf) const {
+ if (config->is64()) {
+ write64le(buf, sym->getRVA() + config->imageBase);
} else {
- write32le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase);
+ write32le(buf, sym->getRVA() + config->imageBase);
}
}
-void RVATableChunk::writeTo(uint8_t *Buf) const {
- ulittle32_t *Begin = reinterpret_cast<ulittle32_t *>(Buf + OutputSectionOff);
- size_t Cnt = 0;
- for (const ChunkAndOffset &CO : Syms)
- Begin[Cnt++] = CO.InputChunk->getRVA() + CO.Offset;
- std::sort(Begin, Begin + Cnt);
- assert(std::unique(Begin, Begin + Cnt) == Begin + Cnt &&
+void RVATableChunk::writeTo(uint8_t *buf) const {
+ ulittle32_t *begin = reinterpret_cast<ulittle32_t *>(buf);
+ size_t cnt = 0;
+ for (const ChunkAndOffset &co : syms)
+ begin[cnt++] = co.inputChunk->getRVA() + co.offset;
+ std::sort(begin, begin + cnt);
+ assert(std::unique(begin, begin + cnt) == begin + cnt &&
"RVA tables should be de-duplicated");
}
// MinGW specific, for the "automatic import of variables from DLLs" feature.
size_t PseudoRelocTableChunk::getSize() const {
- if (Relocs.empty())
+ if (relocs.empty())
return 0;
- return 12 + 12 * Relocs.size();
+ return 12 + 12 * relocs.size();
}
// MinGW specific.
-void PseudoRelocTableChunk::writeTo(uint8_t *Buf) const {
- if (Relocs.empty())
+void PseudoRelocTableChunk::writeTo(uint8_t *buf) const {
+ if (relocs.empty())
return;
- ulittle32_t *Table = reinterpret_cast<ulittle32_t *>(Buf + OutputSectionOff);
+ ulittle32_t *table = reinterpret_cast<ulittle32_t *>(buf);
// This is the list header, to signal the runtime pseudo relocation v2
// format.
- Table[0] = 0;
- Table[1] = 0;
- Table[2] = 1;
-
- size_t Idx = 3;
- for (const RuntimePseudoReloc &RPR : Relocs) {
- Table[Idx + 0] = RPR.Sym->getRVA();
- Table[Idx + 1] = RPR.Target->getRVA() + RPR.TargetOffset;
- Table[Idx + 2] = RPR.Flags;
- Idx += 3;
+ table[0] = 0;
+ table[1] = 0;
+ table[2] = 1;
+
+ size_t idx = 3;
+ for (const RuntimePseudoReloc &rpr : relocs) {
+ table[idx + 0] = rpr.sym->getRVA();
+ table[idx + 1] = rpr.target->getRVA() + rpr.targetOffset;
+ table[idx + 2] = rpr.flags;
+ idx += 3;
}
}
@@ -772,26 +829,26 @@ void PseudoRelocTableChunk::writeTo(uint8_t *Buf) const {
//
// Usually we have a lot of relocations for each page, so the number of
// bytes for one .reloc entry is close to 2 bytes on average.
-BaserelChunk::BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End) {
+BaserelChunk::BaserelChunk(uint32_t page, Baserel *begin, Baserel *end) {
// Block header consists of 4 byte page RVA and 4 byte block size.
// Each entry is 2 byte. Last entry may be padding.
- Data.resize(alignTo((End - Begin) * 2 + 8, 4));
- uint8_t *P = Data.data();
- write32le(P, Page);
- write32le(P + 4, Data.size());
- P += 8;
- for (Baserel *I = Begin; I != End; ++I) {
- write16le(P, (I->Type << 12) | (I->RVA - Page));
- P += 2;
+ data.resize(alignTo((end - begin) * 2 + 8, 4));
+ uint8_t *p = data.data();
+ write32le(p, page);
+ write32le(p + 4, data.size());
+ p += 8;
+ for (Baserel *i = begin; i != end; ++i) {
+ write16le(p, (i->type << 12) | (i->rva - page));
+ p += 2;
}
}
-void BaserelChunk::writeTo(uint8_t *Buf) const {
- memcpy(Buf + OutputSectionOff, Data.data(), Data.size());
+void BaserelChunk::writeTo(uint8_t *buf) const {
+ memcpy(buf, data.data(), data.size());
}
uint8_t Baserel::getDefaultType() {
- switch (Config->Machine) {
+ switch (config->machine) {
case AMD64:
case ARM64:
return IMAGE_REL_BASED_DIR64;
@@ -803,36 +860,38 @@ uint8_t Baserel::getDefaultType() {
}
}
-std::map<uint32_t, MergeChunk *> MergeChunk::Instances;
+MergeChunk *MergeChunk::instances[Log2MaxSectionAlignment + 1] = {};
-MergeChunk::MergeChunk(uint32_t Alignment)
- : Builder(StringTableBuilder::RAW, Alignment) {
- this->Alignment = Alignment;
+MergeChunk::MergeChunk(uint32_t alignment)
+ : builder(StringTableBuilder::RAW, alignment) {
+ setAlignment(alignment);
}
-void MergeChunk::addSection(SectionChunk *C) {
- auto *&MC = Instances[C->Alignment];
- if (!MC)
- MC = make<MergeChunk>(C->Alignment);
- MC->Sections.push_back(C);
+void MergeChunk::addSection(SectionChunk *c) {
+ assert(isPowerOf2_32(c->getAlignment()));
+ uint8_t p2Align = llvm::Log2_32(c->getAlignment());
+ assert(p2Align < array_lengthof(instances));
+ auto *&mc = instances[p2Align];
+ if (!mc)
+ mc = make<MergeChunk>(c->getAlignment());
+ mc->sections.push_back(c);
}
void MergeChunk::finalizeContents() {
- if (!Finalized) {
- for (SectionChunk *C : Sections)
- if (C->Live)
- Builder.add(toStringRef(C->getContents()));
- Builder.finalize();
- Finalized = true;
- }
+ assert(!finalized && "should only finalize once");
+ for (SectionChunk *c : sections)
+ if (c->live)
+ builder.add(toStringRef(c->getContents()));
+ builder.finalize();
+ finalized = true;
+}
- for (SectionChunk *C : Sections) {
- if (!C->Live)
+void MergeChunk::assignSubsectionRVAs() {
+ for (SectionChunk *c : sections) {
+ if (!c->live)
continue;
- size_t Off = Builder.getOffset(toStringRef(C->getContents()));
- C->setOutputSection(Out);
- C->setRVA(RVA + Off);
- C->OutputSectionOff = OutputSectionOff + Off;
+ size_t off = builder.getOffset(toStringRef(c->getContents()));
+ c->setRVA(rva + off);
}
}
@@ -841,21 +900,21 @@ uint32_t MergeChunk::getOutputCharacteristics() const {
}
size_t MergeChunk::getSize() const {
- return Builder.getSize();
+ return builder.getSize();
}
-void MergeChunk::writeTo(uint8_t *Buf) const {
- Builder.write(Buf + OutputSectionOff);
+void MergeChunk::writeTo(uint8_t *buf) const {
+ builder.write(buf);
}
// MinGW specific.
-size_t AbsolutePointerChunk::getSize() const { return Config->Wordsize; }
+size_t AbsolutePointerChunk::getSize() const { return config->wordsize; }
-void AbsolutePointerChunk::writeTo(uint8_t *Buf) const {
- if (Config->is64()) {
- write64le(Buf + OutputSectionOff, Value);
+void AbsolutePointerChunk::writeTo(uint8_t *buf) const {
+ if (config->is64()) {
+ write64le(buf, value);
} else {
- write32le(Buf + OutputSectionOff, Value);
+ write32le(buf, value);
}
}
diff --git a/COFF/Chunks.h b/COFF/Chunks.h
index f8a0ddd8ef3b..6bb629fe998b 100644
--- a/COFF/Chunks.h
+++ b/COFF/Chunks.h
@@ -1,9 +1,8 @@
//===- Chunks.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -14,6 +13,7 @@
#include "InputFiles.h"
#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/MC/StringTableBuilder.h"
@@ -40,10 +40,13 @@ class RuntimePseudoReloc;
class Symbol;
// Mask for permissions (discardable, writable, readable, executable, etc).
-const uint32_t PermMask = 0xFE000000;
+const uint32_t permMask = 0xFE000000;
// Mask for section types (code, data, bss).
-const uint32_t TypeMask = 0x000000E0;
+const uint32_t typeMask = 0x000000E0;
+
+// The log base 2 of the largest section alignment, which is log2(8192), or 13.
+enum : unsigned { Log2MaxSectionAlignment = 13 };
// A Chunk represents a chunk of data that will occupy space in the
// output (if the resolver chose that). It may or may not be backed by
@@ -51,81 +54,126 @@ const uint32_t TypeMask = 0x000000E0;
// doesn't even have actual data (if common or bss).
class Chunk {
public:
- enum Kind { SectionKind, OtherKind };
- Kind kind() const { return ChunkKind; }
- virtual ~Chunk() = default;
+ enum Kind : uint8_t { SectionKind, OtherKind, ImportThunkKind };
+ Kind kind() const { return chunkKind; }
// Returns the size of this chunk (even if this is a common or BSS.)
- virtual size_t getSize() const = 0;
+ size_t getSize() const;
+
+ // Returns chunk alignment in power of two form. Value values are powers of
+ // two from 1 to 8192.
+ uint32_t getAlignment() const { return 1U << p2Align; }
+
+ // Update the chunk section alignment measured in bytes. Internally alignment
+ // is stored in log2.
+ void setAlignment(uint32_t align) {
+ // Treat zero byte alignment as 1 byte alignment.
+ align = align ? align : 1;
+ assert(llvm::isPowerOf2_32(align) && "alignment is not a power of 2");
+ p2Align = llvm::Log2_32(align);
+ assert(p2Align <= Log2MaxSectionAlignment &&
+ "impossible requested alignment");
+ }
// Write this chunk to a mmap'ed file, assuming Buf is pointing to
// beginning of the file. Because this function may use RVA values
// of other chunks for relocations, you need to set them properly
// before calling this function.
- virtual void writeTo(uint8_t *Buf) const {}
+ void writeTo(uint8_t *buf) const;
+
+ // The writer sets and uses the addresses. In practice, PE images cannot be
+ // larger than 2GB. Chunks are always laid as part of the image, so Chunk RVAs
+ // can be stored with 32 bits.
+ uint32_t getRVA() const { return rva; }
+ void setRVA(uint64_t v) {
+ rva = (uint32_t)v;
+ assert(rva == v && "RVA truncated");
+ }
- // Called by the writer once before assigning addresses and writing
- // the output.
- virtual void readRelocTargets() {}
+ // Returns readable/writable/executable bits.
+ uint32_t getOutputCharacteristics() const;
- // Called if restarting thunk addition.
- virtual void resetRelocTargets() {}
+ // Returns the section name if this is a section chunk.
+ // It is illegal to call this function on non-section chunks.
+ StringRef getSectionName() const;
- // Called by the writer after an RVA is assigned, but before calling
- // getSize().
- virtual void finalizeContents() {}
+ // An output section has pointers to chunks in the section, and each
+ // chunk has a back pointer to an output section.
+ void setOutputSectionIdx(uint16_t o) { osidx = o; }
+ uint16_t getOutputSectionIdx() const { return osidx; }
+ OutputSection *getOutputSection() const;
- // The writer sets and uses the addresses.
- uint64_t getRVA() const { return RVA; }
- void setRVA(uint64_t V) { RVA = V; }
+ // Windows-specific.
+ // Collect all locations that contain absolute addresses for base relocations.
+ void getBaserels(std::vector<Baserel> *res);
+ // Returns a human-readable name of this chunk. Chunks are unnamed chunks of
+ // bytes, so this is used only for logging or debugging.
+ StringRef getDebugName() const;
+
+ // Return true if this file has the hotpatch flag set to true in the
+ // S_COMPILE3 record in codeview debug info. Also returns true for some thunks
+ // synthesized by the linker.
+ bool isHotPatchable() const;
+
+protected:
+ Chunk(Kind k = OtherKind) : chunkKind(k), hasData(true), p2Align(0) {}
+
+ const Kind chunkKind;
+
+public:
// Returns true if this has non-zero data. BSS chunks return
// false. If false is returned, the space occupied by this chunk
- // will be filled with zeros.
- virtual bool hasData() const { return true; }
+ // will be filled with zeros. Corresponds to the
+ // IMAGE_SCN_CNT_UNINITIALIZED_DATA section characteristic bit.
+ uint8_t hasData : 1;
+
+public:
+ // The alignment of this chunk, stored in log2 form. The writer uses the
+ // value.
+ uint8_t p2Align : 7;
+
+ // The output section index for this chunk. The first valid section number is
+ // one.
+ uint16_t osidx = 0;
+
+ // The RVA of this chunk in the output. The writer sets a value.
+ uint32_t rva = 0;
+};
+
+class NonSectionChunk : public Chunk {
+public:
+ virtual ~NonSectionChunk() = default;
+
+ // Returns the size of this chunk (even if this is a common or BSS.)
+ virtual size_t getSize() const = 0;
- // Returns readable/writable/executable bits.
virtual uint32_t getOutputCharacteristics() const { return 0; }
+ // Write this chunk to a mmap'ed file, assuming Buf is pointing to
+ // beginning of the file. Because this function may use RVA values
+ // of other chunks for relocations, you need to set them properly
+ // before calling this function.
+ virtual void writeTo(uint8_t *buf) const {}
+
// Returns the section name if this is a section chunk.
// It is illegal to call this function on non-section chunks.
virtual StringRef getSectionName() const {
llvm_unreachable("unimplemented getSectionName");
}
- // An output section has pointers to chunks in the section, and each
- // chunk has a back pointer to an output section.
- void setOutputSection(OutputSection *O) { Out = O; }
- OutputSection *getOutputSection() const { return Out; }
-
// Windows-specific.
// Collect all locations that contain absolute addresses for base relocations.
- virtual void getBaserels(std::vector<Baserel> *Res) {}
+ virtual void getBaserels(std::vector<Baserel> *res) {}
// Returns a human-readable name of this chunk. Chunks are unnamed chunks of
// bytes, so this is used only for logging or debugging.
- virtual StringRef getDebugName() { return ""; }
+ virtual StringRef getDebugName() const { return ""; }
- // The alignment of this chunk. The writer uses the value.
- uint32_t Alignment = 1;
+ static bool classof(const Chunk *c) { return c->kind() != SectionKind; }
protected:
- Chunk(Kind K = OtherKind) : ChunkKind(K) {}
- const Kind ChunkKind;
-
- // The RVA of this chunk in the output. The writer sets a value.
- uint64_t RVA = 0;
-
- // The output section for this chunk.
- OutputSection *Out = nullptr;
-
-public:
- // The offset from beginning of the output section. The writer sets a value.
- uint64_t OutputSectionOff = 0;
-
- // Whether this section needs to be kept distinct from other sections during
- // ICF. This is set by the driver using address-significance tables.
- bool KeepUnique = false;
+ NonSectionChunk(Kind k = OtherKind) : Chunk(k) {}
};
// A chunk corresponding a section of an input file.
@@ -139,39 +187,41 @@ public:
std::random_access_iterator_tag, Symbol *> {
friend SectionChunk;
- ObjFile *File;
+ ObjFile *file;
- symbol_iterator(ObjFile *File, const coff_relocation *I)
- : symbol_iterator::iterator_adaptor_base(I), File(File) {}
+ symbol_iterator(ObjFile *file, const coff_relocation *i)
+ : symbol_iterator::iterator_adaptor_base(i), file(file) {}
public:
symbol_iterator() = default;
- Symbol *operator*() const { return File->getSymbol(I->SymbolTableIndex); }
+ Symbol *operator*() const { return file->getSymbol(I->SymbolTableIndex); }
};
- SectionChunk(ObjFile *File, const coff_section *Header);
- static bool classof(const Chunk *C) { return C->kind() == SectionKind; }
- void readRelocTargets() override;
- void resetRelocTargets() override;
- size_t getSize() const override { return Header->SizeOfRawData; }
+ SectionChunk(ObjFile *file, const coff_section *header);
+ static bool classof(const Chunk *c) { return c->kind() == SectionKind; }
+ size_t getSize() const { return header->SizeOfRawData; }
ArrayRef<uint8_t> getContents() const;
- void writeTo(uint8_t *Buf) const override;
- bool hasData() const override;
- uint32_t getOutputCharacteristics() const override;
- StringRef getSectionName() const override { return SectionName; }
- void getBaserels(std::vector<Baserel> *Res) override;
+ void writeTo(uint8_t *buf) const;
+
+ uint32_t getOutputCharacteristics() const {
+ return header->Characteristics & (permMask | typeMask);
+ }
+ StringRef getSectionName() const {
+ return StringRef(sectionNameData, sectionNameSize);
+ }
+ void getBaserels(std::vector<Baserel> *res);
bool isCOMDAT() const;
- void applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
- uint64_t P) const;
- void applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
- uint64_t P) const;
- void applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
- uint64_t P) const;
- void applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
- uint64_t P) const;
+ void applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
+ uint64_t p) const;
+ void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
+ uint64_t p) const;
+ void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
+ uint64_t p) const;
+ void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
+ uint64_t p) const;
- void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &Res);
+ void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &res);
// Called if the garbage collector decides to not include this chunk
// in a final output. It's supposed to print out a log message to stdout.
@@ -179,69 +229,169 @@ public:
// Adds COMDAT associative sections to this COMDAT section. A chunk
// and its children are treated as a group by the garbage collector.
- void addAssociative(SectionChunk *Child);
+ void addAssociative(SectionChunk *child);
- StringRef getDebugName() override;
+ StringRef getDebugName() const;
// True if this is a codeview debug info chunk. These will not be laid out in
// the image. Instead they will end up in the PDB, if one is requested.
bool isCodeView() const {
- return SectionName == ".debug" || SectionName.startswith(".debug$");
+ return getSectionName() == ".debug" || getSectionName().startswith(".debug$");
}
// True if this is a DWARF debug info or exception handling chunk.
bool isDWARF() const {
- return SectionName.startswith(".debug_") || SectionName == ".eh_frame";
+ return getSectionName().startswith(".debug_") || getSectionName() == ".eh_frame";
}
// Allow iteration over the bodies of this chunk's relocated symbols.
llvm::iterator_range<symbol_iterator> symbols() const {
- return llvm::make_range(symbol_iterator(File, Relocs.begin()),
- symbol_iterator(File, Relocs.end()));
+ return llvm::make_range(symbol_iterator(file, relocsData),
+ symbol_iterator(file, relocsData + relocsSize));
+ }
+
+ ArrayRef<coff_relocation> getRelocs() const {
+ return llvm::makeArrayRef(relocsData, relocsSize);
}
+ // Reloc setter used by ARM range extension thunk insertion.
+ void setRelocs(ArrayRef<coff_relocation> newRelocs) {
+ relocsData = newRelocs.data();
+ relocsSize = newRelocs.size();
+ assert(relocsSize == newRelocs.size() && "reloc size truncation");
+ }
+
+ // Single linked list iterator for associated comdat children.
+ class AssociatedIterator
+ : public llvm::iterator_facade_base<
+ AssociatedIterator, std::forward_iterator_tag, SectionChunk> {
+ public:
+ AssociatedIterator() = default;
+ AssociatedIterator(SectionChunk *head) : cur(head) {}
+ AssociatedIterator &operator=(const AssociatedIterator &r) {
+ cur = r.cur;
+ return *this;
+ }
+ bool operator==(const AssociatedIterator &r) const { return cur == r.cur; }
+ const SectionChunk &operator*() const { return *cur; }
+ SectionChunk &operator*() { return *cur; }
+ AssociatedIterator &operator++() {
+ cur = cur->assocChildren;
+ return *this;
+ }
+
+ private:
+ SectionChunk *cur = nullptr;
+ };
+
// Allow iteration over the associated child chunks for this section.
- ArrayRef<SectionChunk *> children() const { return AssocChildren; }
+ llvm::iterator_range<AssociatedIterator> children() const {
+ return llvm::make_range(AssociatedIterator(assocChildren),
+ AssociatedIterator(nullptr));
+ }
// The section ID this chunk belongs to in its Obj.
uint32_t getSectionNumber() const;
- // A pointer pointing to a replacement for this chunk.
- // Initially it points to "this" object. If this chunk is merged
- // with other chunk by ICF, it points to another chunk,
- // and this chunk is considered as dead.
- SectionChunk *Repl;
+ ArrayRef<uint8_t> consumeDebugMagic();
- // The CRC of the contents as described in the COFF spec 4.5.5.
- // Auxiliary Format 5: Section Definitions. Used for ICF.
- uint32_t Checksum = 0;
+ static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> data,
+ StringRef sectionName);
- const coff_section *Header;
+ static SectionChunk *findByName(ArrayRef<SectionChunk *> sections,
+ StringRef name);
// The file that this chunk was created from.
- ObjFile *File;
+ ObjFile *file;
+
+ // Pointer to the COFF section header in the input file.
+ const coff_section *header;
// The COMDAT leader symbol if this is a COMDAT chunk.
- DefinedRegular *Sym = nullptr;
+ DefinedRegular *sym = nullptr;
- ArrayRef<coff_relocation> Relocs;
+ // The CRC of the contents as described in the COFF spec 4.5.5.
+ // Auxiliary Format 5: Section Definitions. Used for ICF.
+ uint32_t checksum = 0;
// Used by the garbage collector.
- bool Live;
+ bool live;
+
+ // Whether this section needs to be kept distinct from other sections during
+ // ICF. This is set by the driver using address-significance tables.
+ bool keepUnique = false;
- // When inserting a thunk, we need to adjust a relocation to point to
- // the thunk instead of the actual original target Symbol.
- std::vector<Symbol *> RelocTargets;
+ // The COMDAT selection if this is a COMDAT chunk.
+ llvm::COFF::COMDATType selection = (llvm::COFF::COMDATType)0;
+
+ // A pointer pointing to a replacement for this chunk.
+ // Initially it points to "this" object. If this chunk is merged
+ // with other chunk by ICF, it points to another chunk,
+ // and this chunk is considered as dead.
+ SectionChunk *repl;
private:
- StringRef SectionName;
- std::vector<SectionChunk *> AssocChildren;
+ SectionChunk *assocChildren = nullptr;
// Used for ICF (Identical COMDAT Folding)
- void replace(SectionChunk *Other);
- uint32_t Class[2] = {0, 0};
+ void replace(SectionChunk *other);
+ uint32_t eqClass[2] = {0, 0};
+
+ // Relocations for this section. Size is stored below.
+ const coff_relocation *relocsData;
+
+ // Section name string. Size is stored below.
+ const char *sectionNameData;
+
+ uint32_t relocsSize = 0;
+ uint32_t sectionNameSize = 0;
};
+// Inline methods to implement faux-virtual dispatch for SectionChunk.
+
+inline size_t Chunk::getSize() const {
+ if (isa<SectionChunk>(this))
+ return static_cast<const SectionChunk *>(this)->getSize();
+ else
+ return static_cast<const NonSectionChunk *>(this)->getSize();
+}
+
+inline uint32_t Chunk::getOutputCharacteristics() const {
+ if (isa<SectionChunk>(this))
+ return static_cast<const SectionChunk *>(this)->getOutputCharacteristics();
+ else
+ return static_cast<const NonSectionChunk *>(this)
+ ->getOutputCharacteristics();
+}
+
+inline void Chunk::writeTo(uint8_t *buf) const {
+ if (isa<SectionChunk>(this))
+ static_cast<const SectionChunk *>(this)->writeTo(buf);
+ else
+ static_cast<const NonSectionChunk *>(this)->writeTo(buf);
+}
+
+inline StringRef Chunk::getSectionName() const {
+ if (isa<SectionChunk>(this))
+ return static_cast<const SectionChunk *>(this)->getSectionName();
+ else
+ return static_cast<const NonSectionChunk *>(this)->getSectionName();
+}
+
+inline void Chunk::getBaserels(std::vector<Baserel> *res) {
+ if (isa<SectionChunk>(this))
+ static_cast<SectionChunk *>(this)->getBaserels(res);
+ else
+ static_cast<NonSectionChunk *>(this)->getBaserels(res);
+}
+
+inline StringRef Chunk::getDebugName() const {
+ if (isa<SectionChunk>(this))
+ return static_cast<const SectionChunk *>(this)->getDebugName();
+ else
+ return static_cast<const NonSectionChunk *>(this)->getDebugName();
+}
+
// This class is used to implement an lld-specific feature (not implemented in
// MSVC) that minimizes the output size by finding string literals sharing tail
// parts and merging them.
@@ -251,60 +401,60 @@ private:
// The MergeChunk then tail merges the strings using the StringTableBuilder
// class and assigns RVAs and section offsets to each of the member chunks based
// on the offsets assigned by the StringTableBuilder.
-class MergeChunk : public Chunk {
+class MergeChunk : public NonSectionChunk {
public:
- MergeChunk(uint32_t Alignment);
- static void addSection(SectionChunk *C);
- void finalizeContents() override;
+ MergeChunk(uint32_t alignment);
+ static void addSection(SectionChunk *c);
+ void finalizeContents();
+ void assignSubsectionRVAs();
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override { return ".rdata"; }
size_t getSize() const override;
- void writeTo(uint8_t *Buf) const override;
+ void writeTo(uint8_t *buf) const override;
- static std::map<uint32_t, MergeChunk *> Instances;
- std::vector<SectionChunk *> Sections;
+ static MergeChunk *instances[Log2MaxSectionAlignment + 1];
+ std::vector<SectionChunk *> sections;
private:
- llvm::StringTableBuilder Builder;
- bool Finalized = false;
+ llvm::StringTableBuilder builder;
+ bool finalized = false;
};
// A chunk for common symbols. Common chunks don't have actual data.
-class CommonChunk : public Chunk {
+class CommonChunk : public NonSectionChunk {
public:
- CommonChunk(const COFFSymbolRef Sym);
- size_t getSize() const override { return Sym.getValue(); }
- bool hasData() const override { return false; }
+ CommonChunk(const COFFSymbolRef sym);
+ size_t getSize() const override { return sym.getValue(); }
uint32_t getOutputCharacteristics() const override;
StringRef getSectionName() const override { return ".bss"; }
private:
- const COFFSymbolRef Sym;
+ const COFFSymbolRef sym;
};
// A chunk for linker-created strings.
-class StringChunk : public Chunk {
+class StringChunk : public NonSectionChunk {
public:
- explicit StringChunk(StringRef S) : Str(S) {}
- size_t getSize() const override { return Str.size() + 1; }
- void writeTo(uint8_t *Buf) const override;
+ explicit StringChunk(StringRef s) : str(s) {}
+ size_t getSize() const override { return str.size() + 1; }
+ void writeTo(uint8_t *buf) const override;
private:
- StringRef Str;
+ StringRef str;
};
-static const uint8_t ImportThunkX86[] = {
+static const uint8_t importThunkX86[] = {
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0
};
-static const uint8_t ImportThunkARM[] = {
+static const uint8_t importThunkARM[] = {
0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0
0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0
0xdc, 0xf8, 0x00, 0xf0, // ldr.w pc, [ip]
};
-static const uint8_t ImportThunkARM64[] = {
+static const uint8_t importThunkARM64[] = {
0x10, 0x00, 0x00, 0x90, // adrp x16, #0
0x10, 0x02, 0x40, 0xf9, // ldr x16, [x16]
0x00, 0x02, 0x1f, 0xd6, // br x16
@@ -313,78 +463,85 @@ static const uint8_t ImportThunkARM64[] = {
// Windows-specific.
// A chunk for DLL import jump table entry. In a final output, its
// contents will be a JMP instruction to some __imp_ symbol.
-class ImportThunkChunkX64 : public Chunk {
+class ImportThunkChunk : public NonSectionChunk {
public:
- explicit ImportThunkChunkX64(Defined *S);
- size_t getSize() const override { return sizeof(ImportThunkX86); }
- void writeTo(uint8_t *Buf) const override;
+ ImportThunkChunk(Defined *s)
+ : NonSectionChunk(ImportThunkKind), impSymbol(s) {}
+ static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; }
-private:
- Defined *ImpSymbol;
+protected:
+ Defined *impSymbol;
};
-class ImportThunkChunkX86 : public Chunk {
+class ImportThunkChunkX64 : public ImportThunkChunk {
public:
- explicit ImportThunkChunkX86(Defined *S) : ImpSymbol(S) {}
- size_t getSize() const override { return sizeof(ImportThunkX86); }
- void getBaserels(std::vector<Baserel> *Res) override;
- void writeTo(uint8_t *Buf) const override;
+ explicit ImportThunkChunkX64(Defined *s);
+ size_t getSize() const override { return sizeof(importThunkX86); }
+ void writeTo(uint8_t *buf) const override;
+};
-private:
- Defined *ImpSymbol;
+class ImportThunkChunkX86 : public ImportThunkChunk {
+public:
+ explicit ImportThunkChunkX86(Defined *s) : ImportThunkChunk(s) {}
+ size_t getSize() const override { return sizeof(importThunkX86); }
+ void getBaserels(std::vector<Baserel> *res) override;
+ void writeTo(uint8_t *buf) const override;
};
-class ImportThunkChunkARM : public Chunk {
+class ImportThunkChunkARM : public ImportThunkChunk {
public:
- explicit ImportThunkChunkARM(Defined *S) : ImpSymbol(S) {}
- size_t getSize() const override { return sizeof(ImportThunkARM); }
- void getBaserels(std::vector<Baserel> *Res) override;
- void writeTo(uint8_t *Buf) const override;
+ explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) {}
+ size_t getSize() const override { return sizeof(importThunkARM); }
+ void getBaserels(std::vector<Baserel> *res) override;
+ void writeTo(uint8_t *buf) const override;
+};
-private:
- Defined *ImpSymbol;
+class ImportThunkChunkARM64 : public ImportThunkChunk {
+public:
+ explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) {}
+ size_t getSize() const override { return sizeof(importThunkARM64); }
+ void writeTo(uint8_t *buf) const override;
};
-class ImportThunkChunkARM64 : public Chunk {
+class RangeExtensionThunkARM : public NonSectionChunk {
public:
- explicit ImportThunkChunkARM64(Defined *S) : ImpSymbol(S) {}
- size_t getSize() const override { return sizeof(ImportThunkARM64); }
- void writeTo(uint8_t *Buf) const override;
+ explicit RangeExtensionThunkARM(Defined *t) : target(t) {}
+ size_t getSize() const override;
+ void writeTo(uint8_t *buf) const override;
-private:
- Defined *ImpSymbol;
+ Defined *target;
};
-class RangeExtensionThunk : public Chunk {
+class RangeExtensionThunkARM64 : public NonSectionChunk {
public:
- explicit RangeExtensionThunk(Defined *T) : Target(T) {}
+ explicit RangeExtensionThunkARM64(Defined *t) : target(t) {}
size_t getSize() const override;
- void writeTo(uint8_t *Buf) const override;
+ void writeTo(uint8_t *buf) const override;
- Defined *Target;
+ Defined *target;
};
// Windows-specific.
// See comments for DefinedLocalImport class.
-class LocalImportChunk : public Chunk {
+class LocalImportChunk : public NonSectionChunk {
public:
- explicit LocalImportChunk(Defined *S) : Sym(S) {
- Alignment = Config->Wordsize;
+ explicit LocalImportChunk(Defined *s) : sym(s) {
+ setAlignment(config->wordsize);
}
size_t getSize() const override;
- void getBaserels(std::vector<Baserel> *Res) override;
- void writeTo(uint8_t *Buf) const override;
+ void getBaserels(std::vector<Baserel> *res) override;
+ void writeTo(uint8_t *buf) const override;
private:
- Defined *Sym;
+ Defined *sym;
};
// Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and
// offset into the chunk. Order does not matter as the RVA table will be sorted
// later.
struct ChunkAndOffset {
- Chunk *InputChunk;
- uint32_t Offset;
+ Chunk *inputChunk;
+ uint32_t offset;
struct DenseMapInfo {
static ChunkAndOffset getEmptyKey() {
@@ -393,12 +550,12 @@ struct ChunkAndOffset {
static ChunkAndOffset getTombstoneKey() {
return {llvm::DenseMapInfo<Chunk *>::getTombstoneKey(), 0};
}
- static unsigned getHashValue(const ChunkAndOffset &CO) {
+ static unsigned getHashValue(const ChunkAndOffset &co) {
return llvm::DenseMapInfo<std::pair<Chunk *, uint32_t>>::getHashValue(
- {CO.InputChunk, CO.Offset});
+ {co.inputChunk, co.offset});
}
- static bool isEqual(const ChunkAndOffset &LHS, const ChunkAndOffset &RHS) {
- return LHS.InputChunk == RHS.InputChunk && LHS.Offset == RHS.Offset;
+ static bool isEqual(const ChunkAndOffset &lhs, const ChunkAndOffset &rhs) {
+ return lhs.inputChunk == rhs.inputChunk && lhs.offset == rhs.offset;
}
};
};
@@ -406,48 +563,48 @@ struct ChunkAndOffset {
using SymbolRVASet = llvm::DenseSet<ChunkAndOffset>;
// Table which contains symbol RVAs. Used for /safeseh and /guard:cf.
-class RVATableChunk : public Chunk {
+class RVATableChunk : public NonSectionChunk {
public:
- explicit RVATableChunk(SymbolRVASet S) : Syms(std::move(S)) {}
- size_t getSize() const override { return Syms.size() * 4; }
- void writeTo(uint8_t *Buf) const override;
+ explicit RVATableChunk(SymbolRVASet s) : syms(std::move(s)) {}
+ size_t getSize() const override { return syms.size() * 4; }
+ void writeTo(uint8_t *buf) const override;
private:
- SymbolRVASet Syms;
+ SymbolRVASet syms;
};
// Windows-specific.
// This class represents a block in .reloc section.
// See the PE/COFF spec 5.6 for details.
-class BaserelChunk : public Chunk {
+class BaserelChunk : public NonSectionChunk {
public:
- BaserelChunk(uint32_t Page, Baserel *Begin, Baserel *End);
- size_t getSize() const override { return Data.size(); }
- void writeTo(uint8_t *Buf) const override;
+ BaserelChunk(uint32_t page, Baserel *begin, Baserel *end);
+ size_t getSize() const override { return data.size(); }
+ void writeTo(uint8_t *buf) const override;
private:
- std::vector<uint8_t> Data;
+ std::vector<uint8_t> data;
};
class Baserel {
public:
- Baserel(uint32_t V, uint8_t Ty) : RVA(V), Type(Ty) {}
- explicit Baserel(uint32_t V) : Baserel(V, getDefaultType()) {}
+ Baserel(uint32_t v, uint8_t ty) : rva(v), type(ty) {}
+ explicit Baserel(uint32_t v) : Baserel(v, getDefaultType()) {}
uint8_t getDefaultType();
- uint32_t RVA;
- uint8_t Type;
+ uint32_t rva;
+ uint8_t type;
};
// This is a placeholder Chunk, to allow attaching a DefinedSynthetic to a
// specific place in a section, without any data. This is used for the MinGW
// specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept
// of an empty chunk isn't MinGW specific.
-class EmptyChunk : public Chunk {
+class EmptyChunk : public NonSectionChunk {
public:
EmptyChunk() {}
size_t getSize() const override { return 0; }
- void writeTo(uint8_t *Buf) const override {}
+ void writeTo(uint8_t *buf) const override {}
};
// MinGW specific, for the "automatic import of variables from DLLs" feature.
@@ -456,17 +613,17 @@ public:
// the reference didn't use the dllimport attribute. The MinGW runtime will
// process this table after loading, before handling control over to user
// code.
-class PseudoRelocTableChunk : public Chunk {
+class PseudoRelocTableChunk : public NonSectionChunk {
public:
- PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &Relocs)
- : Relocs(std::move(Relocs)) {
- Alignment = 4;
+ PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &relocs)
+ : relocs(std::move(relocs)) {
+ setAlignment(4);
}
size_t getSize() const override;
- void writeTo(uint8_t *Buf) const override;
+ void writeTo(uint8_t *buf) const override;
private:
- std::vector<RuntimePseudoReloc> Relocs;
+ std::vector<RuntimePseudoReloc> relocs;
};
// MinGW specific; information about one individual location in the image
@@ -474,37 +631,48 @@ private:
// one individual element in the PseudoRelocTableChunk table.
class RuntimePseudoReloc {
public:
- RuntimePseudoReloc(Defined *Sym, SectionChunk *Target, uint32_t TargetOffset,
- int Flags)
- : Sym(Sym), Target(Target), TargetOffset(TargetOffset), Flags(Flags) {}
+ RuntimePseudoReloc(Defined *sym, SectionChunk *target, uint32_t targetOffset,
+ int flags)
+ : sym(sym), target(target), targetOffset(targetOffset), flags(flags) {}
- Defined *Sym;
- SectionChunk *Target;
- uint32_t TargetOffset;
+ Defined *sym;
+ SectionChunk *target;
+ uint32_t targetOffset;
// The Flags field contains the size of the relocation, in bits. No other
// flags are currently defined.
- int Flags;
+ int flags;
};
// MinGW specific. A Chunk that contains one pointer-sized absolute value.
-class AbsolutePointerChunk : public Chunk {
+class AbsolutePointerChunk : public NonSectionChunk {
public:
- AbsolutePointerChunk(uint64_t Value) : Value(Value) {
- Alignment = getSize();
+ AbsolutePointerChunk(uint64_t value) : value(value) {
+ setAlignment(getSize());
}
size_t getSize() const override;
- void writeTo(uint8_t *Buf) const override;
+ void writeTo(uint8_t *buf) const override;
private:
- uint64_t Value;
+ uint64_t value;
};
-void applyMOV32T(uint8_t *Off, uint32_t V);
-void applyBranch24T(uint8_t *Off, int32_t V);
+// Return true if this file has the hotpatch flag set to true in the S_COMPILE3
+// record in codeview debug info. Also returns true for some thunks synthesized
+// by the linker.
+inline bool Chunk::isHotPatchable() const {
+ if (auto *sc = dyn_cast<SectionChunk>(this))
+ return sc->file->hotPatchable;
+ else if (isa<ImportThunkChunk>(this))
+ return true;
+ return false;
+}
+
+void applyMOV32T(uint8_t *off, uint32_t v);
+void applyBranch24T(uint8_t *off, int32_t v);
-void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift);
-void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit);
-void applyArm64Branch26(uint8_t *Off, int64_t V);
+void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift);
+void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit);
+void applyArm64Branch26(uint8_t *off, int64_t v);
} // namespace coff
} // namespace lld
diff --git a/COFF/Config.h b/COFF/Config.h
index 8915b6a3bdd8..1b0e24042710 100644
--- a/COFF/Config.h
+++ b/COFF/Config.h
@@ -1,9 +1,8 @@
//===- Config.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -29,6 +28,7 @@ class DefinedAbsolute;
class DefinedRelative;
class StringChunk;
class Symbol;
+class InputFile;
// Short aliases.
static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
@@ -38,30 +38,30 @@ static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386;
// Represents an /export option.
struct Export {
- StringRef Name; // N in /export:N or /export:E=N
- StringRef ExtName; // E in /export:E=N
- Symbol *Sym = nullptr;
- uint16_t Ordinal = 0;
- bool Noname = false;
- bool Data = false;
- bool Private = false;
- bool Constant = false;
+ StringRef name; // N in /export:N or /export:E=N
+ StringRef extName; // E in /export:E=N
+ Symbol *sym = nullptr;
+ uint16_t ordinal = 0;
+ bool noname = false;
+ bool data = false;
+ bool isPrivate = false;
+ bool constant = false;
// If an export is a form of /export:foo=dllname.bar, that means
// that foo should be exported as an alias to bar in the DLL.
- // ForwardTo is set to "dllname.bar" part. Usually empty.
- StringRef ForwardTo;
- StringChunk *ForwardChunk = nullptr;
+ // forwardTo is set to "dllname.bar" part. Usually empty.
+ StringRef forwardTo;
+ StringChunk *forwardChunk = nullptr;
// True if this /export option was in .drectves section.
- bool Directives = false;
- StringRef SymbolName;
- StringRef ExportName; // Name in DLL
-
- bool operator==(const Export &E) {
- return (Name == E.Name && ExtName == E.ExtName &&
- Ordinal == E.Ordinal && Noname == E.Noname &&
- Data == E.Data && Private == E.Private);
+ bool directives = false;
+ StringRef symbolName;
+ StringRef exportName; // Name in DLL
+
+ bool operator==(const Export &e) {
+ return (name == e.name && extName == e.extName &&
+ ordinal == e.ordinal && noname == e.noname &&
+ data == e.data && isPrivate == e.isPrivate);
}
};
@@ -81,130 +81,149 @@ enum class GuardCFLevel {
// Global configuration.
struct Configuration {
enum ManifestKind { SideBySide, Embed, No };
- bool is64() { return Machine == AMD64 || Machine == ARM64; }
-
- llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN;
- size_t Wordsize;
- bool Verbose = false;
- WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
- Symbol *Entry = nullptr;
- bool NoEntry = false;
- std::string OutputFile;
- std::string ImportName;
- bool DoGC = true;
- bool DoICF = true;
- bool TailMerge;
- bool Relocatable = true;
- bool ForceMultiple = false;
- bool ForceUnresolved = false;
- bool Debug = false;
- bool DebugDwarf = false;
- bool DebugGHashes = false;
- bool DebugSymtab = false;
- bool ShowTiming = false;
- unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
- std::vector<std::string> NatvisFiles;
- llvm::SmallString<128> PDBAltPath;
- llvm::SmallString<128> PDBPath;
- llvm::SmallString<128> PDBSourcePath;
- std::vector<llvm::StringRef> Argv;
+ bool is64() { return machine == AMD64 || machine == ARM64; }
+
+ llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN;
+ size_t wordsize;
+ bool verbose = false;
+ WindowsSubsystem subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
+ Symbol *entry = nullptr;
+ bool noEntry = false;
+ std::string outputFile;
+ std::string importName;
+ bool demangle = true;
+ bool doGC = true;
+ bool doICF = true;
+ bool tailMerge;
+ bool relocatable = true;
+ bool forceMultiple = false;
+ bool forceMultipleRes = false;
+ bool forceUnresolved = false;
+ bool debug = false;
+ bool debugDwarf = false;
+ bool debugGHashes = false;
+ bool debugSymtab = false;
+ bool showTiming = false;
+ bool showSummary = false;
+ unsigned debugTypes = static_cast<unsigned>(DebugType::None);
+ std::vector<std::string> natvisFiles;
+ llvm::SmallString<128> pdbAltPath;
+ llvm::SmallString<128> pdbPath;
+ llvm::SmallString<128> pdbSourcePath;
+ std::vector<llvm::StringRef> argv;
// Symbols in this set are considered as live by the garbage collector.
- std::vector<Symbol *> GCRoot;
+ std::vector<Symbol *> gcroot;
- std::set<StringRef> NoDefaultLibs;
- bool NoDefaultLibAll = false;
+ std::set<std::string> noDefaultLibs;
+ bool noDefaultLibAll = false;
// True if we are creating a DLL.
- bool DLL = false;
- StringRef Implib;
- std::vector<Export> Exports;
- std::set<std::string> DelayLoads;
- std::map<std::string, int> DLLOrder;
- Symbol *DelayLoadHelper = nullptr;
+ bool dll = false;
+ StringRef implib;
+ std::vector<Export> exports;
+ std::set<std::string> delayLoads;
+ std::map<std::string, int> dllOrder;
+ Symbol *delayLoadHelper = nullptr;
- bool SaveTemps = false;
+ bool saveTemps = false;
// /guard:cf
- GuardCFLevel GuardCF = GuardCFLevel::Off;
+ GuardCFLevel guardCF = GuardCFLevel::Off;
// Used for SafeSEH.
- Symbol *SEHTable = nullptr;
- Symbol *SEHCount = nullptr;
+ bool safeSEH = false;
+ Symbol *sehTable = nullptr;
+ Symbol *sehCount = nullptr;
// Used for /opt:lldlto=N
- unsigned LTOO = 2;
+ unsigned ltoo = 2;
// Used for /opt:lldltojobs=N
- unsigned ThinLTOJobs = 0;
+ unsigned thinLTOJobs = 0;
// Used for /opt:lldltopartitions=N
- unsigned LTOPartitions = 1;
+ unsigned ltoPartitions = 1;
// Used for /opt:lldltocache=path
- StringRef LTOCache;
+ StringRef ltoCache;
// Used for /opt:lldltocachepolicy=policy
- llvm::CachePruningPolicy LTOCachePolicy;
+ llvm::CachePruningPolicy ltoCachePolicy;
// Used for /merge:from=to (e.g. /merge:.rdata=.text)
- std::map<StringRef, StringRef> Merge;
+ std::map<StringRef, StringRef> merge;
// Used for /section=.name,{DEKPRSW} to set section attributes.
- std::map<StringRef, uint32_t> Section;
+ std::map<StringRef, uint32_t> section;
// Options for manifest files.
- ManifestKind Manifest = No;
- int ManifestID = 1;
- StringRef ManifestDependency;
- bool ManifestUAC = true;
- std::vector<std::string> ManifestInput;
- StringRef ManifestLevel = "'asInvoker'";
- StringRef ManifestUIAccess = "'false'";
- StringRef ManifestFile;
+ ManifestKind manifest = No;
+ int manifestID = 1;
+ StringRef manifestDependency;
+ bool manifestUAC = true;
+ std::vector<std::string> manifestInput;
+ StringRef manifestLevel = "'asInvoker'";
+ StringRef manifestUIAccess = "'false'";
+ StringRef manifestFile;
// Used for /aligncomm.
- std::map<std::string, int> AlignComm;
+ std::map<std::string, int> alignComm;
// Used for /failifmismatch.
- std::map<StringRef, StringRef> MustMatch;
+ std::map<StringRef, std::pair<StringRef, InputFile *>> mustMatch;
// Used for /alternatename.
- std::map<StringRef, StringRef> AlternateNames;
+ std::map<StringRef, StringRef> alternateNames;
// Used for /order.
- llvm::StringMap<int> Order;
+ llvm::StringMap<int> order;
// Used for /lldmap.
- std::string MapFile;
-
- uint64_t ImageBase = -1;
- uint64_t StackReserve = 1024 * 1024;
- uint64_t StackCommit = 4096;
- uint64_t HeapReserve = 1024 * 1024;
- uint64_t HeapCommit = 4096;
- uint32_t MajorImageVersion = 0;
- uint32_t MinorImageVersion = 0;
- uint32_t MajorOSVersion = 6;
- uint32_t MinorOSVersion = 0;
- uint32_t Timestamp = 0;
- bool DynamicBase = true;
- bool AllowBind = true;
- bool NxCompat = true;
- bool AllowIsolation = true;
- bool TerminalServerAware = true;
- bool LargeAddressAware = false;
- bool HighEntropyVA = false;
- bool AppContainer = false;
- bool MinGW = false;
- bool WarnMissingOrderSymbol = true;
- bool WarnLocallyDefinedImported = true;
- bool WarnDebugInfoUnusable = true;
- bool Incremental = true;
- bool IntegrityCheck = false;
- bool KillAt = false;
- bool Repro = false;
+ std::string mapFile;
+
+ // Used for /thinlto-index-only:
+ llvm::StringRef thinLTOIndexOnlyArg;
+
+ // Used for /thinlto-object-prefix-replace:
+ std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
+
+ // Used for /thinlto-object-suffix-replace:
+ std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
+
+ uint64_t imageBase = -1;
+ uint64_t fileAlign = 512;
+ uint64_t stackReserve = 1024 * 1024;
+ uint64_t stackCommit = 4096;
+ uint64_t heapReserve = 1024 * 1024;
+ uint64_t heapCommit = 4096;
+ uint32_t majorImageVersion = 0;
+ uint32_t minorImageVersion = 0;
+ uint32_t majorOSVersion = 6;
+ uint32_t minorOSVersion = 0;
+ uint32_t timestamp = 0;
+ uint32_t functionPadMin = 0;
+ bool dynamicBase = true;
+ bool allowBind = true;
+ bool nxCompat = true;
+ bool allowIsolation = true;
+ bool terminalServerAware = true;
+ bool largeAddressAware = false;
+ bool highEntropyVA = false;
+ bool appContainer = false;
+ bool mingw = false;
+ bool warnMissingOrderSymbol = true;
+ bool warnLocallyDefinedImported = true;
+ bool warnDebugInfoUnusable = true;
+ bool incremental = true;
+ bool integrityCheck = false;
+ bool killAt = false;
+ bool repro = false;
+ bool swaprunCD = false;
+ bool swaprunNet = false;
+ bool thinLTOEmitImportsFiles;
+ bool thinLTOIndexOnly;
};
-extern Configuration *Config;
+extern Configuration *config;
} // namespace coff
} // namespace lld
diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp
index 599cc5892a16..40d1f463aa3f 100644
--- a/COFF/DLL.cpp
+++ b/COFF/DLL.cpp
@@ -1,9 +1,8 @@
//===- DLL.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -36,149 +35,167 @@ namespace {
// Import table
// A chunk for the import descriptor table.
-class HintNameChunk : public Chunk {
+class HintNameChunk : public NonSectionChunk {
public:
- HintNameChunk(StringRef N, uint16_t H) : Name(N), Hint(H) {}
+ HintNameChunk(StringRef n, uint16_t h) : name(n), hint(h) {}
size_t getSize() const override {
// Starts with 2 byte Hint field, followed by a null-terminated string,
// ends with 0 or 1 byte padding.
- return alignTo(Name.size() + 3, 2);
+ return alignTo(name.size() + 3, 2);
}
- void writeTo(uint8_t *Buf) const override {
- write16le(Buf + OutputSectionOff, Hint);
- memcpy(Buf + OutputSectionOff + 2, Name.data(), Name.size());
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, getSize());
+ write16le(buf, hint);
+ memcpy(buf + 2, name.data(), name.size());
}
private:
- StringRef Name;
- uint16_t Hint;
+ StringRef name;
+ uint16_t hint;
};
// A chunk for the import descriptor table.
-class LookupChunk : public Chunk {
+class LookupChunk : public NonSectionChunk {
public:
- explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = Config->Wordsize; }
- size_t getSize() const override { return Config->Wordsize; }
+ explicit LookupChunk(Chunk *c) : hintName(c) {
+ setAlignment(config->wordsize);
+ }
+ size_t getSize() const override { return config->wordsize; }
- void writeTo(uint8_t *Buf) const override {
- write32le(Buf + OutputSectionOff, HintName->getRVA());
+ void writeTo(uint8_t *buf) const override {
+ if (config->is64())
+ write64le(buf, hintName->getRVA());
+ else
+ write32le(buf, hintName->getRVA());
}
- Chunk *HintName;
+ Chunk *hintName;
};
// A chunk for the import descriptor table.
// This chunk represent import-by-ordinal symbols.
// See Microsoft PE/COFF spec 7.1. Import Header for details.
-class OrdinalOnlyChunk : public Chunk {
+class OrdinalOnlyChunk : public NonSectionChunk {
public:
- explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) {
- Alignment = Config->Wordsize;
+ explicit OrdinalOnlyChunk(uint16_t v) : ordinal(v) {
+ setAlignment(config->wordsize);
}
- size_t getSize() const override { return Config->Wordsize; }
+ size_t getSize() const override { return config->wordsize; }
- void writeTo(uint8_t *Buf) const override {
+ void writeTo(uint8_t *buf) const override {
// An import-by-ordinal slot has MSB 1 to indicate that
// this is import-by-ordinal (and not import-by-name).
- if (Config->is64()) {
- write64le(Buf + OutputSectionOff, (1ULL << 63) | Ordinal);
+ if (config->is64()) {
+ write64le(buf, (1ULL << 63) | ordinal);
} else {
- write32le(Buf + OutputSectionOff, (1ULL << 31) | Ordinal);
+ write32le(buf, (1ULL << 31) | ordinal);
}
}
- uint16_t Ordinal;
+ uint16_t ordinal;
};
// A chunk for the import descriptor table.
-class ImportDirectoryChunk : public Chunk {
+class ImportDirectoryChunk : public NonSectionChunk {
public:
- explicit ImportDirectoryChunk(Chunk *N) : DLLName(N) {}
+ explicit ImportDirectoryChunk(Chunk *n) : dllName(n) {}
size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); }
- void writeTo(uint8_t *Buf) const override {
- auto *E = (coff_import_directory_table_entry *)(Buf + OutputSectionOff);
- E->ImportLookupTableRVA = LookupTab->getRVA();
- E->NameRVA = DLLName->getRVA();
- E->ImportAddressTableRVA = AddressTab->getRVA();
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, getSize());
+
+ auto *e = (coff_import_directory_table_entry *)(buf);
+ e->ImportLookupTableRVA = lookupTab->getRVA();
+ e->NameRVA = dllName->getRVA();
+ e->ImportAddressTableRVA = addressTab->getRVA();
}
- Chunk *DLLName;
- Chunk *LookupTab;
- Chunk *AddressTab;
+ Chunk *dllName;
+ Chunk *lookupTab;
+ Chunk *addressTab;
};
// A chunk representing null terminator in the import table.
// Contents of this chunk is always null bytes.
-class NullChunk : public Chunk {
+class NullChunk : public NonSectionChunk {
public:
- explicit NullChunk(size_t N) : Size(N) {}
- bool hasData() const override { return false; }
- size_t getSize() const override { return Size; }
+ explicit NullChunk(size_t n) : size(n) { hasData = false; }
+ size_t getSize() const override { return size; }
+
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, size);
+ }
private:
- size_t Size;
+ size_t size;
};
static std::vector<std::vector<DefinedImportData *>>
-binImports(const std::vector<DefinedImportData *> &Imports) {
+binImports(const std::vector<DefinedImportData *> &imports) {
// Group DLL-imported symbols by DLL name because that's how
// symbols are layed out in the import descriptor table.
- auto Less = [](const std::string &A, const std::string &B) {
- return Config->DLLOrder[A] < Config->DLLOrder[B];
+ auto less = [](const std::string &a, const std::string &b) {
+ return config->dllOrder[a] < config->dllOrder[b];
};
std::map<std::string, std::vector<DefinedImportData *>,
- bool(*)(const std::string &, const std::string &)> M(Less);
- for (DefinedImportData *Sym : Imports)
- M[Sym->getDLLName().lower()].push_back(Sym);
+ bool(*)(const std::string &, const std::string &)> m(less);
+ for (DefinedImportData *sym : imports)
+ m[sym->getDLLName().lower()].push_back(sym);
- std::vector<std::vector<DefinedImportData *>> V;
- for (auto &KV : M) {
+ std::vector<std::vector<DefinedImportData *>> v;
+ for (auto &kv : m) {
// Sort symbols by name for each group.
- std::vector<DefinedImportData *> &Syms = KV.second;
- std::sort(Syms.begin(), Syms.end(),
- [](DefinedImportData *A, DefinedImportData *B) {
- return A->getName() < B->getName();
+ std::vector<DefinedImportData *> &syms = kv.second;
+ std::sort(syms.begin(), syms.end(),
+ [](DefinedImportData *a, DefinedImportData *b) {
+ return a->getName() < b->getName();
});
- V.push_back(std::move(Syms));
+ v.push_back(std::move(syms));
}
- return V;
+ return v;
}
// Export table
// See Microsoft PE/COFF spec 4.3 for details.
// A chunk for the delay import descriptor table etnry.
-class DelayDirectoryChunk : public Chunk {
+class DelayDirectoryChunk : public NonSectionChunk {
public:
- explicit DelayDirectoryChunk(Chunk *N) : DLLName(N) {}
+ explicit DelayDirectoryChunk(Chunk *n) : dllName(n) {}
size_t getSize() const override {
return sizeof(delay_import_directory_table_entry);
}
- void writeTo(uint8_t *Buf) const override {
- auto *E = (delay_import_directory_table_entry *)(Buf + OutputSectionOff);
- E->Attributes = 1;
- E->Name = DLLName->getRVA();
- E->ModuleHandle = ModuleHandle->getRVA();
- E->DelayImportAddressTable = AddressTab->getRVA();
- E->DelayImportNameTable = NameTab->getRVA();
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, getSize());
+
+ auto *e = (delay_import_directory_table_entry *)(buf);
+ e->Attributes = 1;
+ e->Name = dllName->getRVA();
+ e->ModuleHandle = moduleHandle->getRVA();
+ e->DelayImportAddressTable = addressTab->getRVA();
+ e->DelayImportNameTable = nameTab->getRVA();
}
- Chunk *DLLName;
- Chunk *ModuleHandle;
- Chunk *AddressTab;
- Chunk *NameTab;
+ Chunk *dllName;
+ Chunk *moduleHandle;
+ Chunk *addressTab;
+ Chunk *nameTab;
};
// Initial contents for delay-loaded functions.
// This code calls __delayLoadHelper2 function to resolve a symbol
// and then overwrites its jump table slot with the result
// for subsequent function calls.
-static const uint8_t ThunkX64[] = {
+static const uint8_t thunkX64[] = {
+ 0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_<FUNCNAME>]
+ 0xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib>
+};
+
+static const uint8_t tailMergeX64[] = {
0x51, // push rcx
0x52, // push rdx
0x41, 0x50, // push r8
@@ -188,7 +205,7 @@ static const uint8_t ThunkX64[] = {
0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm1
0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm2
0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm3
- 0x48, 0x8D, 0x15, 0, 0, 0, 0, // lea rdx, [__imp_<FUNCNAME>]
+ 0x48, 0x8B, 0xD0, // mov rdx, rax
0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...]
0xE8, 0, 0, 0, 0, // call __delayLoadHelper2
0x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp]
@@ -203,10 +220,15 @@ static const uint8_t ThunkX64[] = {
0xFF, 0xE0, // jmp rax
};
-static const uint8_t ThunkX86[] = {
+static const uint8_t thunkX86[] = {
+ 0xB8, 0, 0, 0, 0, // mov eax, offset ___imp__<FUNCNAME>
+ 0xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib>
+};
+
+static const uint8_t tailMergeX86[] = {
0x51, // push ecx
0x52, // push edx
- 0x68, 0, 0, 0, 0, // push offset ___imp__<FUNCNAME>
+ 0x50, // push eax
0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR_<DLLNAME>_dll
0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8
0x5A, // pop edx
@@ -214,9 +236,13 @@ static const uint8_t ThunkX86[] = {
0xFF, 0xE0, // jmp eax
};
-static const uint8_t ThunkARM[] = {
+static const uint8_t thunkARM[] = {
0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 __imp_<FUNCNAME>
0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_<FUNCNAME>
+ 0x00, 0xf0, 0x00, 0xb8, // b.w __tailMerge_<lib>
+};
+
+static const uint8_t tailMergeARM[] = {
0x2d, 0xe9, 0x0f, 0x48, // push.w {r0, r1, r2, r3, r11, lr}
0x0d, 0xf2, 0x10, 0x0b, // addw r11, sp, #16
0x2d, 0xed, 0x10, 0x0b, // vpush {d0, d1, d2, d3, d4, d5, d6, d7}
@@ -230,9 +256,13 @@ static const uint8_t ThunkARM[] = {
0x60, 0x47, // bx ip
};
-static const uint8_t ThunkARM64[] = {
+static const uint8_t thunkARM64[] = {
0x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_<FUNCNAME>
0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_<FUNCNAME>
+ 0x00, 0x00, 0x00, 0x14, // b __tailMerge_<lib>
+};
+
+static const uint8_t tailMergeARM64[] = {
0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]!
0xfd, 0x03, 0x00, 0x91, // mov x29, sp
0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16]
@@ -261,370 +291,445 @@ static const uint8_t ThunkARM64[] = {
};
// A chunk for the delay import thunk.
-class ThunkChunkX64 : public Chunk {
+class ThunkChunkX64 : public NonSectionChunk {
+public:
+ ThunkChunkX64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
+
+ size_t getSize() const override { return sizeof(thunkX64); }
+
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, thunkX64, sizeof(thunkX64));
+ write32le(buf + 3, imp->getRVA() - rva - 7);
+ write32le(buf + 8, tailMerge->getRVA() - rva - 12);
+ }
+
+ Defined *imp = nullptr;
+ Chunk *tailMerge = nullptr;
+};
+
+class TailMergeChunkX64 : public NonSectionChunk {
+public:
+ TailMergeChunkX64(Chunk *d, Defined *h) : desc(d), helper(h) {}
+
+ size_t getSize() const override { return sizeof(tailMergeX64); }
+
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, tailMergeX64, sizeof(tailMergeX64));
+ write32le(buf + 39, desc->getRVA() - rva - 43);
+ write32le(buf + 44, helper->getRVA() - rva - 48);
+ }
+
+ Chunk *desc = nullptr;
+ Defined *helper = nullptr;
+};
+
+class ThunkChunkX86 : public NonSectionChunk {
+public:
+ ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
+
+ size_t getSize() const override { return sizeof(thunkX86); }
+
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, thunkX86, sizeof(thunkX86));
+ write32le(buf + 1, imp->getRVA() + config->imageBase);
+ write32le(buf + 6, tailMerge->getRVA() - rva - 10);
+ }
+
+ void getBaserels(std::vector<Baserel> *res) override {
+ res->emplace_back(rva + 1);
+ }
+
+ Defined *imp = nullptr;
+ Chunk *tailMerge = nullptr;
+};
+
+class TailMergeChunkX86 : public NonSectionChunk {
public:
- ThunkChunkX64(Defined *I, Chunk *D, Defined *H)
- : Imp(I), Desc(D), Helper(H) {}
+ TailMergeChunkX86(Chunk *d, Defined *h) : desc(d), helper(h) {}
- size_t getSize() const override { return sizeof(ThunkX64); }
+ size_t getSize() const override { return sizeof(tailMergeX86); }
- void writeTo(uint8_t *Buf) const override {
- memcpy(Buf + OutputSectionOff, ThunkX64, sizeof(ThunkX64));
- write32le(Buf + OutputSectionOff + 36, Imp->getRVA() - RVA - 40);
- write32le(Buf + OutputSectionOff + 43, Desc->getRVA() - RVA - 47);
- write32le(Buf + OutputSectionOff + 48, Helper->getRVA() - RVA - 52);
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, tailMergeX86, sizeof(tailMergeX86));
+ write32le(buf + 4, desc->getRVA() + config->imageBase);
+ write32le(buf + 9, helper->getRVA() - rva - 13);
}
- Defined *Imp = nullptr;
- Chunk *Desc = nullptr;
- Defined *Helper = nullptr;
+ void getBaserels(std::vector<Baserel> *res) override {
+ res->emplace_back(rva + 4);
+ }
+
+ Chunk *desc = nullptr;
+ Defined *helper = nullptr;
};
-class ThunkChunkX86 : public Chunk {
+class ThunkChunkARM : public NonSectionChunk {
public:
- ThunkChunkX86(Defined *I, Chunk *D, Defined *H)
- : Imp(I), Desc(D), Helper(H) {}
+ ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
- size_t getSize() const override { return sizeof(ThunkX86); }
+ size_t getSize() const override { return sizeof(thunkARM); }
- void writeTo(uint8_t *Buf) const override {
- memcpy(Buf + OutputSectionOff, ThunkX86, sizeof(ThunkX86));
- write32le(Buf + OutputSectionOff + 3, Imp->getRVA() + Config->ImageBase);
- write32le(Buf + OutputSectionOff + 8, Desc->getRVA() + Config->ImageBase);
- write32le(Buf + OutputSectionOff + 13, Helper->getRVA() - RVA - 17);
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, thunkARM, sizeof(thunkARM));
+ applyMOV32T(buf + 0, imp->getRVA() + config->imageBase);
+ applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12);
}
- void getBaserels(std::vector<Baserel> *Res) override {
- Res->emplace_back(RVA + 3);
- Res->emplace_back(RVA + 8);
+ void getBaserels(std::vector<Baserel> *res) override {
+ res->emplace_back(rva + 0, IMAGE_REL_BASED_ARM_MOV32T);
}
- Defined *Imp = nullptr;
- Chunk *Desc = nullptr;
- Defined *Helper = nullptr;
+ Defined *imp = nullptr;
+ Chunk *tailMerge = nullptr;
};
-class ThunkChunkARM : public Chunk {
+class TailMergeChunkARM : public NonSectionChunk {
public:
- ThunkChunkARM(Defined *I, Chunk *D, Defined *H)
- : Imp(I), Desc(D), Helper(H) {}
+ TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {}
- size_t getSize() const override { return sizeof(ThunkARM); }
+ size_t getSize() const override { return sizeof(tailMergeARM); }
- void writeTo(uint8_t *Buf) const override {
- memcpy(Buf + OutputSectionOff, ThunkARM, sizeof(ThunkARM));
- applyMOV32T(Buf + OutputSectionOff + 0, Imp->getRVA() + Config->ImageBase);
- applyMOV32T(Buf + OutputSectionOff + 22, Desc->getRVA() + Config->ImageBase);
- applyBranch24T(Buf + OutputSectionOff + 30, Helper->getRVA() - RVA - 34);
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, tailMergeARM, sizeof(tailMergeARM));
+ applyMOV32T(buf + 14, desc->getRVA() + config->imageBase);
+ applyBranch24T(buf + 22, helper->getRVA() - rva - 26);
}
- void getBaserels(std::vector<Baserel> *Res) override {
- Res->emplace_back(RVA + 0, IMAGE_REL_BASED_ARM_MOV32T);
- Res->emplace_back(RVA + 22, IMAGE_REL_BASED_ARM_MOV32T);
+ void getBaserels(std::vector<Baserel> *res) override {
+ res->emplace_back(rva + 14, IMAGE_REL_BASED_ARM_MOV32T);
}
- Defined *Imp = nullptr;
- Chunk *Desc = nullptr;
- Defined *Helper = nullptr;
+ Chunk *desc = nullptr;
+ Defined *helper = nullptr;
};
-class ThunkChunkARM64 : public Chunk {
+class ThunkChunkARM64 : public NonSectionChunk {
public:
- ThunkChunkARM64(Defined *I, Chunk *D, Defined *H)
- : Imp(I), Desc(D), Helper(H) {}
+ ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
- size_t getSize() const override { return sizeof(ThunkARM64); }
+ size_t getSize() const override { return sizeof(thunkARM64); }
- void writeTo(uint8_t *Buf) const override {
- memcpy(Buf + OutputSectionOff, ThunkARM64, sizeof(ThunkARM64));
- applyArm64Addr(Buf + OutputSectionOff + 0, Imp->getRVA(), RVA + 0, 12);
- applyArm64Imm(Buf + OutputSectionOff + 4, Imp->getRVA() & 0xfff, 0);
- applyArm64Addr(Buf + OutputSectionOff + 52, Desc->getRVA(), RVA + 52, 12);
- applyArm64Imm(Buf + OutputSectionOff + 56, Desc->getRVA() & 0xfff, 0);
- applyArm64Branch26(Buf + OutputSectionOff + 60,
- Helper->getRVA() - RVA - 60);
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, thunkARM64, sizeof(thunkARM64));
+ applyArm64Addr(buf + 0, imp->getRVA(), rva + 0, 12);
+ applyArm64Imm(buf + 4, imp->getRVA() & 0xfff, 0);
+ applyArm64Branch26(buf + 8, tailMerge->getRVA() - rva - 8);
}
- Defined *Imp = nullptr;
- Chunk *Desc = nullptr;
- Defined *Helper = nullptr;
+ Defined *imp = nullptr;
+ Chunk *tailMerge = nullptr;
+};
+
+class TailMergeChunkARM64 : public NonSectionChunk {
+public:
+ TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) {}
+
+ size_t getSize() const override { return sizeof(tailMergeARM64); }
+
+ void writeTo(uint8_t *buf) const override {
+ memcpy(buf, tailMergeARM64, sizeof(tailMergeARM64));
+ applyArm64Addr(buf + 44, desc->getRVA(), rva + 44, 12);
+ applyArm64Imm(buf + 48, desc->getRVA() & 0xfff, 0);
+ applyArm64Branch26(buf + 52, helper->getRVA() - rva - 52);
+ }
+
+ Chunk *desc = nullptr;
+ Defined *helper = nullptr;
};
// A chunk for the import descriptor table.
-class DelayAddressChunk : public Chunk {
+class DelayAddressChunk : public NonSectionChunk {
public:
- explicit DelayAddressChunk(Chunk *C) : Thunk(C) {
- Alignment = Config->Wordsize;
+ explicit DelayAddressChunk(Chunk *c) : thunk(c) {
+ setAlignment(config->wordsize);
}
- size_t getSize() const override { return Config->Wordsize; }
+ size_t getSize() const override { return config->wordsize; }
- void writeTo(uint8_t *Buf) const override {
- if (Config->is64()) {
- write64le(Buf + OutputSectionOff, Thunk->getRVA() + Config->ImageBase);
+ void writeTo(uint8_t *buf) const override {
+ if (config->is64()) {
+ write64le(buf, thunk->getRVA() + config->imageBase);
} else {
- uint32_t Bit = 0;
+ uint32_t bit = 0;
// Pointer to thumb code must have the LSB set, so adjust it.
- if (Config->Machine == ARMNT)
- Bit = 1;
- write32le(Buf + OutputSectionOff, (Thunk->getRVA() + Config->ImageBase) | Bit);
+ if (config->machine == ARMNT)
+ bit = 1;
+ write32le(buf, (thunk->getRVA() + config->imageBase) | bit);
}
}
- void getBaserels(std::vector<Baserel> *Res) override {
- Res->emplace_back(RVA);
+ void getBaserels(std::vector<Baserel> *res) override {
+ res->emplace_back(rva);
}
- Chunk *Thunk;
+ Chunk *thunk;
};
// Export table
// Read Microsoft PE/COFF spec 5.3 for details.
// A chunk for the export descriptor table.
-class ExportDirectoryChunk : public Chunk {
+class ExportDirectoryChunk : public NonSectionChunk {
public:
- ExportDirectoryChunk(int I, int J, Chunk *D, Chunk *A, Chunk *N, Chunk *O)
- : MaxOrdinal(I), NameTabSize(J), DLLName(D), AddressTab(A), NameTab(N),
- OrdinalTab(O) {}
+ ExportDirectoryChunk(int i, int j, Chunk *d, Chunk *a, Chunk *n, Chunk *o)
+ : maxOrdinal(i), nameTabSize(j), dllName(d), addressTab(a), nameTab(n),
+ ordinalTab(o) {}
size_t getSize() const override {
return sizeof(export_directory_table_entry);
}
- void writeTo(uint8_t *Buf) const override {
- auto *E = (export_directory_table_entry *)(Buf + OutputSectionOff);
- E->NameRVA = DLLName->getRVA();
- E->OrdinalBase = 0;
- E->AddressTableEntries = MaxOrdinal + 1;
- E->NumberOfNamePointers = NameTabSize;
- E->ExportAddressTableRVA = AddressTab->getRVA();
- E->NamePointerRVA = NameTab->getRVA();
- E->OrdinalTableRVA = OrdinalTab->getRVA();
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, getSize());
+
+ auto *e = (export_directory_table_entry *)(buf);
+ e->NameRVA = dllName->getRVA();
+ e->OrdinalBase = 0;
+ e->AddressTableEntries = maxOrdinal + 1;
+ e->NumberOfNamePointers = nameTabSize;
+ e->ExportAddressTableRVA = addressTab->getRVA();
+ e->NamePointerRVA = nameTab->getRVA();
+ e->OrdinalTableRVA = ordinalTab->getRVA();
}
- uint16_t MaxOrdinal;
- uint16_t NameTabSize;
- Chunk *DLLName;
- Chunk *AddressTab;
- Chunk *NameTab;
- Chunk *OrdinalTab;
+ uint16_t maxOrdinal;
+ uint16_t nameTabSize;
+ Chunk *dllName;
+ Chunk *addressTab;
+ Chunk *nameTab;
+ Chunk *ordinalTab;
};
-class AddressTableChunk : public Chunk {
+class AddressTableChunk : public NonSectionChunk {
public:
- explicit AddressTableChunk(size_t MaxOrdinal) : Size(MaxOrdinal + 1) {}
- size_t getSize() const override { return Size * 4; }
+ explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal + 1) {}
+ size_t getSize() const override { return size * 4; }
- void writeTo(uint8_t *Buf) const override {
- memset(Buf + OutputSectionOff, 0, getSize());
+ void writeTo(uint8_t *buf) const override {
+ memset(buf, 0, getSize());
- for (const Export &E : Config->Exports) {
- uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4;
- uint32_t Bit = 0;
+ for (const Export &e : config->exports) {
+ uint8_t *p = buf + e.ordinal * 4;
+ uint32_t bit = 0;
// Pointer to thumb code must have the LSB set, so adjust it.
- if (Config->Machine == ARMNT && !E.Data)
- Bit = 1;
- if (E.ForwardChunk) {
- write32le(P, E.ForwardChunk->getRVA() | Bit);
+ if (config->machine == ARMNT && !e.data)
+ bit = 1;
+ if (e.forwardChunk) {
+ write32le(p, e.forwardChunk->getRVA() | bit);
} else {
- write32le(P, cast<Defined>(E.Sym)->getRVA() | Bit);
+ write32le(p, cast<Defined>(e.sym)->getRVA() | bit);
}
}
}
private:
- size_t Size;
+ size_t size;
};
-class NamePointersChunk : public Chunk {
+class NamePointersChunk : public NonSectionChunk {
public:
- explicit NamePointersChunk(std::vector<Chunk *> &V) : Chunks(V) {}
- size_t getSize() const override { return Chunks.size() * 4; }
-
- void writeTo(uint8_t *Buf) const override {
- uint8_t *P = Buf + OutputSectionOff;
- for (Chunk *C : Chunks) {
- write32le(P, C->getRVA());
- P += 4;
+ explicit NamePointersChunk(std::vector<Chunk *> &v) : chunks(v) {}
+ size_t getSize() const override { return chunks.size() * 4; }
+
+ void writeTo(uint8_t *buf) const override {
+ for (Chunk *c : chunks) {
+ write32le(buf, c->getRVA());
+ buf += 4;
}
}
private:
- std::vector<Chunk *> Chunks;
+ std::vector<Chunk *> chunks;
};
-class ExportOrdinalChunk : public Chunk {
+class ExportOrdinalChunk : public NonSectionChunk {
public:
- explicit ExportOrdinalChunk(size_t I) : Size(I) {}
- size_t getSize() const override { return Size * 2; }
+ explicit ExportOrdinalChunk(size_t i) : size(i) {}
+ size_t getSize() const override { return size * 2; }
- void writeTo(uint8_t *Buf) const override {
- uint8_t *P = Buf + OutputSectionOff;
- for (Export &E : Config->Exports) {
- if (E.Noname)
+ void writeTo(uint8_t *buf) const override {
+ for (Export &e : config->exports) {
+ if (e.noname)
continue;
- write16le(P, E.Ordinal);
- P += 2;
+ write16le(buf, e.ordinal);
+ buf += 2;
}
}
private:
- size_t Size;
+ size_t size;
};
} // anonymous namespace
void IdataContents::create() {
- std::vector<std::vector<DefinedImportData *>> V = binImports(Imports);
+ std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
// Create .idata contents for each DLL.
- for (std::vector<DefinedImportData *> &Syms : V) {
+ for (std::vector<DefinedImportData *> &syms : v) {
// Create lookup and address tables. If they have external names,
- // we need to create HintName chunks to store the names.
+ // we need to create hintName chunks to store the names.
// If they don't (if they are import-by-ordinals), we store only
// ordinal values to the table.
- size_t Base = Lookups.size();
- for (DefinedImportData *S : Syms) {
- uint16_t Ord = S->getOrdinal();
- if (S->getExternalName().empty()) {
- Lookups.push_back(make<OrdinalOnlyChunk>(Ord));
- Addresses.push_back(make<OrdinalOnlyChunk>(Ord));
+ size_t base = lookups.size();
+ for (DefinedImportData *s : syms) {
+ uint16_t ord = s->getOrdinal();
+ if (s->getExternalName().empty()) {
+ lookups.push_back(make<OrdinalOnlyChunk>(ord));
+ addresses.push_back(make<OrdinalOnlyChunk>(ord));
continue;
}
- auto *C = make<HintNameChunk>(S->getExternalName(), Ord);
- Lookups.push_back(make<LookupChunk>(C));
- Addresses.push_back(make<LookupChunk>(C));
- Hints.push_back(C);
+ auto *c = make<HintNameChunk>(s->getExternalName(), ord);
+ lookups.push_back(make<LookupChunk>(c));
+ addresses.push_back(make<LookupChunk>(c));
+ hints.push_back(c);
}
// Terminate with null values.
- Lookups.push_back(make<NullChunk>(Config->Wordsize));
- Addresses.push_back(make<NullChunk>(Config->Wordsize));
+ lookups.push_back(make<NullChunk>(config->wordsize));
+ addresses.push_back(make<NullChunk>(config->wordsize));
- for (int I = 0, E = Syms.size(); I < E; ++I)
- Syms[I]->setLocation(Addresses[Base + I]);
+ for (int i = 0, e = syms.size(); i < e; ++i)
+ syms[i]->setLocation(addresses[base + i]);
// Create the import table header.
- DLLNames.push_back(make<StringChunk>(Syms[0]->getDLLName()));
- auto *Dir = make<ImportDirectoryChunk>(DLLNames.back());
- Dir->LookupTab = Lookups[Base];
- Dir->AddressTab = Addresses[Base];
- Dirs.push_back(Dir);
+ dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));
+ auto *dir = make<ImportDirectoryChunk>(dllNames.back());
+ dir->lookupTab = lookups[base];
+ dir->addressTab = addresses[base];
+ dirs.push_back(dir);
}
// Add null terminator.
- Dirs.push_back(make<NullChunk>(sizeof(ImportDirectoryTableEntry)));
+ dirs.push_back(make<NullChunk>(sizeof(ImportDirectoryTableEntry)));
}
std::vector<Chunk *> DelayLoadContents::getChunks() {
- std::vector<Chunk *> V;
- V.insert(V.end(), Dirs.begin(), Dirs.end());
- V.insert(V.end(), Names.begin(), Names.end());
- V.insert(V.end(), HintNames.begin(), HintNames.end());
- V.insert(V.end(), DLLNames.begin(), DLLNames.end());
- return V;
+ std::vector<Chunk *> v;
+ v.insert(v.end(), dirs.begin(), dirs.end());
+ v.insert(v.end(), names.begin(), names.end());
+ v.insert(v.end(), hintNames.begin(), hintNames.end());
+ v.insert(v.end(), dllNames.begin(), dllNames.end());
+ return v;
}
std::vector<Chunk *> DelayLoadContents::getDataChunks() {
- std::vector<Chunk *> V;
- V.insert(V.end(), ModuleHandles.begin(), ModuleHandles.end());
- V.insert(V.end(), Addresses.begin(), Addresses.end());
- return V;
+ std::vector<Chunk *> v;
+ v.insert(v.end(), moduleHandles.begin(), moduleHandles.end());
+ v.insert(v.end(), addresses.begin(), addresses.end());
+ return v;
}
uint64_t DelayLoadContents::getDirSize() {
- return Dirs.size() * sizeof(delay_import_directory_table_entry);
+ return dirs.size() * sizeof(delay_import_directory_table_entry);
}
-void DelayLoadContents::create(Defined *H) {
- Helper = H;
- std::vector<std::vector<DefinedImportData *>> V = binImports(Imports);
+void DelayLoadContents::create(Defined *h) {
+ helper = h;
+ std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
// Create .didat contents for each DLL.
- for (std::vector<DefinedImportData *> &Syms : V) {
+ for (std::vector<DefinedImportData *> &syms : v) {
// Create the delay import table header.
- DLLNames.push_back(make<StringChunk>(Syms[0]->getDLLName()));
- auto *Dir = make<DelayDirectoryChunk>(DLLNames.back());
-
- size_t Base = Addresses.size();
- for (DefinedImportData *S : Syms) {
- Chunk *T = newThunkChunk(S, Dir);
- auto *A = make<DelayAddressChunk>(T);
- Addresses.push_back(A);
- Thunks.push_back(T);
- StringRef ExtName = S->getExternalName();
- if (ExtName.empty()) {
- Names.push_back(make<OrdinalOnlyChunk>(S->getOrdinal()));
+ dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));
+ auto *dir = make<DelayDirectoryChunk>(dllNames.back());
+
+ size_t base = addresses.size();
+ Chunk *tm = newTailMergeChunk(dir);
+ for (DefinedImportData *s : syms) {
+ Chunk *t = newThunkChunk(s, tm);
+ auto *a = make<DelayAddressChunk>(t);
+ addresses.push_back(a);
+ thunks.push_back(t);
+ StringRef extName = s->getExternalName();
+ if (extName.empty()) {
+ names.push_back(make<OrdinalOnlyChunk>(s->getOrdinal()));
} else {
- auto *C = make<HintNameChunk>(ExtName, 0);
- Names.push_back(make<LookupChunk>(C));
- HintNames.push_back(C);
+ auto *c = make<HintNameChunk>(extName, 0);
+ names.push_back(make<LookupChunk>(c));
+ hintNames.push_back(c);
}
}
+ thunks.push_back(tm);
// Terminate with null values.
- Addresses.push_back(make<NullChunk>(8));
- Names.push_back(make<NullChunk>(8));
+ addresses.push_back(make<NullChunk>(8));
+ names.push_back(make<NullChunk>(8));
- for (int I = 0, E = Syms.size(); I < E; ++I)
- Syms[I]->setLocation(Addresses[Base + I]);
- auto *MH = make<NullChunk>(8);
- MH->Alignment = 8;
- ModuleHandles.push_back(MH);
+ for (int i = 0, e = syms.size(); i < e; ++i)
+ syms[i]->setLocation(addresses[base + i]);
+ auto *mh = make<NullChunk>(8);
+ mh->setAlignment(8);
+ moduleHandles.push_back(mh);
// Fill the delay import table header fields.
- Dir->ModuleHandle = MH;
- Dir->AddressTab = Addresses[Base];
- Dir->NameTab = Names[Base];
- Dirs.push_back(Dir);
+ dir->moduleHandle = mh;
+ dir->addressTab = addresses[base];
+ dir->nameTab = names[base];
+ dirs.push_back(dir);
}
// Add null terminator.
- Dirs.push_back(make<NullChunk>(sizeof(delay_import_directory_table_entry)));
+ dirs.push_back(make<NullChunk>(sizeof(delay_import_directory_table_entry)));
+}
+
+Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
+ switch (config->machine) {
+ case AMD64:
+ return make<TailMergeChunkX64>(dir, helper);
+ case I386:
+ return make<TailMergeChunkX86>(dir, helper);
+ case ARMNT:
+ return make<TailMergeChunkARM>(dir, helper);
+ case ARM64:
+ return make<TailMergeChunkARM64>(dir, helper);
+ default:
+ llvm_unreachable("unsupported machine type");
+ }
}
-Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *S, Chunk *Dir) {
- switch (Config->Machine) {
+Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
+ Chunk *tailMerge) {
+ switch (config->machine) {
case AMD64:
- return make<ThunkChunkX64>(S, Dir, Helper);
+ return make<ThunkChunkX64>(s, tailMerge);
case I386:
- return make<ThunkChunkX86>(S, Dir, Helper);
+ return make<ThunkChunkX86>(s, tailMerge);
case ARMNT:
- return make<ThunkChunkARM>(S, Dir, Helper);
+ return make<ThunkChunkARM>(s, tailMerge);
case ARM64:
- return make<ThunkChunkARM64>(S, Dir, Helper);
+ return make<ThunkChunkARM64>(s, tailMerge);
default:
llvm_unreachable("unsupported machine type");
}
}
EdataContents::EdataContents() {
- uint16_t MaxOrdinal = 0;
- for (Export &E : Config->Exports)
- MaxOrdinal = std::max(MaxOrdinal, E.Ordinal);
-
- auto *DLLName = make<StringChunk>(sys::path::filename(Config->OutputFile));
- auto *AddressTab = make<AddressTableChunk>(MaxOrdinal);
- std::vector<Chunk *> Names;
- for (Export &E : Config->Exports)
- if (!E.Noname)
- Names.push_back(make<StringChunk>(E.ExportName));
-
- std::vector<Chunk *> Forwards;
- for (Export &E : Config->Exports) {
- if (E.ForwardTo.empty())
+ uint16_t maxOrdinal = 0;
+ for (Export &e : config->exports)
+ maxOrdinal = std::max(maxOrdinal, e.ordinal);
+
+ auto *dllName = make<StringChunk>(sys::path::filename(config->outputFile));
+ auto *addressTab = make<AddressTableChunk>(maxOrdinal);
+ std::vector<Chunk *> names;
+ for (Export &e : config->exports)
+ if (!e.noname)
+ names.push_back(make<StringChunk>(e.exportName));
+
+ std::vector<Chunk *> forwards;
+ for (Export &e : config->exports) {
+ if (e.forwardTo.empty())
continue;
- E.ForwardChunk = make<StringChunk>(E.ForwardTo);
- Forwards.push_back(E.ForwardChunk);
- }
-
- auto *NameTab = make<NamePointersChunk>(Names);
- auto *OrdinalTab = make<ExportOrdinalChunk>(Names.size());
- auto *Dir = make<ExportDirectoryChunk>(MaxOrdinal, Names.size(), DLLName,
- AddressTab, NameTab, OrdinalTab);
- Chunks.push_back(Dir);
- Chunks.push_back(DLLName);
- Chunks.push_back(AddressTab);
- Chunks.push_back(NameTab);
- Chunks.push_back(OrdinalTab);
- Chunks.insert(Chunks.end(), Names.begin(), Names.end());
- Chunks.insert(Chunks.end(), Forwards.begin(), Forwards.end());
+ e.forwardChunk = make<StringChunk>(e.forwardTo);
+ forwards.push_back(e.forwardChunk);
+ }
+
+ auto *nameTab = make<NamePointersChunk>(names);
+ auto *ordinalTab = make<ExportOrdinalChunk>(names.size());
+ auto *dir = make<ExportDirectoryChunk>(maxOrdinal, names.size(), dllName,
+ addressTab, nameTab, ordinalTab);
+ chunks.push_back(dir);
+ chunks.push_back(dllName);
+ chunks.push_back(addressTab);
+ chunks.push_back(nameTab);
+ chunks.push_back(ordinalTab);
+ chunks.insert(chunks.end(), names.begin(), names.end());
+ chunks.insert(chunks.end(), forwards.begin(), forwards.end());
}
} // namespace coff
diff --git a/COFF/DLL.h b/COFF/DLL.h
index a298271e2c0d..ce0ee01c4a3d 100644
--- a/COFF/DLL.h
+++ b/COFF/DLL.h
@@ -1,9 +1,8 @@
//===- DLL.h ----------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -22,45 +21,46 @@ namespace coff {
// call create() to populate the chunk vectors.
class IdataContents {
public:
- void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
- bool empty() { return Imports.empty(); }
+ void add(DefinedImportData *sym) { imports.push_back(sym); }
+ bool empty() { return imports.empty(); }
void create();
- std::vector<DefinedImportData *> Imports;
- std::vector<Chunk *> Dirs;
- std::vector<Chunk *> Lookups;
- std::vector<Chunk *> Addresses;
- std::vector<Chunk *> Hints;
- std::vector<Chunk *> DLLNames;
+ std::vector<DefinedImportData *> imports;
+ std::vector<Chunk *> dirs;
+ std::vector<Chunk *> lookups;
+ std::vector<Chunk *> addresses;
+ std::vector<Chunk *> hints;
+ std::vector<Chunk *> dllNames;
};
// Windows-specific.
// DelayLoadContents creates all chunks for the delay-load DLL import table.
class DelayLoadContents {
public:
- void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
- bool empty() { return Imports.empty(); }
- void create(Defined *Helper);
+ void add(DefinedImportData *sym) { imports.push_back(sym); }
+ bool empty() { return imports.empty(); }
+ void create(Defined *helper);
std::vector<Chunk *> getChunks();
std::vector<Chunk *> getDataChunks();
- ArrayRef<Chunk *> getCodeChunks() { return Thunks; }
+ ArrayRef<Chunk *> getCodeChunks() { return thunks; }
- uint64_t getDirRVA() { return Dirs[0]->getRVA(); }
+ uint64_t getDirRVA() { return dirs[0]->getRVA(); }
uint64_t getDirSize();
private:
- Chunk *newThunkChunk(DefinedImportData *S, Chunk *Dir);
+ Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge);
+ Chunk *newTailMergeChunk(Chunk *dir);
- Defined *Helper;
- std::vector<DefinedImportData *> Imports;
- std::vector<Chunk *> Dirs;
- std::vector<Chunk *> ModuleHandles;
- std::vector<Chunk *> Addresses;
- std::vector<Chunk *> Names;
- std::vector<Chunk *> HintNames;
- std::vector<Chunk *> Thunks;
- std::vector<Chunk *> DLLNames;
+ Defined *helper;
+ std::vector<DefinedImportData *> imports;
+ std::vector<Chunk *> dirs;
+ std::vector<Chunk *> moduleHandles;
+ std::vector<Chunk *> addresses;
+ std::vector<Chunk *> names;
+ std::vector<Chunk *> hintNames;
+ std::vector<Chunk *> thunks;
+ std::vector<Chunk *> dllNames;
};
// Windows-specific.
@@ -68,11 +68,11 @@ private:
class EdataContents {
public:
EdataContents();
- std::vector<Chunk *> Chunks;
+ std::vector<Chunk *> chunks;
- uint64_t getRVA() { return Chunks[0]->getRVA(); }
+ uint64_t getRVA() { return chunks[0]->getRVA(); }
uint64_t getSize() {
- return Chunks.back()->getRVA() + Chunks.back()->getSize() - getRVA();
+ return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA();
}
};
diff --git a/COFF/DebugTypes.cpp b/COFF/DebugTypes.cpp
new file mode 100644
index 000000000000..78c1c78b408d
--- /dev/null
+++ b/COFF/DebugTypes.cpp
@@ -0,0 +1,268 @@
+//===- DebugTypes.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DebugTypes.h"
+#include "Driver.h"
+#include "InputFiles.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/PDB/GenericError.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/Support/Path.h"
+
+using namespace lld;
+using namespace lld::coff;
+using namespace llvm;
+using namespace llvm::codeview;
+
+namespace {
+// The TypeServerSource class represents a PDB type server, a file referenced by
+// OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ
+// files, therefore there must be only once instance per OBJ lot. The file path
+// is discovered from the dependent OBJ's debug type stream. The
+// TypeServerSource object is then queued and loaded by the COFF Driver. The
+// debug type stream for such PDB files will be merged first in the final PDB,
+// before any dependent OBJ.
+class TypeServerSource : public TpiSource {
+public:
+ explicit TypeServerSource(MemoryBufferRef m, llvm::pdb::NativeSession *s)
+ : TpiSource(PDB, nullptr), session(s), mb(m) {}
+
+ // Queue a PDB type server for loading in the COFF Driver
+ static void enqueue(const ObjFile *dependentFile,
+ const TypeServer2Record &ts);
+
+ // Create an instance
+ static Expected<TypeServerSource *> getInstance(MemoryBufferRef m);
+
+ // Fetch the PDB instance loaded for a corresponding dependent OBJ.
+ static Expected<TypeServerSource *>
+ findFromFile(const ObjFile *dependentFile);
+
+ static std::map<std::string, std::pair<std::string, TypeServerSource *>>
+ instances;
+
+ // The interface to the PDB (if it was opened successfully)
+ std::unique_ptr<llvm::pdb::NativeSession> session;
+
+private:
+ MemoryBufferRef mb;
+};
+
+// This class represents the debug type stream of an OBJ file that depends on a
+// PDB type server (see TypeServerSource).
+class UseTypeServerSource : public TpiSource {
+public:
+ UseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts)
+ : TpiSource(UsingPDB, f), typeServerDependency(*ts) {}
+
+ // Information about the PDB type server dependency, that needs to be loaded
+ // in before merging this OBJ.
+ TypeServer2Record typeServerDependency;
+};
+
+// This class represents the debug type stream of a Microsoft precompiled
+// headers OBJ (PCH OBJ). This OBJ kind needs to be merged first in the output
+// PDB, before any other OBJs that depend on this. Note that only MSVC generate
+// such files, clang does not.
+class PrecompSource : public TpiSource {
+public:
+ PrecompSource(const ObjFile *f) : TpiSource(PCH, f) {}
+};
+
+// This class represents the debug type stream of an OBJ file that depends on a
+// Microsoft precompiled headers OBJ (see PrecompSource).
+class UsePrecompSource : public TpiSource {
+public:
+ UsePrecompSource(const ObjFile *f, const PrecompRecord *precomp)
+ : TpiSource(UsingPCH, f), precompDependency(*precomp) {}
+
+ // Information about the Precomp OBJ dependency, that needs to be loaded in
+ // before merging this OBJ.
+ PrecompRecord precompDependency;
+};
+} // namespace
+
+static std::vector<std::unique_ptr<TpiSource>> GC;
+
+TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) {
+ GC.push_back(std::unique_ptr<TpiSource>(this));
+}
+
+TpiSource *lld::coff::makeTpiSource(const ObjFile *f) {
+ return new TpiSource(TpiSource::Regular, f);
+}
+
+TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f,
+ const TypeServer2Record *ts) {
+ TypeServerSource::enqueue(f, *ts);
+ return new UseTypeServerSource(f, ts);
+}
+
+TpiSource *lld::coff::makePrecompSource(const ObjFile *f) {
+ return new PrecompSource(f);
+}
+
+TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f,
+ const PrecompRecord *precomp) {
+ return new UsePrecompSource(f, precomp);
+}
+
+namespace lld {
+namespace coff {
+template <>
+const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) {
+ assert(source->kind == TpiSource::UsingPCH);
+ return ((const UsePrecompSource *)source)->precompDependency;
+}
+
+template <>
+const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) {
+ assert(source->kind == TpiSource::UsingPDB);
+ return ((const UseTypeServerSource *)source)->typeServerDependency;
+}
+} // namespace coff
+} // namespace lld
+
+std::map<std::string, std::pair<std::string, TypeServerSource *>>
+ TypeServerSource::instances;
+
+// Make a PDB path assuming the PDB is in the same folder as the OBJ
+static std::string getPdbBaseName(const ObjFile *file, StringRef tSPath) {
+ StringRef localPath =
+ !file->parentName.empty() ? file->parentName : file->getName();
+ SmallString<128> path = sys::path::parent_path(localPath);
+
+ // Currently, type server PDBs are only created by MSVC cl, which only runs
+ // on Windows, so we can assume type server paths are Windows style.
+ sys::path::append(path, sys::path::filename(tSPath, sys::path::Style::windows));
+ return path.str();
+}
+
+// The casing of the PDB path stamped in the OBJ can differ from the actual path
+// on disk. With this, we ensure to always use lowercase as a key for the
+// PDBInputFile::Instances map, at least on Windows.
+static std::string normalizePdbPath(StringRef path) {
+#if defined(_WIN32)
+ return path.lower();
+#else // LINUX
+ return path;
+#endif
+}
+
+// If existing, return the actual PDB path on disk.
+static Optional<std::string> findPdbPath(StringRef pdbPath,
+ const ObjFile *dependentFile) {
+ // Ensure the file exists before anything else. In some cases, if the path
+ // points to a removable device, Driver::enqueuePath() would fail with an
+ // error (EAGAIN, "resource unavailable try again") which we want to skip
+ // silently.
+ if (llvm::sys::fs::exists(pdbPath))
+ return normalizePdbPath(pdbPath);
+ std::string ret = getPdbBaseName(dependentFile, pdbPath);
+ if (llvm::sys::fs::exists(ret))
+ return normalizePdbPath(ret);
+ return None;
+}
+
+// Fetch the PDB instance that was already loaded by the COFF Driver.
+Expected<TypeServerSource *>
+TypeServerSource::findFromFile(const ObjFile *dependentFile) {
+ const TypeServer2Record &ts =
+ retrieveDependencyInfo<TypeServer2Record>(dependentFile->debugTypesObj);
+
+ Optional<std::string> p = findPdbPath(ts.Name, dependentFile);
+ if (!p)
+ return createFileError(ts.Name, errorCodeToError(std::error_code(
+ ENOENT, std::generic_category())));
+
+ auto it = TypeServerSource::instances.find(*p);
+ // The PDB file exists on disk, at this point we expect it to have been
+ // inserted in the map by TypeServerSource::loadPDB()
+ assert(it != TypeServerSource::instances.end());
+
+ std::pair<std::string, TypeServerSource *> &pdb = it->second;
+
+ if (!pdb.second)
+ return createFileError(
+ *p, createStringError(inconvertibleErrorCode(), pdb.first.c_str()));
+
+ pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile();
+ pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream());
+
+ // Just because a file with a matching name was found doesn't mean it can be
+ // used. The GUID must match between the PDB header and the OBJ
+ // TypeServer2 record. The 'Age' is used by MSVC incremental compilation.
+ if (info.getGuid() != ts.getGuid())
+ return createFileError(
+ ts.Name,
+ make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
+
+ return pdb.second;
+}
+
+// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
+// moved here.
+Expected<llvm::pdb::NativeSession *>
+lld::coff::findTypeServerSource(const ObjFile *f) {
+ Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f);
+ if (!ts)
+ return ts.takeError();
+ return ts.get()->session.get();
+}
+
+// Queue a PDB type server for loading in the COFF Driver
+void TypeServerSource::enqueue(const ObjFile *dependentFile,
+ const TypeServer2Record &ts) {
+ // Start by finding where the PDB is located (either the record path or next
+ // to the OBJ file)
+ Optional<std::string> p = findPdbPath(ts.Name, dependentFile);
+ if (!p)
+ return;
+ auto it = TypeServerSource::instances.emplace(
+ *p, std::pair<std::string, TypeServerSource *>{});
+ if (!it.second)
+ return; // another OBJ already scheduled this PDB for load
+
+ driver->enqueuePath(*p, false);
+}
+
+// Create an instance of TypeServerSource or an error string if the PDB couldn't
+// be loaded. The error message will be displayed later, when the referring OBJ
+// will be merged in. NOTE - a PDB load failure is not a link error: some
+// debug info will simply be missing from the final PDB - that is the default
+// accepted behavior.
+void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) {
+ std::string path = normalizePdbPath(m.getBufferIdentifier());
+
+ Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m);
+ if (!ts)
+ TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr};
+ else
+ TypeServerSource::instances[path] = {{}, *ts};
+}
+
+Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) {
+ std::unique_ptr<llvm::pdb::IPDBSession> iSession;
+ Error err = pdb::NativeSession::createFromPdb(
+ MemoryBuffer::getMemBuffer(m, false), iSession);
+ if (err)
+ return std::move(err);
+
+ std::unique_ptr<llvm::pdb::NativeSession> session(
+ static_cast<pdb::NativeSession *>(iSession.release()));
+
+ pdb::PDBFile &pdbFile = session->getPDBFile();
+ Expected<pdb::InfoStream &> info = pdbFile.getPDBInfoStream();
+ // All PDB Files should have an Info stream.
+ if (!info)
+ return info.takeError();
+ return new TypeServerSource(m, session.release());
+}
diff --git a/COFF/DebugTypes.h b/COFF/DebugTypes.h
new file mode 100644
index 000000000000..e37c727232d0
--- /dev/null
+++ b/COFF/DebugTypes.h
@@ -0,0 +1,60 @@
+//===- DebugTypes.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_DEBUGTYPES_H
+#define LLD_COFF_DEBUGTYPES_H
+
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+namespace llvm {
+namespace codeview {
+class PrecompRecord;
+class TypeServer2Record;
+} // namespace codeview
+namespace pdb {
+class NativeSession;
+}
+} // namespace llvm
+
+namespace lld {
+namespace coff {
+
+class ObjFile;
+
+class TpiSource {
+public:
+ enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB };
+
+ TpiSource(TpiKind k, const ObjFile *f);
+ virtual ~TpiSource() {}
+
+ const TpiKind kind;
+ const ObjFile *file;
+};
+
+TpiSource *makeTpiSource(const ObjFile *f);
+TpiSource *makeUseTypeServerSource(const ObjFile *f,
+ const llvm::codeview::TypeServer2Record *ts);
+TpiSource *makePrecompSource(const ObjFile *f);
+TpiSource *makeUsePrecompSource(const ObjFile *f,
+ const llvm::codeview::PrecompRecord *precomp);
+
+void loadTypeServerSource(llvm::MemoryBufferRef m);
+
+// Temporary interface to get the dependency
+template <typename T> const T &retrieveDependencyInfo(const TpiSource *source);
+
+// Temporary interface until we move PDBLinker::maybeMergeTypeServerPDB here
+llvm::Expected<llvm::pdb::NativeSession *>
+findTypeServerSource(const ObjFile *f);
+
+} // namespace coff
+} // namespace lld
+
+#endif \ No newline at end of file
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index 2e4b1e6d3147..d7af50b9318f 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -1,14 +1,14 @@
//===- Driver.cpp ---------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Driver.h"
#include "Config.h"
+#include "DebugTypes.h"
#include "ICF.h"
#include "InputFiles.h"
#include "MarkLive.h"
@@ -19,7 +19,9 @@
#include "lld/Common/Args.h"
#include "lld/Common/Driver.h"
#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Filesystem.h"
#include "lld/Common/Memory.h"
+#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/Optional.h"
@@ -28,6 +30,7 @@
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/COFFModuleDefinition.h"
+#include "llvm/Object/WindowsMachineFlag.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
@@ -51,43 +54,68 @@ using llvm::sys::Process;
namespace lld {
namespace coff {
-static Timer InputFileTimer("Input File Reading", Timer::root());
+static Timer inputFileTimer("Input File Reading", Timer::root());
-Configuration *Config;
-LinkerDriver *Driver;
+Configuration *config;
+LinkerDriver *driver;
-bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
- errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
- errorHandler().ErrorOS = &Diag;
- errorHandler().ColorDiagnostics = Diag.has_colors();
- errorHandler().ErrorLimitExceededMsg =
+bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &diag) {
+ errorHandler().logName = args::getFilenameWithoutExe(args[0]);
+ errorHandler().errorOS = &diag;
+ errorHandler().colorDiagnostics = diag.has_colors();
+ errorHandler().errorLimitExceededMsg =
"too many errors emitted, stopping now"
" (use /errorlimit:0 to see all errors)";
- errorHandler().ExitEarly = CanExitEarly;
- Config = make<Configuration>();
+ errorHandler().exitEarly = canExitEarly;
+ config = make<Configuration>();
- Symtab = make<SymbolTable>();
+ symtab = make<SymbolTable>();
- Driver = make<LinkerDriver>();
- Driver->link(Args);
+ driver = make<LinkerDriver>();
+ driver->link(args);
// Call exit() if we can to avoid calling destructors.
- if (CanExitEarly)
+ if (canExitEarly)
exitLld(errorCount() ? 1 : 0);
freeArena();
- ObjFile::Instances.clear();
- ImportFile::Instances.clear();
- BitcodeFile::Instances.clear();
+ ObjFile::instances.clear();
+ ImportFile::instances.clear();
+ BitcodeFile::instances.clear();
+ memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances));
return !errorCount();
}
+// Parse options of the form "old;new".
+static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
+ unsigned id) {
+ auto *arg = args.getLastArg(id);
+ if (!arg)
+ return {"", ""};
+
+ StringRef s = arg->getValue();
+ std::pair<StringRef, StringRef> ret = s.split(';');
+ if (ret.second.empty())
+ error(arg->getSpelling() + " expects 'old;new' format, but got " + s);
+ return ret;
+}
+
// Drop directory components and replace extension with ".exe" or ".dll".
-static std::string getOutputPath(StringRef Path) {
- auto P = Path.find_last_of("\\/");
- StringRef S = (P == StringRef::npos) ? Path : Path.substr(P + 1);
- const char* E = Config->DLL ? ".dll" : ".exe";
- return (S.substr(0, S.rfind('.')) + E).str();
+static std::string getOutputPath(StringRef path) {
+ auto p = path.find_last_of("\\/");
+ StringRef s = (p == StringRef::npos) ? path : path.substr(p + 1);
+ const char* e = config->dll ? ".dll" : ".exe";
+ return (s.substr(0, s.rfind('.')) + e).str();
+}
+
+// Returns true if S matches /crtend.?\.o$/.
+static bool isCrtend(StringRef s) {
+ if (!s.endswith(".o"))
+ return false;
+ s = s.drop_back(2);
+ if (s.endswith("crtend"))
+ return true;
+ return !s.empty() && s.drop_back().endswith("crtend");
}
// ErrorOr is not default constructible, so it cannot be used as the type
@@ -95,347 +123,401 @@ static std::string getOutputPath(StringRef Path) {
// FIXME: We could open the file in createFutureForFile and avoid needing to
// return an error here, but for the moment that would cost us a file descriptor
// (a limited resource on Windows) for the duration that the future is pending.
-typedef std::pair<std::unique_ptr<MemoryBuffer>, std::error_code> MBErrPair;
+using MBErrPair = std::pair<std::unique_ptr<MemoryBuffer>, std::error_code>;
// Create a std::future that opens and maps a file using the best strategy for
// the host platform.
-static std::future<MBErrPair> createFutureForFile(std::string Path) {
+static std::future<MBErrPair> createFutureForFile(std::string path) {
#if _WIN32
// On Windows, file I/O is relatively slow so it is best to do this
// asynchronously.
- auto Strategy = std::launch::async;
+ auto strategy = std::launch::async;
#else
- auto Strategy = std::launch::deferred;
+ auto strategy = std::launch::deferred;
#endif
- return std::async(Strategy, [=]() {
- auto MBOrErr = MemoryBuffer::getFile(Path,
+ return std::async(strategy, [=]() {
+ auto mbOrErr = MemoryBuffer::getFile(path,
/*FileSize*/ -1,
/*RequiresNullTerminator*/ false);
- if (!MBOrErr)
- return MBErrPair{nullptr, MBOrErr.getError()};
- return MBErrPair{std::move(*MBOrErr), std::error_code()};
+ if (!mbOrErr)
+ return MBErrPair{nullptr, mbOrErr.getError()};
+ return MBErrPair{std::move(*mbOrErr), std::error_code()};
});
}
// Symbol names are mangled by prepending "_" on x86.
-static StringRef mangle(StringRef Sym) {
- assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN);
- if (Config->Machine == I386)
- return Saver.save("_" + Sym);
- return Sym;
+static StringRef mangle(StringRef sym) {
+ assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN);
+ if (config->machine == I386)
+ return saver.save("_" + sym);
+ return sym;
}
-static bool findUnderscoreMangle(StringRef Sym) {
- StringRef Entry = Symtab->findMangle(mangle(Sym));
- return !Entry.empty() && !isa<Undefined>(Symtab->find(Entry));
+static bool findUnderscoreMangle(StringRef sym) {
+ Symbol *s = symtab->findMangle(mangle(sym));
+ return s && !isa<Undefined>(s);
}
-MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) {
- MemoryBufferRef MBRef = *MB;
- make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take ownership
+MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
+ MemoryBufferRef mbref = *mb;
+ make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); // take ownership
- if (Driver->Tar)
- Driver->Tar->append(relativeToRoot(MBRef.getBufferIdentifier()),
- MBRef.getBuffer());
- return MBRef;
+ if (driver->tar)
+ driver->tar->append(relativeToRoot(mbref.getBufferIdentifier()),
+ mbref.getBuffer());
+ return mbref;
}
-void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB,
- bool WholeArchive) {
- StringRef Filename = MB->getBufferIdentifier();
+void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
+ bool wholeArchive) {
+ StringRef filename = mb->getBufferIdentifier();
- MemoryBufferRef MBRef = takeBuffer(std::move(MB));
- FilePaths.push_back(Filename);
+ MemoryBufferRef mbref = takeBuffer(std::move(mb));
+ filePaths.push_back(filename);
// File type is detected by contents, not by file extension.
- switch (identify_magic(MBRef.getBuffer())) {
+ switch (identify_magic(mbref.getBuffer())) {
case file_magic::windows_resource:
- Resources.push_back(MBRef);
+ resources.push_back(mbref);
break;
case file_magic::archive:
- if (WholeArchive) {
- std::unique_ptr<Archive> File =
- CHECK(Archive::create(MBRef), Filename + ": failed to parse archive");
+ if (wholeArchive) {
+ std::unique_ptr<Archive> file =
+ CHECK(Archive::create(mbref), filename + ": failed to parse archive");
- for (MemoryBufferRef M : getArchiveMembers(File.get()))
- addArchiveBuffer(M, "<whole-archive>", Filename);
+ for (MemoryBufferRef m : getArchiveMembers(file.get()))
+ addArchiveBuffer(m, "<whole-archive>", filename, 0);
return;
}
- Symtab->addFile(make<ArchiveFile>(MBRef));
+ symtab->addFile(make<ArchiveFile>(mbref));
break;
case file_magic::bitcode:
- Symtab->addFile(make<BitcodeFile>(MBRef));
+ symtab->addFile(make<BitcodeFile>(mbref, "", 0));
break;
case file_magic::coff_object:
case file_magic::coff_import_library:
- Symtab->addFile(make<ObjFile>(MBRef));
+ symtab->addFile(make<ObjFile>(mbref));
+ break;
+ case file_magic::pdb:
+ loadTypeServerSource(mbref);
break;
case file_magic::coff_cl_gl_object:
- error(Filename + ": is not a native COFF file. Recompile without /GL");
+ error(filename + ": is not a native COFF file. Recompile without /GL");
break;
case file_magic::pecoff_executable:
- if (Filename.endswith_lower(".dll")) {
- error(Filename + ": bad file type. Did you specify a DLL instead of an "
+ if (filename.endswith_lower(".dll")) {
+ error(filename + ": bad file type. Did you specify a DLL instead of an "
"import library?");
break;
}
LLVM_FALLTHROUGH;
default:
- error(MBRef.getBufferIdentifier() + ": unknown file type");
+ error(mbref.getBufferIdentifier() + ": unknown file type");
break;
}
}
-void LinkerDriver::enqueuePath(StringRef Path, bool WholeArchive) {
- auto Future =
- std::make_shared<std::future<MBErrPair>>(createFutureForFile(Path));
- std::string PathStr = Path;
+void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) {
+ auto future =
+ std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
+ std::string pathStr = path;
enqueueTask([=]() {
- auto MBOrErr = Future->get();
- if (MBOrErr.second)
- error("could not open " + PathStr + ": " + MBOrErr.second.message());
- else
- Driver->addBuffer(std::move(MBOrErr.first), WholeArchive);
+ auto mbOrErr = future->get();
+ if (mbOrErr.second) {
+ std::string msg =
+ "could not open '" + pathStr + "': " + mbOrErr.second.message();
+ // Check if the filename is a typo for an option flag. OptTable thinks
+ // that all args that are not known options and that start with / are
+ // filenames, but e.g. `/nodefaultlibs` is more likely a typo for
+ // the option `/nodefaultlib` than a reference to a file in the root
+ // directory.
+ std::string nearest;
+ if (COFFOptTable().findNearest(pathStr, nearest) > 1)
+ error(msg);
+ else
+ error(msg + "; did you mean '" + nearest + "'");
+ } else
+ driver->addBuffer(std::move(mbOrErr.first), wholeArchive);
});
}
-void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
- StringRef ParentName) {
- file_magic Magic = identify_magic(MB.getBuffer());
- if (Magic == file_magic::coff_import_library) {
- Symtab->addFile(make<ImportFile>(MB));
+void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName,
+ StringRef parentName,
+ uint64_t offsetInArchive) {
+ file_magic magic = identify_magic(mb.getBuffer());
+ if (magic == file_magic::coff_import_library) {
+ InputFile *imp = make<ImportFile>(mb);
+ imp->parentName = parentName;
+ symtab->addFile(imp);
return;
}
- InputFile *Obj;
- if (Magic == file_magic::coff_object) {
- Obj = make<ObjFile>(MB);
- } else if (Magic == file_magic::bitcode) {
- Obj = make<BitcodeFile>(MB);
+ InputFile *obj;
+ if (magic == file_magic::coff_object) {
+ obj = make<ObjFile>(mb);
+ } else if (magic == file_magic::bitcode) {
+ obj = make<BitcodeFile>(mb, parentName, offsetInArchive);
} else {
- error("unknown file type: " + MB.getBufferIdentifier());
+ error("unknown file type: " + mb.getBufferIdentifier());
return;
}
- Obj->ParentName = ParentName;
- Symtab->addFile(Obj);
- log("Loaded " + toString(Obj) + " for " + SymName);
+ obj->parentName = parentName;
+ symtab->addFile(obj);
+ log("Loaded " + toString(obj) + " for " + symName);
}
-void LinkerDriver::enqueueArchiveMember(const Archive::Child &C,
- StringRef SymName,
- StringRef ParentName) {
- if (!C.getParent()->isThin()) {
- MemoryBufferRef MB = CHECK(
- C.getMemoryBufferRef(),
- "could not get the buffer for the member defining symbol " + SymName);
- enqueueTask([=]() { Driver->addArchiveBuffer(MB, SymName, ParentName); });
+void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
+ StringRef symName,
+ StringRef parentName) {
+
+ auto reportBufferError = [=](Error &&e,
+ StringRef childName) {
+ fatal("could not get the buffer for the member defining symbol " +
+ symName + ": " + parentName + "(" + childName + "): " +
+ toString(std::move(e)));
+ };
+
+ if (!c.getParent()->isThin()) {
+ uint64_t offsetInArchive = c.getChildOffset();
+ Expected<MemoryBufferRef> mbOrErr = c.getMemoryBufferRef();
+ if (!mbOrErr)
+ reportBufferError(mbOrErr.takeError(), check(c.getFullName()));
+ MemoryBufferRef mb = mbOrErr.get();
+ enqueueTask([=]() {
+ driver->addArchiveBuffer(mb, symName, parentName, offsetInArchive);
+ });
return;
}
- auto Future = std::make_shared<std::future<MBErrPair>>(createFutureForFile(
- CHECK(C.getFullName(),
- "could not get the filename for the member defining symbol " +
- SymName)));
+ std::string childName = CHECK(
+ c.getFullName(),
+ "could not get the filename for the member defining symbol " +
+ symName);
+ auto future = std::make_shared<std::future<MBErrPair>>(
+ createFutureForFile(childName));
enqueueTask([=]() {
- auto MBOrErr = Future->get();
- if (MBOrErr.second)
- fatal("could not get the buffer for the member defining " + SymName +
- ": " + MBOrErr.second.message());
- Driver->addArchiveBuffer(takeBuffer(std::move(MBOrErr.first)), SymName,
- ParentName);
+ auto mbOrErr = future->get();
+ if (mbOrErr.second)
+ reportBufferError(errorCodeToError(mbOrErr.second), childName);
+ driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), symName,
+ parentName, /* OffsetInArchive */ 0);
});
}
-static bool isDecorated(StringRef Sym) {
- return Sym.startswith("@") || Sym.contains("@@") || Sym.startswith("?") ||
- (!Config->MinGW && Sym.contains('@'));
+static bool isDecorated(StringRef sym) {
+ return sym.startswith("@") || sym.contains("@@") || sym.startswith("?") ||
+ (!config->mingw && sym.contains('@'));
}
// Parses .drectve section contents and returns a list of files
// specified by /defaultlib.
-void LinkerDriver::parseDirectives(StringRef S) {
- ArgParser Parser;
+void LinkerDriver::parseDirectives(InputFile *file) {
+ StringRef s = file->getDirectives();
+ if (s.empty())
+ return;
+
+ log("Directives: " + toString(file) + ": " + s);
+
+ ArgParser parser;
// .drectve is always tokenized using Windows shell rules.
// /EXPORT: option can appear too many times, processing in fastpath.
- opt::InputArgList Args;
- std::vector<StringRef> Exports;
- std::tie(Args, Exports) = Parser.parseDirectives(S);
+ opt::InputArgList args;
+ std::vector<StringRef> exports;
+ std::tie(args, exports) = parser.parseDirectives(s);
- for (StringRef E : Exports) {
+ for (StringRef e : exports) {
// If a common header file contains dllexported function
// declarations, many object files may end up with having the
// same /EXPORT options. In order to save cost of parsing them,
// we dedup them first.
- if (!DirectivesExports.insert(E).second)
+ if (!directivesExports.insert(e).second)
continue;
- Export Exp = parseExport(E);
- if (Config->Machine == I386 && Config->MinGW) {
- if (!isDecorated(Exp.Name))
- Exp.Name = Saver.save("_" + Exp.Name);
- if (!Exp.ExtName.empty() && !isDecorated(Exp.ExtName))
- Exp.ExtName = Saver.save("_" + Exp.ExtName);
+ Export exp = parseExport(e);
+ if (config->machine == I386 && config->mingw) {
+ if (!isDecorated(exp.name))
+ exp.name = saver.save("_" + exp.name);
+ if (!exp.extName.empty() && !isDecorated(exp.extName))
+ exp.extName = saver.save("_" + exp.extName);
}
- Exp.Directives = true;
- Config->Exports.push_back(Exp);
+ exp.directives = true;
+ config->exports.push_back(exp);
}
- for (auto *Arg : Args) {
- switch (Arg->getOption().getUnaliasedOption().getID()) {
+ for (auto *arg : args) {
+ switch (arg->getOption().getID()) {
case OPT_aligncomm:
- parseAligncomm(Arg->getValue());
+ parseAligncomm(arg->getValue());
break;
case OPT_alternatename:
- parseAlternateName(Arg->getValue());
+ parseAlternateName(arg->getValue());
break;
case OPT_defaultlib:
- if (Optional<StringRef> Path = findLib(Arg->getValue()))
- enqueuePath(*Path, false);
+ if (Optional<StringRef> path = findLib(arg->getValue()))
+ enqueuePath(*path, false);
break;
case OPT_entry:
- Config->Entry = addUndefined(mangle(Arg->getValue()));
+ config->entry = addUndefined(mangle(arg->getValue()));
break;
case OPT_failifmismatch:
- checkFailIfMismatch(Arg->getValue());
+ checkFailIfMismatch(arg->getValue(), file);
break;
case OPT_incl:
- addUndefined(Arg->getValue());
+ addUndefined(arg->getValue());
break;
case OPT_merge:
- parseMerge(Arg->getValue());
+ parseMerge(arg->getValue());
break;
case OPT_nodefaultlib:
- Config->NoDefaultLibs.insert(doFindLib(Arg->getValue()));
+ config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower());
break;
case OPT_section:
- parseSection(Arg->getValue());
+ parseSection(arg->getValue());
break;
case OPT_subsystem:
- parseSubsystem(Arg->getValue(), &Config->Subsystem,
- &Config->MajorOSVersion, &Config->MinorOSVersion);
+ parseSubsystem(arg->getValue(), &config->subsystem,
+ &config->majorOSVersion, &config->minorOSVersion);
break;
+ // Only add flags here that link.exe accepts in
+ // `#pragma comment(linker, "/flag")`-generated sections.
case OPT_editandcontinue:
- case OPT_fastfail:
case OPT_guardsym:
- case OPT_natvis:
case OPT_throwingnew:
break;
default:
- error(Arg->getSpelling() + " is not allowed in .drectve");
+ error(arg->getSpelling() + " is not allowed in .drectve");
}
}
}
// Find file from search paths. You can omit ".obj", this function takes
// care of that. Note that the returned path is not guaranteed to exist.
-StringRef LinkerDriver::doFindFile(StringRef Filename) {
- bool HasPathSep = (Filename.find_first_of("/\\") != StringRef::npos);
- if (HasPathSep)
- return Filename;
- bool HasExt = Filename.contains('.');
- for (StringRef Dir : SearchPaths) {
- SmallString<128> Path = Dir;
- sys::path::append(Path, Filename);
- if (sys::fs::exists(Path.str()))
- return Saver.save(Path.str());
- if (!HasExt) {
- Path.append(".obj");
- if (sys::fs::exists(Path.str()))
- return Saver.save(Path.str());
+StringRef LinkerDriver::doFindFile(StringRef filename) {
+ bool hasPathSep = (filename.find_first_of("/\\") != StringRef::npos);
+ if (hasPathSep)
+ return filename;
+ bool hasExt = filename.contains('.');
+ for (StringRef dir : searchPaths) {
+ SmallString<128> path = dir;
+ sys::path::append(path, filename);
+ if (sys::fs::exists(path.str()))
+ return saver.save(path.str());
+ if (!hasExt) {
+ path.append(".obj");
+ if (sys::fs::exists(path.str()))
+ return saver.save(path.str());
}
}
- return Filename;
+ return filename;
}
-static Optional<sys::fs::UniqueID> getUniqueID(StringRef Path) {
- sys::fs::UniqueID Ret;
- if (sys::fs::getUniqueID(Path, Ret))
+static Optional<sys::fs::UniqueID> getUniqueID(StringRef path) {
+ sys::fs::UniqueID ret;
+ if (sys::fs::getUniqueID(path, ret))
return None;
- return Ret;
+ return ret;
}
// Resolves a file path. This never returns the same path
// (in that case, it returns None).
-Optional<StringRef> LinkerDriver::findFile(StringRef Filename) {
- StringRef Path = doFindFile(Filename);
+Optional<StringRef> LinkerDriver::findFile(StringRef filename) {
+ StringRef path = doFindFile(filename);
- if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path)) {
- bool Seen = !VisitedFiles.insert(*ID).second;
- if (Seen)
+ if (Optional<sys::fs::UniqueID> id = getUniqueID(path)) {
+ bool seen = !visitedFiles.insert(*id).second;
+ if (seen)
return None;
}
- if (Path.endswith_lower(".lib"))
- VisitedLibs.insert(sys::path::filename(Path));
- return Path;
+ if (path.endswith_lower(".lib"))
+ visitedLibs.insert(sys::path::filename(path));
+ return path;
}
// MinGW specific. If an embedded directive specified to link to
// foo.lib, but it isn't found, try libfoo.a instead.
-StringRef LinkerDriver::doFindLibMinGW(StringRef Filename) {
- if (Filename.contains('/') || Filename.contains('\\'))
- return Filename;
-
- SmallString<128> S = Filename;
- sys::path::replace_extension(S, ".a");
- StringRef LibName = Saver.save("lib" + S.str());
- return doFindFile(LibName);
+StringRef LinkerDriver::doFindLibMinGW(StringRef filename) {
+ if (filename.contains('/') || filename.contains('\\'))
+ return filename;
+
+ SmallString<128> s = filename;
+ sys::path::replace_extension(s, ".a");
+ StringRef libName = saver.save("lib" + s.str());
+ return doFindFile(libName);
}
// Find library file from search path.
-StringRef LinkerDriver::doFindLib(StringRef Filename) {
+StringRef LinkerDriver::doFindLib(StringRef filename) {
// Add ".lib" to Filename if that has no file extension.
- bool HasExt = Filename.contains('.');
- if (!HasExt)
- Filename = Saver.save(Filename + ".lib");
- StringRef Ret = doFindFile(Filename);
+ bool hasExt = filename.contains('.');
+ if (!hasExt)
+ filename = saver.save(filename + ".lib");
+ StringRef ret = doFindFile(filename);
// For MinGW, if the find above didn't turn up anything, try
// looking for a MinGW formatted library name.
- if (Config->MinGW && Ret == Filename)
- return doFindLibMinGW(Filename);
- return Ret;
+ if (config->mingw && ret == filename)
+ return doFindLibMinGW(filename);
+ return ret;
}
// Resolves a library path. /nodefaultlib options are taken into
// consideration. This never returns the same path (in that case,
// it returns None).
-Optional<StringRef> LinkerDriver::findLib(StringRef Filename) {
- if (Config->NoDefaultLibAll)
+Optional<StringRef> LinkerDriver::findLib(StringRef filename) {
+ if (config->noDefaultLibAll)
return None;
- if (!VisitedLibs.insert(Filename.lower()).second)
+ if (!visitedLibs.insert(filename.lower()).second)
return None;
- StringRef Path = doFindLib(Filename);
- if (Config->NoDefaultLibs.count(Path))
+ StringRef path = doFindLib(filename);
+ if (config->noDefaultLibs.count(path.lower()))
return None;
- if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path))
- if (!VisitedFiles.insert(*ID).second)
+ if (Optional<sys::fs::UniqueID> id = getUniqueID(path))
+ if (!visitedFiles.insert(*id).second)
return None;
- return Path;
+ return path;
}
// Parses LIB environment which contains a list of search paths.
void LinkerDriver::addLibSearchPaths() {
- Optional<std::string> EnvOpt = Process::GetEnv("LIB");
- if (!EnvOpt.hasValue())
+ Optional<std::string> envOpt = Process::GetEnv("LIB");
+ if (!envOpt.hasValue())
return;
- StringRef Env = Saver.save(*EnvOpt);
- while (!Env.empty()) {
- StringRef Path;
- std::tie(Path, Env) = Env.split(';');
- SearchPaths.push_back(Path);
+ StringRef env = saver.save(*envOpt);
+ while (!env.empty()) {
+ StringRef path;
+ std::tie(path, env) = env.split(';');
+ searchPaths.push_back(path);
}
}
-Symbol *LinkerDriver::addUndefined(StringRef Name) {
- Symbol *B = Symtab->addUndefined(Name);
- if (!B->IsGCRoot) {
- B->IsGCRoot = true;
- Config->GCRoot.push_back(B);
+Symbol *LinkerDriver::addUndefined(StringRef name) {
+ Symbol *b = symtab->addUndefined(name);
+ if (!b->isGCRoot) {
+ b->isGCRoot = true;
+ config->gcroot.push_back(b);
}
- return B;
+ return b;
+}
+
+StringRef LinkerDriver::mangleMaybe(Symbol *s) {
+ // If the plain symbol name has already been resolved, do nothing.
+ Undefined *unmangled = dyn_cast<Undefined>(s);
+ if (!unmangled)
+ return "";
+
+ // Otherwise, see if a similar, mangled symbol exists in the symbol table.
+ Symbol *mangled = symtab->findMangle(unmangled->getName());
+ if (!mangled)
+ return "";
+
+ // If we find a similar mangled symbol, make this an alias to it and return
+ // its name.
+ log(unmangled->getName() + " aliased to " + mangled->getName());
+ unmangled->weakAlias = symtab->addUndefined(mangled->getName());
+ return mangled->getName();
}
// Windows specific -- find default entry point name.
@@ -444,15 +526,15 @@ Symbol *LinkerDriver::addUndefined(StringRef Name) {
// each of which corresponds to a user-defined "main" function. This function
// infers an entry point from a user-defined "main" function.
StringRef LinkerDriver::findDefaultEntry() {
- assert(Config->Subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
+ assert(config->subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
"must handle /subsystem before calling this");
- if (Config->MinGW)
- return mangle(Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
+ if (config->mingw)
+ return mangle(config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
? "WinMainCRTStartup"
: "mainCRTStartup");
- if (Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
+ if (config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
if (findUnderscoreMangle("wWinMain")) {
if (!findUnderscoreMangle("WinMain"))
return mangle("wWinMainCRTStartup");
@@ -469,44 +551,44 @@ StringRef LinkerDriver::findDefaultEntry() {
}
WindowsSubsystem LinkerDriver::inferSubsystem() {
- if (Config->DLL)
+ if (config->dll)
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
- if (Config->MinGW)
+ if (config->mingw)
return IMAGE_SUBSYSTEM_WINDOWS_CUI;
// Note that link.exe infers the subsystem from the presence of these
// functions even if /entry: or /nodefaultlib are passed which causes them
// to not be called.
- bool HaveMain = findUnderscoreMangle("main");
- bool HaveWMain = findUnderscoreMangle("wmain");
- bool HaveWinMain = findUnderscoreMangle("WinMain");
- bool HaveWWinMain = findUnderscoreMangle("wWinMain");
- if (HaveMain || HaveWMain) {
- if (HaveWinMain || HaveWWinMain) {
- warn(std::string("found ") + (HaveMain ? "main" : "wmain") + " and " +
- (HaveWinMain ? "WinMain" : "wWinMain") +
+ bool haveMain = findUnderscoreMangle("main");
+ bool haveWMain = findUnderscoreMangle("wmain");
+ bool haveWinMain = findUnderscoreMangle("WinMain");
+ bool haveWWinMain = findUnderscoreMangle("wWinMain");
+ if (haveMain || haveWMain) {
+ if (haveWinMain || haveWWinMain) {
+ warn(std::string("found ") + (haveMain ? "main" : "wmain") + " and " +
+ (haveWinMain ? "WinMain" : "wWinMain") +
"; defaulting to /subsystem:console");
}
return IMAGE_SUBSYSTEM_WINDOWS_CUI;
}
- if (HaveWinMain || HaveWWinMain)
+ if (haveWinMain || haveWWinMain)
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
return IMAGE_SUBSYSTEM_UNKNOWN;
}
static uint64_t getDefaultImageBase() {
- if (Config->is64())
- return Config->DLL ? 0x180000000 : 0x140000000;
- return Config->DLL ? 0x10000000 : 0x400000;
+ if (config->is64())
+ return config->dll ? 0x180000000 : 0x140000000;
+ return config->dll ? 0x10000000 : 0x400000;
}
-static std::string createResponseFile(const opt::InputArgList &Args,
- ArrayRef<StringRef> FilePaths,
- ArrayRef<StringRef> SearchPaths) {
- SmallString<0> Data;
- raw_svector_ostream OS(Data);
+static std::string createResponseFile(const opt::InputArgList &args,
+ ArrayRef<StringRef> filePaths,
+ ArrayRef<StringRef> searchPaths) {
+ SmallString<0> data;
+ raw_svector_ostream os(data);
- for (auto *Arg : Args) {
- switch (Arg->getOption().getID()) {
+ for (auto *arg : args) {
+ switch (arg->getOption().getID()) {
case OPT_linkrepro:
case OPT_INPUT:
case OPT_defaultlib:
@@ -518,32 +600,37 @@ static std::string createResponseFile(const opt::InputArgList &Args,
case OPT_manifestinput:
case OPT_manifestuac:
break;
+ case OPT_implib:
+ case OPT_pdb:
+ case OPT_out:
+ os << arg->getSpelling() << sys::path::filename(arg->getValue()) << "\n";
+ break;
default:
- OS << toString(*Arg) << "\n";
+ os << toString(*arg) << "\n";
}
}
- for (StringRef Path : SearchPaths) {
- std::string RelPath = relativeToRoot(Path);
- OS << "/libpath:" << quote(RelPath) << "\n";
+ for (StringRef path : searchPaths) {
+ std::string relPath = relativeToRoot(path);
+ os << "/libpath:" << quote(relPath) << "\n";
}
- for (StringRef Path : FilePaths)
- OS << quote(relativeToRoot(Path)) << "\n";
+ for (StringRef path : filePaths)
+ os << quote(relativeToRoot(path)) << "\n";
- return Data.str();
+ return data.str();
}
enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab };
-static DebugKind parseDebugKind(const opt::InputArgList &Args) {
- auto *A = Args.getLastArg(OPT_debug, OPT_debug_opt);
- if (!A)
+static DebugKind parseDebugKind(const opt::InputArgList &args) {
+ auto *a = args.getLastArg(OPT_debug, OPT_debug_opt);
+ if (!a)
return DebugKind::None;
- if (A->getNumValues() == 0)
+ if (a->getNumValues() == 0)
return DebugKind::Full;
- DebugKind Debug = StringSwitch<DebugKind>(A->getValue())
+ DebugKind debug = StringSwitch<DebugKind>(a->getValue())
.CaseLower("none", DebugKind::None)
.CaseLower("full", DebugKind::Full)
.CaseLower("fastlink", DebugKind::FastLink)
@@ -553,67 +640,68 @@ static DebugKind parseDebugKind(const opt::InputArgList &Args) {
.CaseLower("symtab", DebugKind::Symtab)
.Default(DebugKind::Unknown);
- if (Debug == DebugKind::FastLink) {
+ if (debug == DebugKind::FastLink) {
warn("/debug:fastlink unsupported; using /debug:full");
return DebugKind::Full;
}
- if (Debug == DebugKind::Unknown) {
- error("/debug: unknown option: " + Twine(A->getValue()));
+ if (debug == DebugKind::Unknown) {
+ error("/debug: unknown option: " + Twine(a->getValue()));
return DebugKind::None;
}
- return Debug;
+ return debug;
}
-static unsigned parseDebugTypes(const opt::InputArgList &Args) {
- unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
+static unsigned parseDebugTypes(const opt::InputArgList &args) {
+ unsigned debugTypes = static_cast<unsigned>(DebugType::None);
- if (auto *A = Args.getLastArg(OPT_debugtype)) {
- SmallVector<StringRef, 3> Types;
- A->getSpelling().split(Types, ',', /*KeepEmpty=*/false);
+ if (auto *a = args.getLastArg(OPT_debugtype)) {
+ SmallVector<StringRef, 3> types;
+ StringRef(a->getValue())
+ .split(types, ',', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
- for (StringRef Type : Types) {
- unsigned V = StringSwitch<unsigned>(Type.lower())
+ for (StringRef type : types) {
+ unsigned v = StringSwitch<unsigned>(type.lower())
.Case("cv", static_cast<unsigned>(DebugType::CV))
.Case("pdata", static_cast<unsigned>(DebugType::PData))
.Case("fixup", static_cast<unsigned>(DebugType::Fixup))
.Default(0);
- if (V == 0) {
- warn("/debugtype: unknown option: " + Twine(A->getValue()));
+ if (v == 0) {
+ warn("/debugtype: unknown option '" + type + "'");
continue;
}
- DebugTypes |= V;
+ debugTypes |= v;
}
- return DebugTypes;
+ return debugTypes;
}
// Default debug types
- DebugTypes = static_cast<unsigned>(DebugType::CV);
- if (Args.hasArg(OPT_driver))
- DebugTypes |= static_cast<unsigned>(DebugType::PData);
- if (Args.hasArg(OPT_profile))
- DebugTypes |= static_cast<unsigned>(DebugType::Fixup);
+ debugTypes = static_cast<unsigned>(DebugType::CV);
+ if (args.hasArg(OPT_driver))
+ debugTypes |= static_cast<unsigned>(DebugType::PData);
+ if (args.hasArg(OPT_profile))
+ debugTypes |= static_cast<unsigned>(DebugType::Fixup);
- return DebugTypes;
+ return debugTypes;
}
-static std::string getMapFile(const opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_lldmap, OPT_lldmap_file);
- if (!Arg)
+static std::string getMapFile(const opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file);
+ if (!arg)
return "";
- if (Arg->getOption().getID() == OPT_lldmap_file)
- return Arg->getValue();
+ if (arg->getOption().getID() == OPT_lldmap_file)
+ return arg->getValue();
- assert(Arg->getOption().getID() == OPT_lldmap);
- StringRef OutFile = Config->OutputFile;
- return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str();
+ assert(arg->getOption().getID() == OPT_lldmap);
+ StringRef outFile = config->outputFile;
+ return (outFile.substr(0, outFile.rfind('.')) + ".map").str();
}
static std::string getImplibPath() {
- if (!Config->Implib.empty())
- return Config->Implib;
- SmallString<128> Out = StringRef(Config->OutputFile);
- sys::path::replace_extension(Out, ".lib");
- return Out.str();
+ if (!config->implib.empty())
+ return config->implib;
+ SmallString<128> out = StringRef(config->outputFile);
+ sys::path::replace_extension(out, ".lib");
+ return out.str();
}
//
@@ -624,289 +712,346 @@ static std::string getImplibPath() {
// LINK | {value} | {value}.{.dll/.exe} | {output name}
// LIB | {value} | {value}.dll | {output name}.dll
//
-static std::string getImportName(bool AsLib) {
- SmallString<128> Out;
+static std::string getImportName(bool asLib) {
+ SmallString<128> out;
- if (Config->ImportName.empty()) {
- Out.assign(sys::path::filename(Config->OutputFile));
- if (AsLib)
- sys::path::replace_extension(Out, ".dll");
+ if (config->importName.empty()) {
+ out.assign(sys::path::filename(config->outputFile));
+ if (asLib)
+ sys::path::replace_extension(out, ".dll");
} else {
- Out.assign(Config->ImportName);
- if (!sys::path::has_extension(Out))
- sys::path::replace_extension(Out,
- (Config->DLL || AsLib) ? ".dll" : ".exe");
+ out.assign(config->importName);
+ if (!sys::path::has_extension(out))
+ sys::path::replace_extension(out,
+ (config->dll || asLib) ? ".dll" : ".exe");
}
- return Out.str();
+ return out.str();
}
-static void createImportLibrary(bool AsLib) {
- std::vector<COFFShortExport> Exports;
- for (Export &E1 : Config->Exports) {
- COFFShortExport E2;
- E2.Name = E1.Name;
- E2.SymbolName = E1.SymbolName;
- E2.ExtName = E1.ExtName;
- E2.Ordinal = E1.Ordinal;
- E2.Noname = E1.Noname;
- E2.Data = E1.Data;
- E2.Private = E1.Private;
- E2.Constant = E1.Constant;
- Exports.push_back(E2);
- }
-
- auto HandleError = [](Error &&E) {
- handleAllErrors(std::move(E),
- [](ErrorInfoBase &EIB) { error(EIB.message()); });
+static void createImportLibrary(bool asLib) {
+ std::vector<COFFShortExport> exports;
+ for (Export &e1 : config->exports) {
+ COFFShortExport e2;
+ e2.Name = e1.name;
+ e2.SymbolName = e1.symbolName;
+ e2.ExtName = e1.extName;
+ e2.Ordinal = e1.ordinal;
+ e2.Noname = e1.noname;
+ e2.Data = e1.data;
+ e2.Private = e1.isPrivate;
+ e2.Constant = e1.constant;
+ exports.push_back(e2);
+ }
+
+ auto handleError = [](Error &&e) {
+ handleAllErrors(std::move(e),
+ [](ErrorInfoBase &eib) { error(eib.message()); });
};
- std::string LibName = getImportName(AsLib);
- std::string Path = getImplibPath();
+ std::string libName = getImportName(asLib);
+ std::string path = getImplibPath();
- if (!Config->Incremental) {
- HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine,
- Config->MinGW));
+ if (!config->incremental) {
+ handleError(writeImportLibrary(libName, path, exports, config->machine,
+ config->mingw));
return;
}
// If the import library already exists, replace it only if the contents
// have changed.
- ErrorOr<std::unique_ptr<MemoryBuffer>> OldBuf = MemoryBuffer::getFile(
- Path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false);
- if (!OldBuf) {
- HandleError(writeImportLibrary(LibName, Path, Exports, Config->Machine,
- Config->MinGW));
+ ErrorOr<std::unique_ptr<MemoryBuffer>> oldBuf = MemoryBuffer::getFile(
+ path, /*FileSize*/ -1, /*RequiresNullTerminator*/ false);
+ if (!oldBuf) {
+ handleError(writeImportLibrary(libName, path, exports, config->machine,
+ config->mingw));
return;
}
- SmallString<128> TmpName;
- if (std::error_code EC =
- sys::fs::createUniqueFile(Path + ".tmp-%%%%%%%%.lib", TmpName))
- fatal("cannot create temporary file for import library " + Path + ": " +
- EC.message());
+ SmallString<128> tmpName;
+ if (std::error_code ec =
+ sys::fs::createUniqueFile(path + ".tmp-%%%%%%%%.lib", tmpName))
+ fatal("cannot create temporary file for import library " + path + ": " +
+ ec.message());
- if (Error E = writeImportLibrary(LibName, TmpName, Exports, Config->Machine,
- Config->MinGW)) {
- HandleError(std::move(E));
+ if (Error e = writeImportLibrary(libName, tmpName, exports, config->machine,
+ config->mingw)) {
+ handleError(std::move(e));
return;
}
- std::unique_ptr<MemoryBuffer> NewBuf = check(MemoryBuffer::getFile(
- TmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false));
- if ((*OldBuf)->getBuffer() != NewBuf->getBuffer()) {
- OldBuf->reset();
- HandleError(errorCodeToError(sys::fs::rename(TmpName, Path)));
+ std::unique_ptr<MemoryBuffer> newBuf = check(MemoryBuffer::getFile(
+ tmpName, /*FileSize*/ -1, /*RequiresNullTerminator*/ false));
+ if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) {
+ oldBuf->reset();
+ handleError(errorCodeToError(sys::fs::rename(tmpName, path)));
} else {
- sys::fs::remove(TmpName);
+ sys::fs::remove(tmpName);
}
}
-static void parseModuleDefs(StringRef Path) {
- std::unique_ptr<MemoryBuffer> MB = CHECK(
- MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
- COFFModuleDefinition M = check(parseCOFFModuleDefinition(
- MB->getMemBufferRef(), Config->Machine, Config->MinGW));
-
- if (Config->OutputFile.empty())
- Config->OutputFile = Saver.save(M.OutputFile);
- Config->ImportName = Saver.save(M.ImportName);
- if (M.ImageBase)
- Config->ImageBase = M.ImageBase;
- if (M.StackReserve)
- Config->StackReserve = M.StackReserve;
- if (M.StackCommit)
- Config->StackCommit = M.StackCommit;
- if (M.HeapReserve)
- Config->HeapReserve = M.HeapReserve;
- if (M.HeapCommit)
- Config->HeapCommit = M.HeapCommit;
- if (M.MajorImageVersion)
- Config->MajorImageVersion = M.MajorImageVersion;
- if (M.MinorImageVersion)
- Config->MinorImageVersion = M.MinorImageVersion;
- if (M.MajorOSVersion)
- Config->MajorOSVersion = M.MajorOSVersion;
- if (M.MinorOSVersion)
- Config->MinorOSVersion = M.MinorOSVersion;
-
- for (COFFShortExport E1 : M.Exports) {
- Export E2;
+static void parseModuleDefs(StringRef path) {
+ std::unique_ptr<MemoryBuffer> mb = CHECK(
+ MemoryBuffer::getFile(path, -1, false, true), "could not open " + path);
+ COFFModuleDefinition m = check(parseCOFFModuleDefinition(
+ mb->getMemBufferRef(), config->machine, config->mingw));
+
+ if (config->outputFile.empty())
+ config->outputFile = saver.save(m.OutputFile);
+ config->importName = saver.save(m.ImportName);
+ if (m.ImageBase)
+ config->imageBase = m.ImageBase;
+ if (m.StackReserve)
+ config->stackReserve = m.StackReserve;
+ if (m.StackCommit)
+ config->stackCommit = m.StackCommit;
+ if (m.HeapReserve)
+ config->heapReserve = m.HeapReserve;
+ if (m.HeapCommit)
+ config->heapCommit = m.HeapCommit;
+ if (m.MajorImageVersion)
+ config->majorImageVersion = m.MajorImageVersion;
+ if (m.MinorImageVersion)
+ config->minorImageVersion = m.MinorImageVersion;
+ if (m.MajorOSVersion)
+ config->majorOSVersion = m.MajorOSVersion;
+ if (m.MinorOSVersion)
+ config->minorOSVersion = m.MinorOSVersion;
+
+ for (COFFShortExport e1 : m.Exports) {
+ Export e2;
// In simple cases, only Name is set. Renamed exports are parsed
// and set as "ExtName = Name". If Name has the form "OtherDll.Func",
// it shouldn't be a normal exported function but a forward to another
// DLL instead. This is supported by both MS and GNU linkers.
- if (E1.ExtName != E1.Name && StringRef(E1.Name).contains('.')) {
- E2.Name = Saver.save(E1.ExtName);
- E2.ForwardTo = Saver.save(E1.Name);
- Config->Exports.push_back(E2);
+ if (e1.ExtName != e1.Name && StringRef(e1.Name).contains('.')) {
+ e2.name = saver.save(e1.ExtName);
+ e2.forwardTo = saver.save(e1.Name);
+ config->exports.push_back(e2);
continue;
}
- E2.Name = Saver.save(E1.Name);
- E2.ExtName = Saver.save(E1.ExtName);
- E2.Ordinal = E1.Ordinal;
- E2.Noname = E1.Noname;
- E2.Data = E1.Data;
- E2.Private = E1.Private;
- E2.Constant = E1.Constant;
- Config->Exports.push_back(E2);
+ e2.name = saver.save(e1.Name);
+ e2.extName = saver.save(e1.ExtName);
+ e2.ordinal = e1.Ordinal;
+ e2.noname = e1.Noname;
+ e2.data = e1.Data;
+ e2.isPrivate = e1.Private;
+ e2.constant = e1.Constant;
+ config->exports.push_back(e2);
}
}
-void LinkerDriver::enqueueTask(std::function<void()> Task) {
- TaskQueue.push_back(std::move(Task));
+void LinkerDriver::enqueueTask(std::function<void()> task) {
+ taskQueue.push_back(std::move(task));
}
bool LinkerDriver::run() {
- ScopedTimer T(InputFileTimer);
+ ScopedTimer t(inputFileTimer);
- bool DidWork = !TaskQueue.empty();
- while (!TaskQueue.empty()) {
- TaskQueue.front()();
- TaskQueue.pop_front();
+ bool didWork = !taskQueue.empty();
+ while (!taskQueue.empty()) {
+ taskQueue.front()();
+ taskQueue.pop_front();
}
- return DidWork;
+ return didWork;
}
// Parse an /order file. If an option is given, the linker places
// COMDAT sections in the same order as their names appear in the
// given file.
-static void parseOrderFile(StringRef Arg) {
+static void parseOrderFile(StringRef arg) {
// For some reason, the MSVC linker requires a filename to be
// preceded by "@".
- if (!Arg.startswith("@")) {
+ if (!arg.startswith("@")) {
error("malformed /order option: '@' missing");
return;
}
// Get a list of all comdat sections for error checking.
- DenseSet<StringRef> Set;
- for (Chunk *C : Symtab->getChunks())
- if (auto *Sec = dyn_cast<SectionChunk>(C))
- if (Sec->Sym)
- Set.insert(Sec->Sym->getName());
+ DenseSet<StringRef> set;
+ for (Chunk *c : symtab->getChunks())
+ if (auto *sec = dyn_cast<SectionChunk>(c))
+ if (sec->sym)
+ set.insert(sec->sym->getName());
// Open a file.
- StringRef Path = Arg.substr(1);
- std::unique_ptr<MemoryBuffer> MB = CHECK(
- MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
+ StringRef path = arg.substr(1);
+ std::unique_ptr<MemoryBuffer> mb = CHECK(
+ MemoryBuffer::getFile(path, -1, false, true), "could not open " + path);
// Parse a file. An order file contains one symbol per line.
// All symbols that were not present in a given order file are
// considered to have the lowest priority 0 and are placed at
// end of an output section.
- for (std::string S : args::getLines(MB->getMemBufferRef())) {
- if (Config->Machine == I386 && !isDecorated(S))
- S = "_" + S;
+ for (std::string s : args::getLines(mb->getMemBufferRef())) {
+ if (config->machine == I386 && !isDecorated(s))
+ s = "_" + s;
- if (Set.count(S) == 0) {
- if (Config->WarnMissingOrderSymbol)
- warn("/order:" + Arg + ": missing symbol: " + S + " [LNK4037]");
+ if (set.count(s) == 0) {
+ if (config->warnMissingOrderSymbol)
+ warn("/order:" + arg + ": missing symbol: " + s + " [LNK4037]");
}
else
- Config->Order[S] = INT_MIN + Config->Order.size();
+ config->order[s] = INT_MIN + config->order.size();
}
}
-static void markAddrsig(Symbol *S) {
- if (auto *D = dyn_cast_or_null<Defined>(S))
- if (Chunk *C = D->getChunk())
- C->KeepUnique = true;
+static void markAddrsig(Symbol *s) {
+ if (auto *d = dyn_cast_or_null<Defined>(s))
+ if (SectionChunk *c = dyn_cast_or_null<SectionChunk>(d->getChunk()))
+ c->keepUnique = true;
}
static void findKeepUniqueSections() {
// Exported symbols could be address-significant in other executables or DSOs,
// so we conservatively mark them as address-significant.
- for (Export &R : Config->Exports)
- markAddrsig(R.Sym);
+ for (Export &r : config->exports)
+ markAddrsig(r.sym);
// Visit the address-significance table in each object file and mark each
// referenced symbol as address-significant.
- for (ObjFile *Obj : ObjFile::Instances) {
- ArrayRef<Symbol *> Syms = Obj->getSymbols();
- if (Obj->AddrsigSec) {
- ArrayRef<uint8_t> Contents;
- Obj->getCOFFObj()->getSectionContents(Obj->AddrsigSec, Contents);
- const uint8_t *Cur = Contents.begin();
- while (Cur != Contents.end()) {
- unsigned Size;
- const char *Err;
- uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err);
- if (Err)
- fatal(toString(Obj) + ": could not decode addrsig section: " + Err);
- if (SymIndex >= Syms.size())
- fatal(toString(Obj) + ": invalid symbol index in addrsig section");
- markAddrsig(Syms[SymIndex]);
- Cur += Size;
+ for (ObjFile *obj : ObjFile::instances) {
+ ArrayRef<Symbol *> syms = obj->getSymbols();
+ if (obj->addrsigSec) {
+ ArrayRef<uint8_t> contents;
+ cantFail(
+ obj->getCOFFObj()->getSectionContents(obj->addrsigSec, contents));
+ const uint8_t *cur = contents.begin();
+ while (cur != contents.end()) {
+ unsigned size;
+ const char *err;
+ uint64_t symIndex = decodeULEB128(cur, &size, contents.end(), &err);
+ if (err)
+ fatal(toString(obj) + ": could not decode addrsig section: " + err);
+ if (symIndex >= syms.size())
+ fatal(toString(obj) + ": invalid symbol index in addrsig section");
+ markAddrsig(syms[symIndex]);
+ cur += size;
}
} else {
// If an object file does not have an address-significance table,
// conservatively mark all of its symbols as address-significant.
- for (Symbol *S : Syms)
- markAddrsig(S);
+ for (Symbol *s : syms)
+ markAddrsig(s);
}
}
}
-// link.exe replaces each %foo% in AltPath with the contents of environment
+// link.exe replaces each %foo% in altPath with the contents of environment
// variable foo, and adds the two magic env vars _PDB (expands to the basename
// of pdb's output path) and _EXT (expands to the extension of the output
// binary).
// lld only supports %_PDB% and %_EXT% and warns on references to all other env
// vars.
-static void parsePDBAltPath(StringRef AltPath) {
- SmallString<128> Buf;
- StringRef PDBBasename =
- sys::path::filename(Config->PDBPath, sys::path::Style::windows);
- StringRef BinaryExtension =
- sys::path::extension(Config->OutputFile, sys::path::Style::windows);
- if (!BinaryExtension.empty())
- BinaryExtension = BinaryExtension.substr(1); // %_EXT% does not include '.'.
+static void parsePDBAltPath(StringRef altPath) {
+ SmallString<128> buf;
+ StringRef pdbBasename =
+ sys::path::filename(config->pdbPath, sys::path::Style::windows);
+ StringRef binaryExtension =
+ sys::path::extension(config->outputFile, sys::path::Style::windows);
+ if (!binaryExtension.empty())
+ binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'.
// Invariant:
- // +--------- Cursor ('a...' might be the empty string).
- // | +----- FirstMark
- // | | +- SecondMark
+ // +--------- cursor ('a...' might be the empty string).
+ // | +----- firstMark
+ // | | +- secondMark
// v v v
// a...%...%...
- size_t Cursor = 0;
- while (Cursor < AltPath.size()) {
- size_t FirstMark, SecondMark;
- if ((FirstMark = AltPath.find('%', Cursor)) == StringRef::npos ||
- (SecondMark = AltPath.find('%', FirstMark + 1)) == StringRef::npos) {
+ size_t cursor = 0;
+ while (cursor < altPath.size()) {
+ size_t firstMark, secondMark;
+ if ((firstMark = altPath.find('%', cursor)) == StringRef::npos ||
+ (secondMark = altPath.find('%', firstMark + 1)) == StringRef::npos) {
// Didn't find another full fragment, treat rest of string as literal.
- Buf.append(AltPath.substr(Cursor));
+ buf.append(altPath.substr(cursor));
break;
}
// Found a full fragment. Append text in front of first %, and interpret
// text between first and second % as variable name.
- Buf.append(AltPath.substr(Cursor, FirstMark - Cursor));
- StringRef Var = AltPath.substr(FirstMark, SecondMark - FirstMark + 1);
- if (Var.equals_lower("%_pdb%"))
- Buf.append(PDBBasename);
- else if (Var.equals_lower("%_ext%"))
- Buf.append(BinaryExtension);
+ buf.append(altPath.substr(cursor, firstMark - cursor));
+ StringRef var = altPath.substr(firstMark, secondMark - firstMark + 1);
+ if (var.equals_lower("%_pdb%"))
+ buf.append(pdbBasename);
+ else if (var.equals_lower("%_ext%"))
+ buf.append(binaryExtension);
else {
warn("only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping " +
- Var + " as literal");
- Buf.append(Var);
+ var + " as literal");
+ buf.append(var);
}
- Cursor = SecondMark + 1;
+ cursor = secondMark + 1;
}
- Config->PDBAltPath = Buf;
+ config->pdbAltPath = buf;
}
-void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
- // If the first command line argument is "/lib", link.exe acts like lib.exe.
- // We call our own implementation of lib.exe that understands bitcode files.
- if (ArgsArr.size() > 1 && StringRef(ArgsArr[1]).equals_lower("/lib")) {
- if (llvm::libDriverMain(ArgsArr.slice(1)) != 0)
- fatal("lib failed");
+/// Check that at most one resource obj file was used.
+/// Call after ObjFile::Instances is complete.
+static void diagnoseMultipleResourceObjFiles() {
+ // The .rsrc$01 section in a resource obj file contains a tree description
+ // of resources. Merging multiple resource obj files would require merging
+ // the trees instead of using usual linker section merging semantics.
+ // Since link.exe disallows linking more than one resource obj file with
+ // LNK4078, mirror that. The normal use of resource files is to give the
+ // linker many .res files, which are then converted to a single resource obj
+ // file internally, so this is not a big restriction in practice.
+ ObjFile *resourceObjFile = nullptr;
+ for (ObjFile *f : ObjFile::instances) {
+ if (!f->isResourceObjFile)
+ continue;
+
+ if (!resourceObjFile) {
+ resourceObjFile = f;
+ continue;
+ }
+
+ error(toString(f) +
+ ": more than one resource obj file not allowed, already got " +
+ toString(resourceObjFile));
+ }
+}
+
+// In MinGW, if no symbols are chosen to be exported, then all symbols are
+// automatically exported by default. This behavior can be forced by the
+// -export-all-symbols option, so that it happens even when exports are
+// explicitly specified. The automatic behavior can be disabled using the
+// -exclude-all-symbols option, so that lld-link behaves like link.exe rather
+// than MinGW in the case that nothing is explicitly exported.
+void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
+ if (!config->dll)
return;
+
+ if (!args.hasArg(OPT_export_all_symbols)) {
+ if (!config->exports.empty())
+ return;
+ if (args.hasArg(OPT_exclude_all_symbols))
+ return;
}
+ AutoExporter exporter;
+
+ for (auto *arg : args.filtered(OPT_wholearchive_file))
+ if (Optional<StringRef> path = doFindFile(arg->getValue()))
+ exporter.addWholeArchive(*path);
+
+ symtab->forEachSymbol([&](Symbol *s) {
+ auto *def = dyn_cast<Defined>(s);
+ if (!exporter.shouldExport(def))
+ return;
+
+ Export e;
+ e.name = def->getName();
+ e.sym = def;
+ if (Chunk *c = def->getChunk())
+ if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
+ e.data = true;
+ config->exports.push_back(e);
+ });
+}
+
+void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// Needed for LTO.
InitializeAllTargetInfos();
InitializeAllTargets();
@@ -914,279 +1059,308 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
InitializeAllAsmParsers();
InitializeAllAsmPrinters();
+ // If the first command line argument is "/lib", link.exe acts like lib.exe.
+ // We call our own implementation of lib.exe that understands bitcode files.
+ if (argsArr.size() > 1 && StringRef(argsArr[1]).equals_lower("/lib")) {
+ if (llvm::libDriverMain(argsArr.slice(1)) != 0)
+ fatal("lib failed");
+ return;
+ }
+
// Parse command line options.
- ArgParser Parser;
- opt::InputArgList Args = Parser.parseLINK(ArgsArr);
+ ArgParser parser;
+ opt::InputArgList args = parser.parseLINK(argsArr);
// Parse and evaluate -mllvm options.
- std::vector<const char *> V;
- V.push_back("lld-link (LLVM option parsing)");
- for (auto *Arg : Args.filtered(OPT_mllvm))
- V.push_back(Arg->getValue());
- cl::ParseCommandLineOptions(V.size(), V.data());
+ std::vector<const char *> v;
+ v.push_back("lld-link (LLVM option parsing)");
+ for (auto *arg : args.filtered(OPT_mllvm))
+ v.push_back(arg->getValue());
+ cl::ParseCommandLineOptions(v.size(), v.data());
// Handle /errorlimit early, because error() depends on it.
- if (auto *Arg = Args.getLastArg(OPT_errorlimit)) {
- int N = 20;
- StringRef S = Arg->getValue();
- if (S.getAsInteger(10, N))
- error(Arg->getSpelling() + " number expected, but got " + S);
- errorHandler().ErrorLimit = N;
+ if (auto *arg = args.getLastArg(OPT_errorlimit)) {
+ int n = 20;
+ StringRef s = arg->getValue();
+ if (s.getAsInteger(10, n))
+ error(arg->getSpelling() + " number expected, but got " + s);
+ errorHandler().errorLimit = n;
}
// Handle /help
- if (Args.hasArg(OPT_help)) {
- printHelp(ArgsArr[0]);
+ if (args.hasArg(OPT_help)) {
+ printHelp(argsArr[0]);
return;
}
- if (Args.hasArg(OPT_show_timing))
- Config->ShowTiming = true;
+ lld::threadsEnabled = args.hasFlag(OPT_threads, OPT_threads_no, true);
+
+ if (args.hasArg(OPT_show_timing))
+ config->showTiming = true;
- ScopedTimer T(Timer::root());
+ config->showSummary = args.hasArg(OPT_summary);
+
+ ScopedTimer t(Timer::root());
// Handle --version, which is an lld extension. This option is a bit odd
// because it doesn't start with "/", but we deliberately chose "--" to
// avoid conflict with /version and for compatibility with clang-cl.
- if (Args.hasArg(OPT_dash_dash_version)) {
+ if (args.hasArg(OPT_dash_dash_version)) {
outs() << getLLDVersion() << "\n";
return;
}
// Handle /lldmingw early, since it can potentially affect how other
// options are handled.
- Config->MinGW = Args.hasArg(OPT_lldmingw);
+ config->mingw = args.hasArg(OPT_lldmingw);
- if (auto *Arg = Args.getLastArg(OPT_linkrepro)) {
- SmallString<64> Path = StringRef(Arg->getValue());
- sys::path::append(Path, "repro.tar");
+ if (auto *arg = args.getLastArg(OPT_linkrepro)) {
+ SmallString<64> path = StringRef(arg->getValue());
+ sys::path::append(path, "repro.tar");
- Expected<std::unique_ptr<TarWriter>> ErrOrWriter =
- TarWriter::create(Path, "repro");
+ Expected<std::unique_ptr<TarWriter>> errOrWriter =
+ TarWriter::create(path, "repro");
- if (ErrOrWriter) {
- Tar = std::move(*ErrOrWriter);
+ if (errOrWriter) {
+ tar = std::move(*errOrWriter);
} else {
- error("/linkrepro: failed to open " + Path + ": " +
- toString(ErrOrWriter.takeError()));
+ error("/linkrepro: failed to open " + path + ": " +
+ toString(errOrWriter.takeError()));
}
}
- if (!Args.hasArg(OPT_INPUT)) {
- if (Args.hasArg(OPT_deffile))
- Config->NoEntry = true;
+ if (!args.hasArg(OPT_INPUT)) {
+ if (args.hasArg(OPT_deffile))
+ config->noEntry = true;
else
fatal("no input files");
}
// Construct search path list.
- SearchPaths.push_back("");
- for (auto *Arg : Args.filtered(OPT_libpath))
- SearchPaths.push_back(Arg->getValue());
+ searchPaths.push_back("");
+ for (auto *arg : args.filtered(OPT_libpath))
+ searchPaths.push_back(arg->getValue());
addLibSearchPaths();
// Handle /ignore
- for (auto *Arg : Args.filtered(OPT_ignore)) {
- SmallVector<StringRef, 8> Vec;
- StringRef(Arg->getValue()).split(Vec, ',');
- for (StringRef S : Vec) {
- if (S == "4037")
- Config->WarnMissingOrderSymbol = false;
- else if (S == "4099")
- Config->WarnDebugInfoUnusable = false;
- else if (S == "4217")
- Config->WarnLocallyDefinedImported = false;
+ for (auto *arg : args.filtered(OPT_ignore)) {
+ SmallVector<StringRef, 8> vec;
+ StringRef(arg->getValue()).split(vec, ',');
+ for (StringRef s : vec) {
+ if (s == "4037")
+ config->warnMissingOrderSymbol = false;
+ else if (s == "4099")
+ config->warnDebugInfoUnusable = false;
+ else if (s == "4217")
+ config->warnLocallyDefinedImported = false;
// Other warning numbers are ignored.
}
}
// Handle /out
- if (auto *Arg = Args.getLastArg(OPT_out))
- Config->OutputFile = Arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_out))
+ config->outputFile = arg->getValue();
// Handle /verbose
- if (Args.hasArg(OPT_verbose))
- Config->Verbose = true;
- errorHandler().Verbose = Config->Verbose;
+ if (args.hasArg(OPT_verbose))
+ config->verbose = true;
+ errorHandler().verbose = config->verbose;
// Handle /force or /force:unresolved
- if (Args.hasArg(OPT_force, OPT_force_unresolved))
- Config->ForceUnresolved = true;
+ if (args.hasArg(OPT_force, OPT_force_unresolved))
+ config->forceUnresolved = true;
// Handle /force or /force:multiple
- if (Args.hasArg(OPT_force, OPT_force_multiple))
- Config->ForceMultiple = true;
+ if (args.hasArg(OPT_force, OPT_force_multiple))
+ config->forceMultiple = true;
+
+ // Handle /force or /force:multipleres
+ if (args.hasArg(OPT_force, OPT_force_multipleres))
+ config->forceMultipleRes = true;
// Handle /debug
- DebugKind Debug = parseDebugKind(Args);
- if (Debug == DebugKind::Full || Debug == DebugKind::Dwarf ||
- Debug == DebugKind::GHash) {
- Config->Debug = true;
- Config->Incremental = true;
+ DebugKind debug = parseDebugKind(args);
+ if (debug == DebugKind::Full || debug == DebugKind::Dwarf ||
+ debug == DebugKind::GHash) {
+ config->debug = true;
+ config->incremental = true;
}
+ // Handle /demangle
+ config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no);
+
// Handle /debugtype
- Config->DebugTypes = parseDebugTypes(Args);
+ config->debugTypes = parseDebugTypes(args);
// Handle /pdb
- bool ShouldCreatePDB =
- (Debug == DebugKind::Full || Debug == DebugKind::GHash);
- if (ShouldCreatePDB) {
- if (auto *Arg = Args.getLastArg(OPT_pdb))
- Config->PDBPath = Arg->getValue();
- if (auto *Arg = Args.getLastArg(OPT_pdbaltpath))
- Config->PDBAltPath = Arg->getValue();
- if (Args.hasArg(OPT_natvis))
- Config->NatvisFiles = Args.getAllArgValues(OPT_natvis);
+ bool shouldCreatePDB =
+ (debug == DebugKind::Full || debug == DebugKind::GHash);
+ if (shouldCreatePDB) {
+ if (auto *arg = args.getLastArg(OPT_pdb))
+ config->pdbPath = arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_pdbaltpath))
+ config->pdbAltPath = arg->getValue();
+ if (args.hasArg(OPT_natvis))
+ config->natvisFiles = args.getAllArgValues(OPT_natvis);
- if (auto *Arg = Args.getLastArg(OPT_pdb_source_path))
- Config->PDBSourcePath = Arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_pdb_source_path))
+ config->pdbSourcePath = arg->getValue();
}
// Handle /noentry
- if (Args.hasArg(OPT_noentry)) {
- if (Args.hasArg(OPT_dll))
- Config->NoEntry = true;
+ if (args.hasArg(OPT_noentry)) {
+ if (args.hasArg(OPT_dll))
+ config->noEntry = true;
else
error("/noentry must be specified with /dll");
}
// Handle /dll
- if (Args.hasArg(OPT_dll)) {
- Config->DLL = true;
- Config->ManifestID = 2;
+ if (args.hasArg(OPT_dll)) {
+ config->dll = true;
+ config->manifestID = 2;
}
// Handle /dynamicbase and /fixed. We can't use hasFlag for /dynamicbase
// because we need to explicitly check whether that option or its inverse was
// present in the argument list in order to handle /fixed.
- auto *DynamicBaseArg = Args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no);
- if (DynamicBaseArg &&
- DynamicBaseArg->getOption().getID() == OPT_dynamicbase_no)
- Config->DynamicBase = false;
+ auto *dynamicBaseArg = args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no);
+ if (dynamicBaseArg &&
+ dynamicBaseArg->getOption().getID() == OPT_dynamicbase_no)
+ config->dynamicBase = false;
// MSDN claims "/FIXED:NO is the default setting for a DLL, and /FIXED is the
// default setting for any other project type.", but link.exe defaults to
// /FIXED:NO for exe outputs as well. Match behavior, not docs.
- bool Fixed = Args.hasFlag(OPT_fixed, OPT_fixed_no, false);
- if (Fixed) {
- if (DynamicBaseArg &&
- DynamicBaseArg->getOption().getID() == OPT_dynamicbase) {
+ bool fixed = args.hasFlag(OPT_fixed, OPT_fixed_no, false);
+ if (fixed) {
+ if (dynamicBaseArg &&
+ dynamicBaseArg->getOption().getID() == OPT_dynamicbase) {
error("/fixed must not be specified with /dynamicbase");
} else {
- Config->Relocatable = false;
- Config->DynamicBase = false;
+ config->relocatable = false;
+ config->dynamicBase = false;
}
}
// Handle /appcontainer
- Config->AppContainer =
- Args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false);
+ config->appContainer =
+ args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false);
// Handle /machine
- if (auto *Arg = Args.getLastArg(OPT_machine))
- Config->Machine = getMachineType(Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_machine)) {
+ config->machine = getMachineType(arg->getValue());
+ if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN)
+ fatal(Twine("unknown /machine argument: ") + arg->getValue());
+ }
// Handle /nodefaultlib:<filename>
- for (auto *Arg : Args.filtered(OPT_nodefaultlib))
- Config->NoDefaultLibs.insert(doFindLib(Arg->getValue()));
+ for (auto *arg : args.filtered(OPT_nodefaultlib))
+ config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower());
// Handle /nodefaultlib
- if (Args.hasArg(OPT_nodefaultlib_all))
- Config->NoDefaultLibAll = true;
+ if (args.hasArg(OPT_nodefaultlib_all))
+ config->noDefaultLibAll = true;
// Handle /base
- if (auto *Arg = Args.getLastArg(OPT_base))
- parseNumbers(Arg->getValue(), &Config->ImageBase);
+ if (auto *arg = args.getLastArg(OPT_base))
+ parseNumbers(arg->getValue(), &config->imageBase);
+
+ // Handle /filealign
+ if (auto *arg = args.getLastArg(OPT_filealign)) {
+ parseNumbers(arg->getValue(), &config->fileAlign);
+ if (!isPowerOf2_64(config->fileAlign))
+ error("/filealign: not a power of two: " + Twine(config->fileAlign));
+ }
// Handle /stack
- if (auto *Arg = Args.getLastArg(OPT_stack))
- parseNumbers(Arg->getValue(), &Config->StackReserve, &Config->StackCommit);
+ if (auto *arg = args.getLastArg(OPT_stack))
+ parseNumbers(arg->getValue(), &config->stackReserve, &config->stackCommit);
// Handle /guard:cf
- if (auto *Arg = Args.getLastArg(OPT_guard))
- parseGuard(Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_guard))
+ parseGuard(arg->getValue());
// Handle /heap
- if (auto *Arg = Args.getLastArg(OPT_heap))
- parseNumbers(Arg->getValue(), &Config->HeapReserve, &Config->HeapCommit);
+ if (auto *arg = args.getLastArg(OPT_heap))
+ parseNumbers(arg->getValue(), &config->heapReserve, &config->heapCommit);
// Handle /version
- if (auto *Arg = Args.getLastArg(OPT_version))
- parseVersion(Arg->getValue(), &Config->MajorImageVersion,
- &Config->MinorImageVersion);
+ if (auto *arg = args.getLastArg(OPT_version))
+ parseVersion(arg->getValue(), &config->majorImageVersion,
+ &config->minorImageVersion);
// Handle /subsystem
- if (auto *Arg = Args.getLastArg(OPT_subsystem))
- parseSubsystem(Arg->getValue(), &Config->Subsystem, &Config->MajorOSVersion,
- &Config->MinorOSVersion);
+ if (auto *arg = args.getLastArg(OPT_subsystem))
+ parseSubsystem(arg->getValue(), &config->subsystem, &config->majorOSVersion,
+ &config->minorOSVersion);
// Handle /timestamp
- if (llvm::opt::Arg *Arg = Args.getLastArg(OPT_timestamp, OPT_repro)) {
- if (Arg->getOption().getID() == OPT_repro) {
- Config->Timestamp = 0;
- Config->Repro = true;
+ if (llvm::opt::Arg *arg = args.getLastArg(OPT_timestamp, OPT_repro)) {
+ if (arg->getOption().getID() == OPT_repro) {
+ config->timestamp = 0;
+ config->repro = true;
} else {
- Config->Repro = false;
- StringRef Value(Arg->getValue());
- if (Value.getAsInteger(0, Config->Timestamp))
- fatal(Twine("invalid timestamp: ") + Value +
+ config->repro = false;
+ StringRef value(arg->getValue());
+ if (value.getAsInteger(0, config->timestamp))
+ fatal(Twine("invalid timestamp: ") + value +
". Expected 32-bit integer");
}
} else {
- Config->Repro = false;
- Config->Timestamp = time(nullptr);
+ config->repro = false;
+ config->timestamp = time(nullptr);
}
// Handle /alternatename
- for (auto *Arg : Args.filtered(OPT_alternatename))
- parseAlternateName(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_alternatename))
+ parseAlternateName(arg->getValue());
// Handle /include
- for (auto *Arg : Args.filtered(OPT_incl))
- addUndefined(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_incl))
+ addUndefined(arg->getValue());
// Handle /implib
- if (auto *Arg = Args.getLastArg(OPT_implib))
- Config->Implib = Arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_implib))
+ config->implib = arg->getValue();
// Handle /opt.
- bool DoGC = Debug == DebugKind::None || Args.hasArg(OPT_profile);
- unsigned ICFLevel =
- Args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on
- unsigned TailMerge = 1;
- for (auto *Arg : Args.filtered(OPT_opt)) {
- std::string Str = StringRef(Arg->getValue()).lower();
- SmallVector<StringRef, 1> Vec;
- StringRef(Str).split(Vec, ',');
- for (StringRef S : Vec) {
- if (S == "ref") {
- DoGC = true;
- } else if (S == "noref") {
- DoGC = false;
- } else if (S == "icf" || S.startswith("icf=")) {
- ICFLevel = 2;
- } else if (S == "noicf") {
- ICFLevel = 0;
- } else if (S == "lldtailmerge") {
- TailMerge = 2;
- } else if (S == "nolldtailmerge") {
- TailMerge = 0;
- } else if (S.startswith("lldlto=")) {
- StringRef OptLevel = S.substr(7);
- if (OptLevel.getAsInteger(10, Config->LTOO) || Config->LTOO > 3)
- error("/opt:lldlto: invalid optimization level: " + OptLevel);
- } else if (S.startswith("lldltojobs=")) {
- StringRef Jobs = S.substr(11);
- if (Jobs.getAsInteger(10, Config->ThinLTOJobs) ||
- Config->ThinLTOJobs == 0)
- error("/opt:lldltojobs: invalid job count: " + Jobs);
- } else if (S.startswith("lldltopartitions=")) {
- StringRef N = S.substr(17);
- if (N.getAsInteger(10, Config->LTOPartitions) ||
- Config->LTOPartitions == 0)
- error("/opt:lldltopartitions: invalid partition count: " + N);
- } else if (S != "lbr" && S != "nolbr")
- error("/opt: unknown option: " + S);
+ bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile);
+ unsigned icfLevel =
+ args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on
+ unsigned tailMerge = 1;
+ for (auto *arg : args.filtered(OPT_opt)) {
+ std::string str = StringRef(arg->getValue()).lower();
+ SmallVector<StringRef, 1> vec;
+ StringRef(str).split(vec, ',');
+ for (StringRef s : vec) {
+ if (s == "ref") {
+ doGC = true;
+ } else if (s == "noref") {
+ doGC = false;
+ } else if (s == "icf" || s.startswith("icf=")) {
+ icfLevel = 2;
+ } else if (s == "noicf") {
+ icfLevel = 0;
+ } else if (s == "lldtailmerge") {
+ tailMerge = 2;
+ } else if (s == "nolldtailmerge") {
+ tailMerge = 0;
+ } else if (s.startswith("lldlto=")) {
+ StringRef optLevel = s.substr(7);
+ if (optLevel.getAsInteger(10, config->ltoo) || config->ltoo > 3)
+ error("/opt:lldlto: invalid optimization level: " + optLevel);
+ } else if (s.startswith("lldltojobs=")) {
+ StringRef jobs = s.substr(11);
+ if (jobs.getAsInteger(10, config->thinLTOJobs) ||
+ config->thinLTOJobs == 0)
+ error("/opt:lldltojobs: invalid job count: " + jobs);
+ } else if (s.startswith("lldltopartitions=")) {
+ StringRef n = s.substr(17);
+ if (n.getAsInteger(10, config->ltoPartitions) ||
+ config->ltoPartitions == 0)
+ error("/opt:lldltopartitions: invalid partition count: " + n);
+ } else if (s != "lbr" && s != "nolbr")
+ error("/opt: unknown option: " + s);
}
}
@@ -1195,37 +1369,37 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// FIXME: LLD only implements "limited" ICF, i.e. it only merges identical
// code. If the user passes /OPT:ICF explicitly, LLD should merge identical
// comdat readonly data.
- if (ICFLevel == 1 && !DoGC)
- ICFLevel = 0;
- Config->DoGC = DoGC;
- Config->DoICF = ICFLevel > 0;
- Config->TailMerge = (TailMerge == 1 && Config->DoICF) || TailMerge == 2;
+ if (icfLevel == 1 && !doGC)
+ icfLevel = 0;
+ config->doGC = doGC;
+ config->doICF = icfLevel > 0;
+ config->tailMerge = (tailMerge == 1 && config->doICF) || tailMerge == 2;
// Handle /lldsavetemps
- if (Args.hasArg(OPT_lldsavetemps))
- Config->SaveTemps = true;
+ if (args.hasArg(OPT_lldsavetemps))
+ config->saveTemps = true;
// Handle /kill-at
- if (Args.hasArg(OPT_kill_at))
- Config->KillAt = true;
+ if (args.hasArg(OPT_kill_at))
+ config->killAt = true;
// Handle /lldltocache
- if (auto *Arg = Args.getLastArg(OPT_lldltocache))
- Config->LTOCache = Arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_lldltocache))
+ config->ltoCache = arg->getValue();
// Handle /lldsavecachepolicy
- if (auto *Arg = Args.getLastArg(OPT_lldltocachepolicy))
- Config->LTOCachePolicy = CHECK(
- parseCachePruningPolicy(Arg->getValue()),
- Twine("/lldltocachepolicy: invalid cache policy: ") + Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_lldltocachepolicy))
+ config->ltoCachePolicy = CHECK(
+ parseCachePruningPolicy(arg->getValue()),
+ Twine("/lldltocachepolicy: invalid cache policy: ") + arg->getValue());
// Handle /failifmismatch
- for (auto *Arg : Args.filtered(OPT_failifmismatch))
- checkFailIfMismatch(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_failifmismatch))
+ checkFailIfMismatch(arg->getValue(), nullptr);
// Handle /merge
- for (auto *Arg : Args.filtered(OPT_merge))
- parseMerge(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_merge))
+ parseMerge(arg->getValue());
// Add default section merging rules after user rules. User rules take
// precedence, but we will emit a warning if there is a conflict.
@@ -1235,130 +1409,137 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
parseMerge(".xdata=.rdata");
parseMerge(".bss=.data");
- if (Config->MinGW) {
+ if (config->mingw) {
parseMerge(".ctors=.rdata");
parseMerge(".dtors=.rdata");
parseMerge(".CRT=.rdata");
}
// Handle /section
- for (auto *Arg : Args.filtered(OPT_section))
- parseSection(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_section))
+ parseSection(arg->getValue());
// Handle /aligncomm
- for (auto *Arg : Args.filtered(OPT_aligncomm))
- parseAligncomm(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_aligncomm))
+ parseAligncomm(arg->getValue());
// Handle /manifestdependency. This enables /manifest unless /manifest:no is
// also passed.
- if (auto *Arg = Args.getLastArg(OPT_manifestdependency)) {
- Config->ManifestDependency = Arg->getValue();
- Config->Manifest = Configuration::SideBySide;
+ if (auto *arg = args.getLastArg(OPT_manifestdependency)) {
+ config->manifestDependency = arg->getValue();
+ config->manifest = Configuration::SideBySide;
}
// Handle /manifest and /manifest:
- if (auto *Arg = Args.getLastArg(OPT_manifest, OPT_manifest_colon)) {
- if (Arg->getOption().getID() == OPT_manifest)
- Config->Manifest = Configuration::SideBySide;
+ if (auto *arg = args.getLastArg(OPT_manifest, OPT_manifest_colon)) {
+ if (arg->getOption().getID() == OPT_manifest)
+ config->manifest = Configuration::SideBySide;
else
- parseManifest(Arg->getValue());
+ parseManifest(arg->getValue());
}
// Handle /manifestuac
- if (auto *Arg = Args.getLastArg(OPT_manifestuac))
- parseManifestUAC(Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_manifestuac))
+ parseManifestUAC(arg->getValue());
// Handle /manifestfile
- if (auto *Arg = Args.getLastArg(OPT_manifestfile))
- Config->ManifestFile = Arg->getValue();
+ if (auto *arg = args.getLastArg(OPT_manifestfile))
+ config->manifestFile = arg->getValue();
// Handle /manifestinput
- for (auto *Arg : Args.filtered(OPT_manifestinput))
- Config->ManifestInput.push_back(Arg->getValue());
+ for (auto *arg : args.filtered(OPT_manifestinput))
+ config->manifestInput.push_back(arg->getValue());
- if (!Config->ManifestInput.empty() &&
- Config->Manifest != Configuration::Embed) {
+ if (!config->manifestInput.empty() &&
+ config->manifest != Configuration::Embed) {
fatal("/manifestinput: requires /manifest:embed");
}
+ config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files);
+ config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
+ args.hasArg(OPT_thinlto_index_only_arg);
+ config->thinLTOIndexOnlyArg =
+ args.getLastArgValue(OPT_thinlto_index_only_arg);
+ config->thinLTOPrefixReplace =
+ getOldNewOptions(args, OPT_thinlto_prefix_replace);
+ config->thinLTOObjectSuffixReplace =
+ getOldNewOptions(args, OPT_thinlto_object_suffix_replace);
// Handle miscellaneous boolean flags.
- Config->AllowBind = Args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
- Config->AllowIsolation =
- Args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true);
- Config->Incremental =
- Args.hasFlag(OPT_incremental, OPT_incremental_no,
- !Config->DoGC && !Config->DoICF && !Args.hasArg(OPT_order) &&
- !Args.hasArg(OPT_profile));
- Config->IntegrityCheck =
- Args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
- Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
- Config->TerminalServerAware =
- !Config->DLL && Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true);
- Config->DebugDwarf = Debug == DebugKind::Dwarf;
- Config->DebugGHashes = Debug == DebugKind::GHash;
- Config->DebugSymtab = Debug == DebugKind::Symtab;
-
- Config->MapFile = getMapFile(Args);
-
- if (Config->Incremental && Args.hasArg(OPT_profile)) {
+ config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
+ config->allowIsolation =
+ args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true);
+ config->incremental =
+ args.hasFlag(OPT_incremental, OPT_incremental_no,
+ !config->doGC && !config->doICF && !args.hasArg(OPT_order) &&
+ !args.hasArg(OPT_profile));
+ config->integrityCheck =
+ args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
+ config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
+ for (auto *arg : args.filtered(OPT_swaprun))
+ parseSwaprun(arg->getValue());
+ config->terminalServerAware =
+ !config->dll && args.hasFlag(OPT_tsaware, OPT_tsaware_no, true);
+ config->debugDwarf = debug == DebugKind::Dwarf;
+ config->debugGHashes = debug == DebugKind::GHash;
+ config->debugSymtab = debug == DebugKind::Symtab;
+
+ config->mapFile = getMapFile(args);
+
+ if (config->incremental && args.hasArg(OPT_profile)) {
warn("ignoring '/incremental' due to '/profile' specification");
- Config->Incremental = false;
+ config->incremental = false;
}
- if (Config->Incremental && Args.hasArg(OPT_order)) {
+ if (config->incremental && args.hasArg(OPT_order)) {
warn("ignoring '/incremental' due to '/order' specification");
- Config->Incremental = false;
+ config->incremental = false;
}
- if (Config->Incremental && Config->DoGC) {
+ if (config->incremental && config->doGC) {
warn("ignoring '/incremental' because REF is enabled; use '/opt:noref' to "
"disable");
- Config->Incremental = false;
+ config->incremental = false;
}
- if (Config->Incremental && Config->DoICF) {
+ if (config->incremental && config->doICF) {
warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to "
"disable");
- Config->Incremental = false;
+ config->incremental = false;
}
if (errorCount())
return;
- std::set<sys::fs::UniqueID> WholeArchives;
- AutoExporter Exporter;
- for (auto *Arg : Args.filtered(OPT_wholearchive_file)) {
- if (Optional<StringRef> Path = doFindFile(Arg->getValue())) {
- if (Optional<sys::fs::UniqueID> ID = getUniqueID(*Path))
- WholeArchives.insert(*ID);
- Exporter.addWholeArchive(*Path);
- }
- }
+ std::set<sys::fs::UniqueID> wholeArchives;
+ for (auto *arg : args.filtered(OPT_wholearchive_file))
+ if (Optional<StringRef> path = doFindFile(arg->getValue()))
+ if (Optional<sys::fs::UniqueID> id = getUniqueID(*path))
+ wholeArchives.insert(*id);
// A predicate returning true if a given path is an argument for
// /wholearchive:, or /wholearchive is enabled globally.
// This function is a bit tricky because "foo.obj /wholearchive:././foo.obj"
// needs to be handled as "/wholearchive:foo.obj foo.obj".
- auto IsWholeArchive = [&](StringRef Path) -> bool {
- if (Args.hasArg(OPT_wholearchive_flag))
+ auto isWholeArchive = [&](StringRef path) -> bool {
+ if (args.hasArg(OPT_wholearchive_flag))
return true;
- if (Optional<sys::fs::UniqueID> ID = getUniqueID(Path))
- return WholeArchives.count(*ID);
+ if (Optional<sys::fs::UniqueID> id = getUniqueID(path))
+ return wholeArchives.count(*id);
return false;
};
// Create a list of input files. Files can be given as arguments
// for /defaultlib option.
- for (auto *Arg : Args.filtered(OPT_INPUT, OPT_wholearchive_file))
- if (Optional<StringRef> Path = findFile(Arg->getValue()))
- enqueuePath(*Path, IsWholeArchive(*Path));
+ for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file))
+ if (Optional<StringRef> path = findFile(arg->getValue()))
+ enqueuePath(*path, isWholeArchive(*path));
- for (auto *Arg : Args.filtered(OPT_defaultlib))
- if (Optional<StringRef> Path = findLib(Arg->getValue()))
- enqueuePath(*Path, false);
+ for (auto *arg : args.filtered(OPT_defaultlib))
+ if (Optional<StringRef> path = findLib(arg->getValue()))
+ enqueuePath(*path, false);
// Windows specific -- Create a resource file containing a manifest file.
- if (Config->Manifest == Configuration::Embed)
+ if (config->manifest == Configuration::Embed)
addBuffer(createManifestRes(), false);
// Read all input files given via the command line.
@@ -1369,154 +1550,169 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// We should have inferred a machine type by now from the input files, but if
// not we assume x64.
- if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
+ if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
warn("/machine is not specified. x64 is assumed");
- Config->Machine = AMD64;
+ config->machine = AMD64;
}
- Config->Wordsize = Config->is64() ? 8 : 4;
+ config->wordsize = config->is64() ? 8 : 4;
+
+ // Handle /safeseh, x86 only, on by default, except for mingw.
+ if (config->machine == I386 &&
+ args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw))
+ config->safeSEH = true;
+
+ // Handle /functionpadmin
+ for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
+ parseFunctionPadMin(arg, config->machine);
// Input files can be Windows resource files (.res files). We use
// WindowsResource to convert resource files to a regular COFF file,
// then link the resulting file normally.
- if (!Resources.empty())
- Symtab->addFile(make<ObjFile>(convertResToCOFF(Resources)));
+ if (!resources.empty())
+ symtab->addFile(make<ObjFile>(convertResToCOFF(resources)));
- if (Tar)
- Tar->append("response.txt",
- createResponseFile(Args, FilePaths,
- ArrayRef<StringRef>(SearchPaths).slice(1)));
+ if (tar)
+ tar->append("response.txt",
+ createResponseFile(args, filePaths,
+ ArrayRef<StringRef>(searchPaths).slice(1)));
// Handle /largeaddressaware
- Config->LargeAddressAware = Args.hasFlag(
- OPT_largeaddressaware, OPT_largeaddressaware_no, Config->is64());
+ config->largeAddressAware = args.hasFlag(
+ OPT_largeaddressaware, OPT_largeaddressaware_no, config->is64());
// Handle /highentropyva
- Config->HighEntropyVA =
- Config->is64() &&
- Args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true);
+ config->highEntropyVA =
+ config->is64() &&
+ args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true);
- if (!Config->DynamicBase &&
- (Config->Machine == ARMNT || Config->Machine == ARM64))
+ if (!config->dynamicBase &&
+ (config->machine == ARMNT || config->machine == ARM64))
error("/dynamicbase:no is not compatible with " +
- machineToStr(Config->Machine));
+ machineToStr(config->machine));
// Handle /export
- for (auto *Arg : Args.filtered(OPT_export)) {
- Export E = parseExport(Arg->getValue());
- if (Config->Machine == I386) {
- if (!isDecorated(E.Name))
- E.Name = Saver.save("_" + E.Name);
- if (!E.ExtName.empty() && !isDecorated(E.ExtName))
- E.ExtName = Saver.save("_" + E.ExtName);
+ for (auto *arg : args.filtered(OPT_export)) {
+ Export e = parseExport(arg->getValue());
+ if (config->machine == I386) {
+ if (!isDecorated(e.name))
+ e.name = saver.save("_" + e.name);
+ if (!e.extName.empty() && !isDecorated(e.extName))
+ e.extName = saver.save("_" + e.extName);
}
- Config->Exports.push_back(E);
+ config->exports.push_back(e);
}
// Handle /def
- if (auto *Arg = Args.getLastArg(OPT_deffile)) {
+ if (auto *arg = args.getLastArg(OPT_deffile)) {
// parseModuleDefs mutates Config object.
- parseModuleDefs(Arg->getValue());
+ parseModuleDefs(arg->getValue());
}
// Handle generation of import library from a def file.
- if (!Args.hasArg(OPT_INPUT)) {
+ if (!args.hasArg(OPT_INPUT)) {
fixupExports();
- createImportLibrary(/*AsLib=*/true);
+ createImportLibrary(/*asLib=*/true);
return;
}
// Windows specific -- if no /subsystem is given, we need to infer
// that from entry point name. Must happen before /entry handling,
// and after the early return when just writing an import library.
- if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
- Config->Subsystem = inferSubsystem();
- if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
+ if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
+ config->subsystem = inferSubsystem();
+ if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
fatal("subsystem must be defined");
}
// Handle /entry and /dll
- if (auto *Arg = Args.getLastArg(OPT_entry)) {
- Config->Entry = addUndefined(mangle(Arg->getValue()));
- } else if (!Config->Entry && !Config->NoEntry) {
- if (Args.hasArg(OPT_dll)) {
- StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12"
+ if (auto *arg = args.getLastArg(OPT_entry)) {
+ config->entry = addUndefined(mangle(arg->getValue()));
+ } else if (!config->entry && !config->noEntry) {
+ if (args.hasArg(OPT_dll)) {
+ StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12"
: "_DllMainCRTStartup";
- Config->Entry = addUndefined(S);
+ config->entry = addUndefined(s);
} else {
// Windows specific -- If entry point name is not given, we need to
// infer that from user-defined entry name.
- StringRef S = findDefaultEntry();
- if (S.empty())
+ StringRef s = findDefaultEntry();
+ if (s.empty())
fatal("entry point must be defined");
- Config->Entry = addUndefined(S);
- log("Entry name inferred: " + S);
+ config->entry = addUndefined(s);
+ log("Entry name inferred: " + s);
}
}
// Handle /delayload
- for (auto *Arg : Args.filtered(OPT_delayload)) {
- Config->DelayLoads.insert(StringRef(Arg->getValue()).lower());
- if (Config->Machine == I386) {
- Config->DelayLoadHelper = addUndefined("___delayLoadHelper2@8");
+ for (auto *arg : args.filtered(OPT_delayload)) {
+ config->delayLoads.insert(StringRef(arg->getValue()).lower());
+ if (config->machine == I386) {
+ config->delayLoadHelper = addUndefined("___delayLoadHelper2@8");
} else {
- Config->DelayLoadHelper = addUndefined("__delayLoadHelper2");
+ config->delayLoadHelper = addUndefined("__delayLoadHelper2");
}
}
// Set default image name if neither /out or /def set it.
- if (Config->OutputFile.empty()) {
- Config->OutputFile =
- getOutputPath((*Args.filtered(OPT_INPUT).begin())->getValue());
+ if (config->outputFile.empty()) {
+ config->outputFile =
+ getOutputPath((*args.filtered(OPT_INPUT).begin())->getValue());
}
- if (ShouldCreatePDB) {
+ // Fail early if an output file is not writable.
+ if (auto e = tryCreateFile(config->outputFile)) {
+ error("cannot open output file " + config->outputFile + ": " + e.message());
+ return;
+ }
+
+ if (shouldCreatePDB) {
// Put the PDB next to the image if no /pdb flag was passed.
- if (Config->PDBPath.empty()) {
- Config->PDBPath = Config->OutputFile;
- sys::path::replace_extension(Config->PDBPath, ".pdb");
+ if (config->pdbPath.empty()) {
+ config->pdbPath = config->outputFile;
+ sys::path::replace_extension(config->pdbPath, ".pdb");
}
// The embedded PDB path should be the absolute path to the PDB if no
// /pdbaltpath flag was passed.
- if (Config->PDBAltPath.empty()) {
- Config->PDBAltPath = Config->PDBPath;
+ if (config->pdbAltPath.empty()) {
+ config->pdbAltPath = config->pdbPath;
// It's important to make the path absolute and remove dots. This path
// will eventually be written into the PE header, and certain Microsoft
// tools won't work correctly if these assumptions are not held.
- sys::fs::make_absolute(Config->PDBAltPath);
- sys::path::remove_dots(Config->PDBAltPath);
+ sys::fs::make_absolute(config->pdbAltPath);
+ sys::path::remove_dots(config->pdbAltPath);
} else {
// Don't do this earlier, so that Config->OutputFile is ready.
- parsePDBAltPath(Config->PDBAltPath);
+ parsePDBAltPath(config->pdbAltPath);
}
}
// Set default image base if /base is not given.
- if (Config->ImageBase == uint64_t(-1))
- Config->ImageBase = getDefaultImageBase();
-
- Symtab->addSynthetic(mangle("__ImageBase"), nullptr);
- if (Config->Machine == I386) {
- Symtab->addAbsolute("___safe_se_handler_table", 0);
- Symtab->addAbsolute("___safe_se_handler_count", 0);
- }
-
- Symtab->addAbsolute(mangle("__guard_fids_count"), 0);
- Symtab->addAbsolute(mangle("__guard_fids_table"), 0);
- Symtab->addAbsolute(mangle("__guard_flags"), 0);
- Symtab->addAbsolute(mangle("__guard_iat_count"), 0);
- Symtab->addAbsolute(mangle("__guard_iat_table"), 0);
- Symtab->addAbsolute(mangle("__guard_longjmp_count"), 0);
- Symtab->addAbsolute(mangle("__guard_longjmp_table"), 0);
+ if (config->imageBase == uint64_t(-1))
+ config->imageBase = getDefaultImageBase();
+
+ symtab->addSynthetic(mangle("__ImageBase"), nullptr);
+ if (config->machine == I386) {
+ symtab->addAbsolute("___safe_se_handler_table", 0);
+ symtab->addAbsolute("___safe_se_handler_count", 0);
+ }
+
+ symtab->addAbsolute(mangle("__guard_fids_count"), 0);
+ symtab->addAbsolute(mangle("__guard_fids_table"), 0);
+ symtab->addAbsolute(mangle("__guard_flags"), 0);
+ symtab->addAbsolute(mangle("__guard_iat_count"), 0);
+ symtab->addAbsolute(mangle("__guard_iat_table"), 0);
+ symtab->addAbsolute(mangle("__guard_longjmp_count"), 0);
+ symtab->addAbsolute(mangle("__guard_longjmp_table"), 0);
// Needed for MSVC 2017 15.5 CRT.
- Symtab->addAbsolute(mangle("__enclave_config"), 0);
+ symtab->addAbsolute(mangle("__enclave_config"), 0);
- if (Config->MinGW) {
- Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
- Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
- Symtab->addAbsolute(mangle("__CTOR_LIST__"), 0);
- Symtab->addAbsolute(mangle("__DTOR_LIST__"), 0);
+ if (config->mingw) {
+ symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
+ symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
+ symtab->addAbsolute(mangle("__CTOR_LIST__"), 0);
+ symtab->addAbsolute(mangle("__DTOR_LIST__"), 0);
}
// This code may add new undefined symbols to the link, which may enqueue more
@@ -1525,33 +1721,33 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
do {
// Windows specific -- if entry point is not found,
// search for its mangled names.
- if (Config->Entry)
- Symtab->mangleMaybe(Config->Entry);
+ if (config->entry)
+ mangleMaybe(config->entry);
// Windows specific -- Make sure we resolve all dllexported symbols.
- for (Export &E : Config->Exports) {
- if (!E.ForwardTo.empty())
+ for (Export &e : config->exports) {
+ if (!e.forwardTo.empty())
continue;
- E.Sym = addUndefined(E.Name);
- if (!E.Directives)
- Symtab->mangleMaybe(E.Sym);
+ e.sym = addUndefined(e.name);
+ if (!e.directives)
+ e.symbolName = mangleMaybe(e.sym);
}
// Add weak aliases. Weak aliases is a mechanism to give remaining
// undefined symbols final chance to be resolved successfully.
- for (auto Pair : Config->AlternateNames) {
- StringRef From = Pair.first;
- StringRef To = Pair.second;
- Symbol *Sym = Symtab->find(From);
- if (!Sym)
+ for (auto pair : config->alternateNames) {
+ StringRef from = pair.first;
+ StringRef to = pair.second;
+ Symbol *sym = symtab->find(from);
+ if (!sym)
continue;
- if (auto *U = dyn_cast<Undefined>(Sym))
- if (!U->WeakAlias)
- U->WeakAlias = Symtab->addUndefined(To);
+ if (auto *u = dyn_cast<Undefined>(sym))
+ if (!u->weakAlias)
+ u->weakAlias = symtab->addUndefined(to);
}
// Windows specific -- if __load_config_used can be resolved, resolve it.
- if (Symtab->findUnderscore("_load_config_used"))
+ if (symtab->findUnderscore("_load_config_used"))
addUndefined(mangle("_load_config_used"));
} while (run());
@@ -1559,11 +1755,29 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
return;
// Do LTO by compiling bitcode input files to a set of native COFF files then
- // link those files.
- Symtab->addCombinedLTOObjects();
+ // link those files (unless -thinlto-index-only was given, in which case we
+ // resolve symbols and write indices, but don't generate native code or link).
+ symtab->addCombinedLTOObjects();
+
+ // If -thinlto-index-only is given, we should create only "index
+ // files" and not object files. Index file creation is already done
+ // in addCombinedLTOObject, so we are done if that's the case.
+ if (config->thinLTOIndexOnly)
+ return;
+
+ // If we generated native object files from bitcode files, this resolves
+ // references to the symbols we use from them.
run();
- if (Config->MinGW) {
+ if (args.hasArg(OPT_include_optional)) {
+ // Handle /includeoptional
+ for (auto *arg : args.filtered(OPT_include_optional))
+ if (dyn_cast_or_null<Lazy>(symtab->find(arg->getValue())))
+ addUndefined(arg->getValue());
+ while (run());
+ }
+
+ if (config->mingw) {
// Load any further object files that might be needed for doing automatic
// imports.
//
@@ -1577,95 +1791,91 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// normal object file as well (although that won't be used for the
// actual autoimport later on). If this pass adds new undefined references,
// we won't iterate further to resolve them.
- Symtab->loadMinGWAutomaticImports();
+ symtab->loadMinGWAutomaticImports();
run();
}
// Make sure we have resolved all symbols.
- Symtab->reportRemainingUndefines();
+ symtab->reportRemainingUndefines();
if (errorCount())
return;
- // Handle /safeseh.
- if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) {
- for (ObjFile *File : ObjFile::Instances)
- if (!File->hasSafeSEH())
- error("/safeseh: " + File->getName() + " is not compatible with SEH");
- if (errorCount())
- return;
- }
-
- // In MinGW, all symbols are automatically exported if no symbols
- // are chosen to be exported.
- if (Config->DLL && ((Config->MinGW && Config->Exports.empty()) ||
- Args.hasArg(OPT_export_all_symbols))) {
- Exporter.initSymbolExcludes();
-
- Symtab->forEachSymbol([=](Symbol *S) {
- auto *Def = dyn_cast<Defined>(S);
- if (!Exporter.shouldExport(Def))
- return;
- Export E;
- E.Name = Def->getName();
- E.Sym = Def;
- if (Def->getChunk() &&
- !(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
- E.Data = true;
- Config->Exports.push_back(E);
- });
+ if (config->mingw) {
+ // In MinGW, all symbols are automatically exported if no symbols
+ // are chosen to be exported.
+ maybeExportMinGWSymbols(args);
+
+ // Make sure the crtend.o object is the last object file. This object
+ // file can contain terminating section chunks that need to be placed
+ // last. GNU ld processes files and static libraries explicitly in the
+ // order provided on the command line, while lld will pull in needed
+ // files from static libraries only after the last object file on the
+ // command line.
+ for (auto i = ObjFile::instances.begin(), e = ObjFile::instances.end();
+ i != e; i++) {
+ ObjFile *file = *i;
+ if (isCrtend(file->getName())) {
+ ObjFile::instances.erase(i);
+ ObjFile::instances.push_back(file);
+ break;
+ }
+ }
}
// Windows specific -- when we are creating a .dll file, we also
// need to create a .lib file.
- if (!Config->Exports.empty() || Config->DLL) {
+ if (!config->exports.empty() || config->dll) {
fixupExports();
- createImportLibrary(/*AsLib=*/false);
+ createImportLibrary(/*asLib=*/false);
assignExportOrdinals();
}
// Handle /output-def (MinGW specific).
- if (auto *Arg = Args.getLastArg(OPT_output_def))
- writeDefFile(Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_output_def))
+ writeDefFile(arg->getValue());
// Set extra alignment for .comm symbols
- for (auto Pair : Config->AlignComm) {
- StringRef Name = Pair.first;
- uint32_t Alignment = Pair.second;
+ for (auto pair : config->alignComm) {
+ StringRef name = pair.first;
+ uint32_t alignment = pair.second;
- Symbol *Sym = Symtab->find(Name);
- if (!Sym) {
- warn("/aligncomm symbol " + Name + " not found");
+ Symbol *sym = symtab->find(name);
+ if (!sym) {
+ warn("/aligncomm symbol " + name + " not found");
continue;
}
// If the symbol isn't common, it must have been replaced with a regular
// symbol, which will carry its own alignment.
- auto *DC = dyn_cast<DefinedCommon>(Sym);
- if (!DC)
+ auto *dc = dyn_cast<DefinedCommon>(sym);
+ if (!dc)
continue;
- CommonChunk *C = DC->getChunk();
- C->Alignment = std::max(C->Alignment, Alignment);
+ CommonChunk *c = dc->getChunk();
+ c->setAlignment(std::max(c->getAlignment(), alignment));
}
// Windows specific -- Create a side-by-side manifest file.
- if (Config->Manifest == Configuration::SideBySide)
+ if (config->manifest == Configuration::SideBySide)
createSideBySideManifest();
// Handle /order. We want to do this at this moment because we
// need a complete list of comdat sections to warn on nonexistent
// functions.
- if (auto *Arg = Args.getLastArg(OPT_order))
- parseOrderFile(Arg->getValue());
+ if (auto *arg = args.getLastArg(OPT_order))
+ parseOrderFile(arg->getValue());
// Identify unreferenced COMDAT sections.
- if (Config->DoGC)
- markLive(Symtab->getChunks());
+ if (config->doGC)
+ markLive(symtab->getChunks());
+
+ // Needs to happen after the last call to addFile().
+ diagnoseMultipleResourceObjFiles();
// Identify identical COMDAT sections to merge them.
- if (Config->DoICF) {
+ if (config->doICF) {
findKeepUniqueSections();
- doICF(Symtab->getChunks());
+ doICF(symtab->getChunks());
}
// Write the result.
@@ -1673,7 +1883,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Stop early so we can print the results.
Timer::root().stop();
- if (Config->ShowTiming)
+ if (config->showTiming)
Timer::root().print();
}
diff --git a/COFF/Driver.h b/COFF/Driver.h
index e779721ab75d..6100c3ca0c9e 100644
--- a/COFF/Driver.h
+++ b/COFF/Driver.h
@@ -1,9 +1,8 @@
//===- Driver.h -------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -31,7 +30,7 @@ namespace lld {
namespace coff {
class LinkerDriver;
-extern LinkerDriver *Driver;
+extern LinkerDriver *driver;
using llvm::COFF::MachineTypes;
using llvm::COFF::WindowsSubsystem;
@@ -45,65 +44,71 @@ public:
class ArgParser {
public:
// Concatenate LINK environment variable and given arguments and parse them.
- llvm::opt::InputArgList parseLINK(std::vector<const char *> Args);
+ llvm::opt::InputArgList parseLINK(std::vector<const char *> args);
// Tokenizes a given string and then parses as command line options.
- llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); }
+ llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); }
// Tokenizes a given string and then parses as command line options in
// .drectve section. /EXPORT options are returned in second element
// to be processed in fastpath.
std::pair<llvm::opt::InputArgList, std::vector<StringRef>>
- parseDirectives(StringRef S);
+ parseDirectives(StringRef s);
private:
// Parses command line options.
- llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args);
+ llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
- std::vector<const char *> tokenize(StringRef S);
+ std::vector<const char *> tokenize(StringRef s);
- COFFOptTable Table;
+ COFFOptTable table;
};
class LinkerDriver {
public:
- void link(llvm::ArrayRef<const char *> Args);
+ void link(llvm::ArrayRef<const char *> args);
// Used by the resolver to parse .drectve section contents.
- void parseDirectives(StringRef S);
+ void parseDirectives(InputFile *file);
// Used by ArchiveFile to enqueue members.
- void enqueueArchiveMember(const Archive::Child &C, StringRef SymName,
- StringRef ParentName);
+ void enqueueArchiveMember(const Archive::Child &c, StringRef symName,
+ StringRef parentName);
+
+ MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
- MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB);
+ void enqueuePath(StringRef path, bool wholeArchive);
private:
- std::unique_ptr<llvm::TarWriter> Tar; // for /linkrepro
+ std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
// Opens a file. Path has to be resolved already.
- MemoryBufferRef openFile(StringRef Path);
+ MemoryBufferRef openFile(StringRef path);
// Searches a file from search paths.
- Optional<StringRef> findFile(StringRef Filename);
- Optional<StringRef> findLib(StringRef Filename);
- StringRef doFindFile(StringRef Filename);
- StringRef doFindLib(StringRef Filename);
- StringRef doFindLibMinGW(StringRef Filename);
+ Optional<StringRef> findFile(StringRef filename);
+ Optional<StringRef> findLib(StringRef filename);
+ StringRef doFindFile(StringRef filename);
+ StringRef doFindLib(StringRef filename);
+ StringRef doFindLibMinGW(StringRef filename);
// Parses LIB environment which contains a list of search paths.
void addLibSearchPaths();
// Library search path. The first element is always "" (current directory).
- std::vector<StringRef> SearchPaths;
+ std::vector<StringRef> searchPaths;
+
+ void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args);
// We don't want to add the same file more than once.
// Files are uniquified by their filesystem and file number.
- std::set<llvm::sys::fs::UniqueID> VisitedFiles;
+ std::set<llvm::sys::fs::UniqueID> visitedFiles;
+
+ std::set<std::string> visitedLibs;
- std::set<std::string> VisitedLibs;
+ Symbol *addUndefined(StringRef sym);
- Symbol *addUndefined(StringRef Sym);
+ StringRef mangleMaybe(Symbol *s);
// Windows specific -- "main" is not the only main function in Windows.
// You can choose one from these four -- {w,}{WinMain,main}.
@@ -115,60 +120,60 @@ private:
StringRef findDefaultEntry();
WindowsSubsystem inferSubsystem();
- void addBuffer(std::unique_ptr<MemoryBuffer> MB, bool WholeArchive);
- void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName,
- StringRef ParentName);
+ void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive);
+ void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
+ StringRef parentName, uint64_t offsetInArchive);
- void enqueuePath(StringRef Path, bool WholeArchive);
-
- void enqueueTask(std::function<void()> Task);
+ void enqueueTask(std::function<void()> task);
bool run();
- std::list<std::function<void()>> TaskQueue;
- std::vector<StringRef> FilePaths;
- std::vector<MemoryBufferRef> Resources;
+ std::list<std::function<void()>> taskQueue;
+ std::vector<StringRef> filePaths;
+ std::vector<MemoryBufferRef> resources;
- llvm::StringSet<> DirectivesExports;
+ llvm::StringSet<> directivesExports;
};
// Functions below this line are defined in DriverUtils.cpp.
-void printHelp(const char *Argv0);
-
-// For /machine option.
-MachineTypes getMachineType(StringRef Arg);
-StringRef machineToStr(MachineTypes MT);
+void printHelp(const char *argv0);
// Parses a string in the form of "<integer>[,<integer>]".
-void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size = nullptr);
+void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr);
-void parseGuard(StringRef Arg);
+void parseGuard(StringRef arg);
// Parses a string in the form of "<integer>[.<integer>]".
// Minor's default value is 0.
-void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor);
+void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor);
// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
-void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
- uint32_t *Minor);
+void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
+ uint32_t *minor);
void parseAlternateName(StringRef);
void parseMerge(StringRef);
void parseSection(StringRef);
void parseAligncomm(StringRef);
+// Parses a string in the form of "[:<integer>]"
+void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine);
+
// Parses a string in the form of "EMBED[,=<integer>]|NO".
-void parseManifest(StringRef Arg);
+void parseManifest(StringRef arg);
// Parses a string in the form of "level=<string>|uiAccess=<string>"
-void parseManifestUAC(StringRef Arg);
+void parseManifestUAC(StringRef arg);
+
+// Parses a string in the form of "cd|net[,(cd|net)]*"
+void parseSwaprun(StringRef arg);
// Create a resource file containing a manifest XML.
std::unique_ptr<MemoryBuffer> createManifestRes();
void createSideBySideManifest();
// Used for dllexported symbols.
-Export parseExport(StringRef Arg);
+Export parseExport(StringRef arg);
void fixupExports();
void assignExportOrdinals();
@@ -176,12 +181,12 @@ void assignExportOrdinals();
// if value matches previous values for the key.
// This feature used in the directive section to reject
// incompatible objects.
-void checkFailIfMismatch(StringRef Arg);
+void checkFailIfMismatch(StringRef arg, InputFile *source);
// Convert Windows resource files (.res files) to a .obj file.
-MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs);
+MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs);
-void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects);
+void runMSVCLinker(std::string rsp, ArrayRef<StringRef> objects);
// Create enum with OPT_xxx values for each option in Options.td
enum {
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 3a11895497a4..4360ac23b262 100644
--- a/COFF/DriverUtils.cpp
+++ b/COFF/DriverUtils.cpp
@@ -1,9 +1,8 @@
//===- DriverUtils.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -48,104 +47,78 @@ const uint16_t RT_MANIFEST = 24;
class Executor {
public:
- explicit Executor(StringRef S) : Prog(Saver.save(S)) {}
- void add(StringRef S) { Args.push_back(Saver.save(S)); }
- void add(std::string &S) { Args.push_back(Saver.save(S)); }
- void add(Twine S) { Args.push_back(Saver.save(S)); }
- void add(const char *S) { Args.push_back(Saver.save(S)); }
+ explicit Executor(StringRef s) : prog(saver.save(s)) {}
+ void add(StringRef s) { args.push_back(saver.save(s)); }
+ void add(std::string &s) { args.push_back(saver.save(s)); }
+ void add(Twine s) { args.push_back(saver.save(s)); }
+ void add(const char *s) { args.push_back(saver.save(s)); }
void run() {
- ErrorOr<std::string> ExeOrErr = sys::findProgramByName(Prog);
- if (auto EC = ExeOrErr.getError())
- fatal("unable to find " + Prog + " in PATH: " + EC.message());
- StringRef Exe = Saver.save(*ExeOrErr);
- Args.insert(Args.begin(), Exe);
+ ErrorOr<std::string> exeOrErr = sys::findProgramByName(prog);
+ if (auto ec = exeOrErr.getError())
+ fatal("unable to find " + prog + " in PATH: " + ec.message());
+ StringRef exe = saver.save(*exeOrErr);
+ args.insert(args.begin(), exe);
- if (sys::ExecuteAndWait(Args[0], Args) != 0)
+ if (sys::ExecuteAndWait(args[0], args) != 0)
fatal("ExecuteAndWait failed: " +
- llvm::join(Args.begin(), Args.end(), " "));
+ llvm::join(args.begin(), args.end(), " "));
}
private:
- StringRef Prog;
- std::vector<StringRef> Args;
+ StringRef prog;
+ std::vector<StringRef> args;
};
} // anonymous namespace
-// Returns /machine's value.
-MachineTypes getMachineType(StringRef S) {
- MachineTypes MT = StringSwitch<MachineTypes>(S.lower())
- .Cases("x64", "amd64", AMD64)
- .Cases("x86", "i386", I386)
- .Case("arm", ARMNT)
- .Case("arm64", ARM64)
- .Default(IMAGE_FILE_MACHINE_UNKNOWN);
- if (MT != IMAGE_FILE_MACHINE_UNKNOWN)
- return MT;
- fatal("unknown /machine argument: " + S);
-}
-
-StringRef machineToStr(MachineTypes MT) {
- switch (MT) {
- case ARMNT:
- return "arm";
- case ARM64:
- return "arm64";
- case AMD64:
- return "x64";
- case I386:
- return "x86";
- default:
- llvm_unreachable("unknown machine type");
- }
-}
-
// Parses a string in the form of "<integer>[,<integer>]".
-void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size) {
- StringRef S1, S2;
- std::tie(S1, S2) = Arg.split(',');
- if (S1.getAsInteger(0, *Addr))
- fatal("invalid number: " + S1);
- if (Size && !S2.empty() && S2.getAsInteger(0, *Size))
- fatal("invalid number: " + S2);
+void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {
+ StringRef s1, s2;
+ std::tie(s1, s2) = arg.split(',');
+ if (s1.getAsInteger(0, *addr))
+ fatal("invalid number: " + s1);
+ if (size && !s2.empty() && s2.getAsInteger(0, *size))
+ fatal("invalid number: " + s2);
}
// Parses a string in the form of "<integer>[.<integer>]".
// If second number is not present, Minor is set to 0.
-void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor) {
- StringRef S1, S2;
- std::tie(S1, S2) = Arg.split('.');
- if (S1.getAsInteger(0, *Major))
- fatal("invalid number: " + S1);
- *Minor = 0;
- if (!S2.empty() && S2.getAsInteger(0, *Minor))
- fatal("invalid number: " + S2);
-}
-
-void parseGuard(StringRef FullArg) {
- SmallVector<StringRef, 1> SplitArgs;
- FullArg.split(SplitArgs, ",");
- for (StringRef Arg : SplitArgs) {
- if (Arg.equals_lower("no"))
- Config->GuardCF = GuardCFLevel::Off;
- else if (Arg.equals_lower("nolongjmp"))
- Config->GuardCF = GuardCFLevel::NoLongJmp;
- else if (Arg.equals_lower("cf") || Arg.equals_lower("longjmp"))
- Config->GuardCF = GuardCFLevel::Full;
+void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) {
+ StringRef s1, s2;
+ std::tie(s1, s2) = arg.split('.');
+ if (s1.getAsInteger(0, *major))
+ fatal("invalid number: " + s1);
+ *minor = 0;
+ if (!s2.empty() && s2.getAsInteger(0, *minor))
+ fatal("invalid number: " + s2);
+}
+
+void parseGuard(StringRef fullArg) {
+ SmallVector<StringRef, 1> splitArgs;
+ fullArg.split(splitArgs, ",");
+ for (StringRef arg : splitArgs) {
+ if (arg.equals_lower("no"))
+ config->guardCF = GuardCFLevel::Off;
+ else if (arg.equals_lower("nolongjmp"))
+ config->guardCF = GuardCFLevel::NoLongJmp;
+ else if (arg.equals_lower("cf") || arg.equals_lower("longjmp"))
+ config->guardCF = GuardCFLevel::Full;
else
- fatal("invalid argument to /guard: " + Arg);
+ fatal("invalid argument to /guard: " + arg);
}
}
// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
-void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
- uint32_t *Minor) {
- StringRef SysStr, Ver;
- std::tie(SysStr, Ver) = Arg.split(',');
- *Sys = StringSwitch<WindowsSubsystem>(SysStr.lower())
+void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
+ uint32_t *minor) {
+ StringRef sysStr, ver;
+ std::tie(sysStr, ver) = arg.split(',');
+ std::string sysStrLower = sysStr.lower();
+ *sys = StringSwitch<WindowsSubsystem>(sysStrLower)
.Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
.Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI)
+ .Case("default", IMAGE_SUBSYSTEM_UNKNOWN)
.Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION)
.Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER)
.Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM)
@@ -154,175 +127,217 @@ void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major,
.Case("posix", IMAGE_SUBSYSTEM_POSIX_CUI)
.Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI)
.Default(IMAGE_SUBSYSTEM_UNKNOWN);
- if (*Sys == IMAGE_SUBSYSTEM_UNKNOWN)
- fatal("unknown subsystem: " + SysStr);
- if (!Ver.empty())
- parseVersion(Ver, Major, Minor);
+ if (*sys == IMAGE_SUBSYSTEM_UNKNOWN && sysStrLower != "default")
+ fatal("unknown subsystem: " + sysStr);
+ if (!ver.empty())
+ parseVersion(ver, major, minor);
}
// Parse a string of the form of "<from>=<to>".
// Results are directly written to Config.
-void parseAlternateName(StringRef S) {
- StringRef From, To;
- std::tie(From, To) = S.split('=');
- if (From.empty() || To.empty())
- fatal("/alternatename: invalid argument: " + S);
- auto It = Config->AlternateNames.find(From);
- if (It != Config->AlternateNames.end() && It->second != To)
- fatal("/alternatename: conflicts: " + S);
- Config->AlternateNames.insert(It, std::make_pair(From, To));
+void parseAlternateName(StringRef s) {
+ StringRef from, to;
+ std::tie(from, to) = s.split('=');
+ if (from.empty() || to.empty())
+ fatal("/alternatename: invalid argument: " + s);
+ auto it = config->alternateNames.find(from);
+ if (it != config->alternateNames.end() && it->second != to)
+ fatal("/alternatename: conflicts: " + s);
+ config->alternateNames.insert(it, std::make_pair(from, to));
}
// Parse a string of the form of "<from>=<to>".
// Results are directly written to Config.
-void parseMerge(StringRef S) {
- StringRef From, To;
- std::tie(From, To) = S.split('=');
- if (From.empty() || To.empty())
- fatal("/merge: invalid argument: " + S);
- if (From == ".rsrc" || To == ".rsrc")
+void parseMerge(StringRef s) {
+ StringRef from, to;
+ std::tie(from, to) = s.split('=');
+ if (from.empty() || to.empty())
+ fatal("/merge: invalid argument: " + s);
+ if (from == ".rsrc" || to == ".rsrc")
fatal("/merge: cannot merge '.rsrc' with any section");
- if (From == ".reloc" || To == ".reloc")
+ if (from == ".reloc" || to == ".reloc")
fatal("/merge: cannot merge '.reloc' with any section");
- auto Pair = Config->Merge.insert(std::make_pair(From, To));
- bool Inserted = Pair.second;
- if (!Inserted) {
- StringRef Existing = Pair.first->second;
- if (Existing != To)
- warn(S + ": already merged into " + Existing);
+ auto pair = config->merge.insert(std::make_pair(from, to));
+ bool inserted = pair.second;
+ if (!inserted) {
+ StringRef existing = pair.first->second;
+ if (existing != to)
+ warn(s + ": already merged into " + existing);
}
}
-static uint32_t parseSectionAttributes(StringRef S) {
- uint32_t Ret = 0;
- for (char C : S.lower()) {
- switch (C) {
+static uint32_t parseSectionAttributes(StringRef s) {
+ uint32_t ret = 0;
+ for (char c : s.lower()) {
+ switch (c) {
case 'd':
- Ret |= IMAGE_SCN_MEM_DISCARDABLE;
+ ret |= IMAGE_SCN_MEM_DISCARDABLE;
break;
case 'e':
- Ret |= IMAGE_SCN_MEM_EXECUTE;
+ ret |= IMAGE_SCN_MEM_EXECUTE;
break;
case 'k':
- Ret |= IMAGE_SCN_MEM_NOT_CACHED;
+ ret |= IMAGE_SCN_MEM_NOT_CACHED;
break;
case 'p':
- Ret |= IMAGE_SCN_MEM_NOT_PAGED;
+ ret |= IMAGE_SCN_MEM_NOT_PAGED;
break;
case 'r':
- Ret |= IMAGE_SCN_MEM_READ;
+ ret |= IMAGE_SCN_MEM_READ;
break;
case 's':
- Ret |= IMAGE_SCN_MEM_SHARED;
+ ret |= IMAGE_SCN_MEM_SHARED;
break;
case 'w':
- Ret |= IMAGE_SCN_MEM_WRITE;
+ ret |= IMAGE_SCN_MEM_WRITE;
break;
default:
- fatal("/section: invalid argument: " + S);
+ fatal("/section: invalid argument: " + s);
}
}
- return Ret;
+ return ret;
}
// Parses /section option argument.
-void parseSection(StringRef S) {
- StringRef Name, Attrs;
- std::tie(Name, Attrs) = S.split(',');
- if (Name.empty() || Attrs.empty())
- fatal("/section: invalid argument: " + S);
- Config->Section[Name] = parseSectionAttributes(Attrs);
+void parseSection(StringRef s) {
+ StringRef name, attrs;
+ std::tie(name, attrs) = s.split(',');
+ if (name.empty() || attrs.empty())
+ fatal("/section: invalid argument: " + s);
+ config->section[name] = parseSectionAttributes(attrs);
}
// Parses /aligncomm option argument.
-void parseAligncomm(StringRef S) {
- StringRef Name, Align;
- std::tie(Name, Align) = S.split(',');
- if (Name.empty() || Align.empty()) {
- error("/aligncomm: invalid argument: " + S);
+void parseAligncomm(StringRef s) {
+ StringRef name, align;
+ std::tie(name, align) = s.split(',');
+ if (name.empty() || align.empty()) {
+ error("/aligncomm: invalid argument: " + s);
+ return;
+ }
+ int v;
+ if (align.getAsInteger(0, v)) {
+ error("/aligncomm: invalid argument: " + s);
return;
}
- int V;
- if (Align.getAsInteger(0, V)) {
- error("/aligncomm: invalid argument: " + S);
+ config->alignComm[name] = std::max(config->alignComm[name], 1 << v);
+}
+
+// Parses /functionpadmin option argument.
+void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) {
+ StringRef arg = a->getNumValues() ? a->getValue() : "";
+ if (!arg.empty()) {
+ // Optional padding in bytes is given.
+ if (arg.getAsInteger(0, config->functionPadMin))
+ error("/functionpadmin: invalid argument: " + arg);
return;
}
- Config->AlignComm[Name] = std::max(Config->AlignComm[Name], 1 << V);
+ // No optional argument given.
+ // Set default padding based on machine, similar to link.exe.
+ // There is no default padding for ARM platforms.
+ if (machine == I386) {
+ config->functionPadMin = 5;
+ } else if (machine == AMD64) {
+ config->functionPadMin = 6;
+ } else {
+ error("/functionpadmin: invalid argument for this machine: " + arg);
+ }
}
// Parses a string in the form of "EMBED[,=<integer>]|NO".
// Results are directly written to Config.
-void parseManifest(StringRef Arg) {
- if (Arg.equals_lower("no")) {
- Config->Manifest = Configuration::No;
+void parseManifest(StringRef arg) {
+ if (arg.equals_lower("no")) {
+ config->manifest = Configuration::No;
return;
}
- if (!Arg.startswith_lower("embed"))
- fatal("invalid option " + Arg);
- Config->Manifest = Configuration::Embed;
- Arg = Arg.substr(strlen("embed"));
- if (Arg.empty())
+ if (!arg.startswith_lower("embed"))
+ fatal("invalid option " + arg);
+ config->manifest = Configuration::Embed;
+ arg = arg.substr(strlen("embed"));
+ if (arg.empty())
return;
- if (!Arg.startswith_lower(",id="))
- fatal("invalid option " + Arg);
- Arg = Arg.substr(strlen(",id="));
- if (Arg.getAsInteger(0, Config->ManifestID))
- fatal("invalid option " + Arg);
+ if (!arg.startswith_lower(",id="))
+ fatal("invalid option " + arg);
+ arg = arg.substr(strlen(",id="));
+ if (arg.getAsInteger(0, config->manifestID))
+ fatal("invalid option " + arg);
}
// Parses a string in the form of "level=<string>|uiAccess=<string>|NO".
// Results are directly written to Config.
-void parseManifestUAC(StringRef Arg) {
- if (Arg.equals_lower("no")) {
- Config->ManifestUAC = false;
+void parseManifestUAC(StringRef arg) {
+ if (arg.equals_lower("no")) {
+ config->manifestUAC = false;
return;
}
for (;;) {
- Arg = Arg.ltrim();
- if (Arg.empty())
+ arg = arg.ltrim();
+ if (arg.empty())
return;
- if (Arg.startswith_lower("level=")) {
- Arg = Arg.substr(strlen("level="));
- std::tie(Config->ManifestLevel, Arg) = Arg.split(" ");
+ if (arg.startswith_lower("level=")) {
+ arg = arg.substr(strlen("level="));
+ std::tie(config->manifestLevel, arg) = arg.split(" ");
continue;
}
- if (Arg.startswith_lower("uiaccess=")) {
- Arg = Arg.substr(strlen("uiaccess="));
- std::tie(Config->ManifestUIAccess, Arg) = Arg.split(" ");
+ if (arg.startswith_lower("uiaccess=")) {
+ arg = arg.substr(strlen("uiaccess="));
+ std::tie(config->manifestUIAccess, arg) = arg.split(" ");
continue;
}
- fatal("invalid option " + Arg);
+ fatal("invalid option " + arg);
}
}
+// Parses a string in the form of "cd|net[,(cd|net)]*"
+// Results are directly written to Config.
+void parseSwaprun(StringRef arg) {
+ do {
+ StringRef swaprun, newArg;
+ std::tie(swaprun, newArg) = arg.split(',');
+ if (swaprun.equals_lower("cd"))
+ config->swaprunCD = true;
+ else if (swaprun.equals_lower("net"))
+ config->swaprunNet = true;
+ else if (swaprun.empty())
+ error("/swaprun: missing argument");
+ else
+ error("/swaprun: invalid argument: " + swaprun);
+ // To catch trailing commas, e.g. `/spawrun:cd,`
+ if (newArg.empty() && arg.endswith(","))
+ error("/swaprun: missing argument");
+ arg = newArg;
+ } while (!arg.empty());
+}
+
// An RAII temporary file class that automatically removes a temporary file.
namespace {
class TemporaryFile {
public:
- TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") {
- SmallString<128> S;
- if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S))
- fatal("cannot create a temporary file: " + EC.message());
- Path = S.str();
-
- if (!Contents.empty()) {
- std::error_code EC;
- raw_fd_ostream OS(Path, EC, sys::fs::F_None);
- if (EC)
- fatal("failed to open " + Path + ": " + EC.message());
- OS << Contents;
+ TemporaryFile(StringRef prefix, StringRef extn, StringRef contents = "") {
+ SmallString<128> s;
+ if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s))
+ fatal("cannot create a temporary file: " + ec.message());
+ path = s.str();
+
+ if (!contents.empty()) {
+ std::error_code ec;
+ raw_fd_ostream os(path, ec, sys::fs::F_None);
+ if (ec)
+ fatal("failed to open " + path + ": " + ec.message());
+ os << contents;
}
}
- TemporaryFile(TemporaryFile &&Obj) {
- std::swap(Path, Obj.Path);
+ TemporaryFile(TemporaryFile &&obj) {
+ std::swap(path, obj.path);
}
~TemporaryFile() {
- if (Path.empty())
+ if (path.empty())
return;
- if (sys::fs::remove(Path))
- fatal("failed to remove " + Path);
+ if (sys::fs::remove(path))
+ fatal("failed to remove " + path);
}
// Returns a memory buffer of this temporary file.
@@ -330,387 +345,390 @@ public:
// so it is safe to remove the file immediately after this function
// is called (you cannot remove an opened file on Windows.)
std::unique_ptr<MemoryBuffer> getMemoryBuffer() {
- // IsVolatileSize=true forces MemoryBuffer to not use mmap().
- return CHECK(MemoryBuffer::getFile(Path, /*FileSize=*/-1,
+ // IsVolatile=true forces MemoryBuffer to not use mmap().
+ return CHECK(MemoryBuffer::getFile(path, /*FileSize=*/-1,
/*RequiresNullTerminator=*/false,
- /*IsVolatileSize=*/true),
- "could not open " + Path);
+ /*IsVolatile=*/true),
+ "could not open " + path);
}
- std::string Path;
+ std::string path;
};
}
static std::string createDefaultXml() {
- std::string Ret;
- raw_string_ostream OS(Ret);
+ std::string ret;
+ raw_string_ostream os(ret);
// Emit the XML. Note that we do *not* verify that the XML attributes are
// syntactically correct. This is intentional for link.exe compatibility.
- OS << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
+ os << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
<< "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n"
<< " manifestVersion=\"1.0\">\n";
- if (Config->ManifestUAC) {
- OS << " <trustInfo>\n"
+ if (config->manifestUAC) {
+ os << " <trustInfo>\n"
<< " <security>\n"
<< " <requestedPrivileges>\n"
- << " <requestedExecutionLevel level=" << Config->ManifestLevel
- << " uiAccess=" << Config->ManifestUIAccess << "/>\n"
+ << " <requestedExecutionLevel level=" << config->manifestLevel
+ << " uiAccess=" << config->manifestUIAccess << "/>\n"
<< " </requestedPrivileges>\n"
<< " </security>\n"
<< " </trustInfo>\n";
}
- if (!Config->ManifestDependency.empty()) {
- OS << " <dependency>\n"
+ if (!config->manifestDependency.empty()) {
+ os << " <dependency>\n"
<< " <dependentAssembly>\n"
- << " <assemblyIdentity " << Config->ManifestDependency << " />\n"
+ << " <assemblyIdentity " << config->manifestDependency << " />\n"
<< " </dependentAssembly>\n"
<< " </dependency>\n";
}
- OS << "</assembly>\n";
- return OS.str();
+ os << "</assembly>\n";
+ return os.str();
}
-static std::string createManifestXmlWithInternalMt(StringRef DefaultXml) {
- std::unique_ptr<MemoryBuffer> DefaultXmlCopy =
- MemoryBuffer::getMemBufferCopy(DefaultXml);
+static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
+ std::unique_ptr<MemoryBuffer> defaultXmlCopy =
+ MemoryBuffer::getMemBufferCopy(defaultXml);
- windows_manifest::WindowsManifestMerger Merger;
- if (auto E = Merger.merge(*DefaultXmlCopy.get()))
+ windows_manifest::WindowsManifestMerger merger;
+ if (auto e = merger.merge(*defaultXmlCopy.get()))
fatal("internal manifest tool failed on default xml: " +
- toString(std::move(E)));
+ toString(std::move(e)));
- for (StringRef Filename : Config->ManifestInput) {
- std::unique_ptr<MemoryBuffer> Manifest =
- check(MemoryBuffer::getFile(Filename));
- if (auto E = Merger.merge(*Manifest.get()))
- fatal("internal manifest tool failed on file " + Filename + ": " +
- toString(std::move(E)));
+ for (StringRef filename : config->manifestInput) {
+ std::unique_ptr<MemoryBuffer> manifest =
+ check(MemoryBuffer::getFile(filename));
+ if (auto e = merger.merge(*manifest.get()))
+ fatal("internal manifest tool failed on file " + filename + ": " +
+ toString(std::move(e)));
}
- return Merger.getMergedManifest().get()->getBuffer();
+ return merger.getMergedManifest().get()->getBuffer();
}
-static std::string createManifestXmlWithExternalMt(StringRef DefaultXml) {
+static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
// Create the default manifest file as a temporary file.
TemporaryFile Default("defaultxml", "manifest");
- std::error_code EC;
- raw_fd_ostream OS(Default.Path, EC, sys::fs::F_Text);
- if (EC)
- fatal("failed to open " + Default.Path + ": " + EC.message());
- OS << DefaultXml;
- OS.close();
+ std::error_code ec;
+ raw_fd_ostream os(Default.path, ec, sys::fs::F_Text);
+ if (ec)
+ fatal("failed to open " + Default.path + ": " + ec.message());
+ os << defaultXml;
+ os.close();
// Merge user-supplied manifests if they are given. Since libxml2 is not
// enabled, we must shell out to Microsoft's mt.exe tool.
- TemporaryFile User("user", "manifest");
+ TemporaryFile user("user", "manifest");
- Executor E("mt.exe");
- E.add("/manifest");
- E.add(Default.Path);
- for (StringRef Filename : Config->ManifestInput) {
- E.add("/manifest");
- E.add(Filename);
+ Executor e("mt.exe");
+ e.add("/manifest");
+ e.add(Default.path);
+ for (StringRef filename : config->manifestInput) {
+ e.add("/manifest");
+ e.add(filename);
}
- E.add("/nologo");
- E.add("/out:" + StringRef(User.Path));
- E.run();
+ e.add("/nologo");
+ e.add("/out:" + StringRef(user.path));
+ e.run();
- return CHECK(MemoryBuffer::getFile(User.Path), "could not open " + User.Path)
+ return CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path)
.get()
->getBuffer();
}
static std::string createManifestXml() {
- std::string DefaultXml = createDefaultXml();
- if (Config->ManifestInput.empty())
- return DefaultXml;
+ std::string defaultXml = createDefaultXml();
+ if (config->manifestInput.empty())
+ return defaultXml;
if (windows_manifest::isAvailable())
- return createManifestXmlWithInternalMt(DefaultXml);
+ return createManifestXmlWithInternalMt(defaultXml);
- return createManifestXmlWithExternalMt(DefaultXml);
+ return createManifestXmlWithExternalMt(defaultXml);
}
static std::unique_ptr<WritableMemoryBuffer>
-createMemoryBufferForManifestRes(size_t ManifestSize) {
- size_t ResSize = alignTo(
+createMemoryBufferForManifestRes(size_t manifestSize) {
+ size_t resSize = alignTo(
object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE +
sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) +
- sizeof(object::WinResHeaderSuffix) + ManifestSize,
+ sizeof(object::WinResHeaderSuffix) + manifestSize,
object::WIN_RES_DATA_ALIGNMENT);
- return WritableMemoryBuffer::getNewMemBuffer(ResSize, Config->OutputFile +
+ return WritableMemoryBuffer::getNewMemBuffer(resSize, config->outputFile +
".manifest.res");
}
-static void writeResFileHeader(char *&Buf) {
- memcpy(Buf, COFF::WinResMagic, sizeof(COFF::WinResMagic));
- Buf += sizeof(COFF::WinResMagic);
- memset(Buf, 0, object::WIN_RES_NULL_ENTRY_SIZE);
- Buf += object::WIN_RES_NULL_ENTRY_SIZE;
+static void writeResFileHeader(char *&buf) {
+ memcpy(buf, COFF::WinResMagic, sizeof(COFF::WinResMagic));
+ buf += sizeof(COFF::WinResMagic);
+ memset(buf, 0, object::WIN_RES_NULL_ENTRY_SIZE);
+ buf += object::WIN_RES_NULL_ENTRY_SIZE;
}
-static void writeResEntryHeader(char *&Buf, size_t ManifestSize) {
+static void writeResEntryHeader(char *&buf, size_t manifestSize) {
// Write the prefix.
- auto *Prefix = reinterpret_cast<object::WinResHeaderPrefix *>(Buf);
- Prefix->DataSize = ManifestSize;
- Prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) +
+ auto *prefix = reinterpret_cast<object::WinResHeaderPrefix *>(buf);
+ prefix->DataSize = manifestSize;
+ prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) +
sizeof(object::WinResIDs) +
sizeof(object::WinResHeaderSuffix);
- Buf += sizeof(object::WinResHeaderPrefix);
+ buf += sizeof(object::WinResHeaderPrefix);
// Write the Type/Name IDs.
- auto *IDs = reinterpret_cast<object::WinResIDs *>(Buf);
- IDs->setType(RT_MANIFEST);
- IDs->setName(Config->ManifestID);
- Buf += sizeof(object::WinResIDs);
+ auto *iDs = reinterpret_cast<object::WinResIDs *>(buf);
+ iDs->setType(RT_MANIFEST);
+ iDs->setName(config->manifestID);
+ buf += sizeof(object::WinResIDs);
// Write the suffix.
- auto *Suffix = reinterpret_cast<object::WinResHeaderSuffix *>(Buf);
- Suffix->DataVersion = 0;
- Suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE;
- Suffix->Language = SUBLANG_ENGLISH_US;
- Suffix->Version = 0;
- Suffix->Characteristics = 0;
- Buf += sizeof(object::WinResHeaderSuffix);
+ auto *suffix = reinterpret_cast<object::WinResHeaderSuffix *>(buf);
+ suffix->DataVersion = 0;
+ suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE;
+ suffix->Language = SUBLANG_ENGLISH_US;
+ suffix->Version = 0;
+ suffix->Characteristics = 0;
+ buf += sizeof(object::WinResHeaderSuffix);
}
// Create a resource file containing a manifest XML.
std::unique_ptr<MemoryBuffer> createManifestRes() {
- std::string Manifest = createManifestXml();
+ std::string manifest = createManifestXml();
- std::unique_ptr<WritableMemoryBuffer> Res =
- createMemoryBufferForManifestRes(Manifest.size());
+ std::unique_ptr<WritableMemoryBuffer> res =
+ createMemoryBufferForManifestRes(manifest.size());
- char *Buf = Res->getBufferStart();
- writeResFileHeader(Buf);
- writeResEntryHeader(Buf, Manifest.size());
+ char *buf = res->getBufferStart();
+ writeResFileHeader(buf);
+ writeResEntryHeader(buf, manifest.size());
// Copy the manifest data into the .res file.
- std::copy(Manifest.begin(), Manifest.end(), Buf);
- return std::move(Res);
+ std::copy(manifest.begin(), manifest.end(), buf);
+ return std::move(res);
}
void createSideBySideManifest() {
- std::string Path = Config->ManifestFile;
- if (Path == "")
- Path = Config->OutputFile + ".manifest";
- std::error_code EC;
- raw_fd_ostream Out(Path, EC, sys::fs::F_Text);
- if (EC)
- fatal("failed to create manifest: " + EC.message());
- Out << createManifestXml();
+ std::string path = config->manifestFile;
+ if (path == "")
+ path = config->outputFile + ".manifest";
+ std::error_code ec;
+ raw_fd_ostream out(path, ec, sys::fs::F_Text);
+ if (ec)
+ fatal("failed to create manifest: " + ec.message());
+ out << createManifestXml();
}
// Parse a string in the form of
// "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]"
// or "<name>=<dllname>.<name>".
// Used for parsing /export arguments.
-Export parseExport(StringRef Arg) {
- Export E;
- StringRef Rest;
- std::tie(E.Name, Rest) = Arg.split(",");
- if (E.Name.empty())
+Export parseExport(StringRef arg) {
+ Export e;
+ StringRef rest;
+ std::tie(e.name, rest) = arg.split(",");
+ if (e.name.empty())
goto err;
- if (E.Name.contains('=')) {
- StringRef X, Y;
- std::tie(X, Y) = E.Name.split("=");
+ if (e.name.contains('=')) {
+ StringRef x, y;
+ std::tie(x, y) = e.name.split("=");
// If "<name>=<dllname>.<name>".
- if (Y.contains(".")) {
- E.Name = X;
- E.ForwardTo = Y;
- return E;
+ if (y.contains(".")) {
+ e.name = x;
+ e.forwardTo = y;
+ return e;
}
- E.ExtName = X;
- E.Name = Y;
- if (E.Name.empty())
+ e.extName = x;
+ e.name = y;
+ if (e.name.empty())
goto err;
}
// If "<name>=<internalname>[,@ordinal[,NONAME]][,DATA][,PRIVATE]"
- while (!Rest.empty()) {
- StringRef Tok;
- std::tie(Tok, Rest) = Rest.split(",");
- if (Tok.equals_lower("noname")) {
- if (E.Ordinal == 0)
+ while (!rest.empty()) {
+ StringRef tok;
+ std::tie(tok, rest) = rest.split(",");
+ if (tok.equals_lower("noname")) {
+ if (e.ordinal == 0)
goto err;
- E.Noname = true;
+ e.noname = true;
continue;
}
- if (Tok.equals_lower("data")) {
- E.Data = true;
+ if (tok.equals_lower("data")) {
+ e.data = true;
continue;
}
- if (Tok.equals_lower("constant")) {
- E.Constant = true;
+ if (tok.equals_lower("constant")) {
+ e.constant = true;
continue;
}
- if (Tok.equals_lower("private")) {
- E.Private = true;
+ if (tok.equals_lower("private")) {
+ e.isPrivate = true;
continue;
}
- if (Tok.startswith("@")) {
- int32_t Ord;
- if (Tok.substr(1).getAsInteger(0, Ord))
+ if (tok.startswith("@")) {
+ int32_t ord;
+ if (tok.substr(1).getAsInteger(0, ord))
goto err;
- if (Ord <= 0 || 65535 < Ord)
+ if (ord <= 0 || 65535 < ord)
goto err;
- E.Ordinal = Ord;
+ e.ordinal = ord;
continue;
}
goto err;
}
- return E;
+ return e;
err:
- fatal("invalid /export: " + Arg);
+ fatal("invalid /export: " + arg);
}
-static StringRef undecorate(StringRef Sym) {
- if (Config->Machine != I386)
- return Sym;
+static StringRef undecorate(StringRef sym) {
+ if (config->machine != I386)
+ return sym;
// In MSVC mode, a fully decorated stdcall function is exported
// as-is with the leading underscore (with type IMPORT_NAME).
// In MinGW mode, a decorated stdcall function gets the underscore
// removed, just like normal cdecl functions.
- if (Sym.startswith("_") && Sym.contains('@') && !Config->MinGW)
- return Sym;
- return Sym.startswith("_") ? Sym.substr(1) : Sym;
+ if (sym.startswith("_") && sym.contains('@') && !config->mingw)
+ return sym;
+ return sym.startswith("_") ? sym.substr(1) : sym;
}
// Convert stdcall/fastcall style symbols into unsuffixed symbols,
// with or without a leading underscore. (MinGW specific.)
-static StringRef killAt(StringRef Sym, bool Prefix) {
- if (Sym.empty())
- return Sym;
+static StringRef killAt(StringRef sym, bool prefix) {
+ if (sym.empty())
+ return sym;
// Strip any trailing stdcall suffix
- Sym = Sym.substr(0, Sym.find('@', 1));
- if (!Sym.startswith("@")) {
- if (Prefix && !Sym.startswith("_"))
- return Saver.save("_" + Sym);
- return Sym;
+ sym = sym.substr(0, sym.find('@', 1));
+ if (!sym.startswith("@")) {
+ if (prefix && !sym.startswith("_"))
+ return saver.save("_" + sym);
+ return sym;
}
// For fastcall, remove the leading @ and replace it with an
// underscore, if prefixes are used.
- Sym = Sym.substr(1);
- if (Prefix)
- Sym = Saver.save("_" + Sym);
- return Sym;
+ sym = sym.substr(1);
+ if (prefix)
+ sym = saver.save("_" + sym);
+ return sym;
}
// Performs error checking on all /export arguments.
// It also sets ordinals.
void fixupExports() {
// Symbol ordinals must be unique.
- std::set<uint16_t> Ords;
- for (Export &E : Config->Exports) {
- if (E.Ordinal == 0)
+ std::set<uint16_t> ords;
+ for (Export &e : config->exports) {
+ if (e.ordinal == 0)
continue;
- if (!Ords.insert(E.Ordinal).second)
- fatal("duplicate export ordinal: " + E.Name);
- }
-
- for (Export &E : Config->Exports) {
- Symbol *Sym = E.Sym;
- if (!E.ForwardTo.empty() || !Sym) {
- E.SymbolName = E.Name;
- } else {
- if (auto *U = dyn_cast<Undefined>(Sym))
- if (U->WeakAlias)
- Sym = U->WeakAlias;
- E.SymbolName = Sym->getName();
- }
+ if (!ords.insert(e.ordinal).second)
+ fatal("duplicate export ordinal: " + e.name);
}
- for (Export &E : Config->Exports) {
- if (!E.ForwardTo.empty()) {
- E.ExportName = undecorate(E.Name);
+ for (Export &e : config->exports) {
+ if (!e.forwardTo.empty()) {
+ e.exportName = undecorate(e.name);
} else {
- E.ExportName = undecorate(E.ExtName.empty() ? E.Name : E.ExtName);
+ e.exportName = undecorate(e.extName.empty() ? e.name : e.extName);
}
}
- if (Config->KillAt && Config->Machine == I386) {
- for (Export &E : Config->Exports) {
- E.Name = killAt(E.Name, true);
- E.ExportName = killAt(E.ExportName, false);
- E.ExtName = killAt(E.ExtName, true);
- E.SymbolName = killAt(E.SymbolName, true);
+ if (config->killAt && config->machine == I386) {
+ for (Export &e : config->exports) {
+ e.name = killAt(e.name, true);
+ e.exportName = killAt(e.exportName, false);
+ e.extName = killAt(e.extName, true);
+ e.symbolName = killAt(e.symbolName, true);
}
}
// Uniquefy by name.
- DenseMap<StringRef, Export *> Map(Config->Exports.size());
- std::vector<Export> V;
- for (Export &E : Config->Exports) {
- auto Pair = Map.insert(std::make_pair(E.ExportName, &E));
- bool Inserted = Pair.second;
- if (Inserted) {
- V.push_back(E);
+ DenseMap<StringRef, Export *> map(config->exports.size());
+ std::vector<Export> v;
+ for (Export &e : config->exports) {
+ auto pair = map.insert(std::make_pair(e.exportName, &e));
+ bool inserted = pair.second;
+ if (inserted) {
+ v.push_back(e);
continue;
}
- Export *Existing = Pair.first->second;
- if (E == *Existing || E.Name != Existing->Name)
+ Export *existing = pair.first->second;
+ if (e == *existing || e.name != existing->name)
continue;
- warn("duplicate /export option: " + E.Name);
+ warn("duplicate /export option: " + e.name);
}
- Config->Exports = std::move(V);
+ config->exports = std::move(v);
// Sort by name.
- std::sort(Config->Exports.begin(), Config->Exports.end(),
- [](const Export &A, const Export &B) {
- return A.ExportName < B.ExportName;
+ std::sort(config->exports.begin(), config->exports.end(),
+ [](const Export &a, const Export &b) {
+ return a.exportName < b.exportName;
});
}
void assignExportOrdinals() {
// Assign unique ordinals if default (= 0).
- uint16_t Max = 0;
- for (Export &E : Config->Exports)
- Max = std::max(Max, E.Ordinal);
- for (Export &E : Config->Exports)
- if (E.Ordinal == 0)
- E.Ordinal = ++Max;
+ uint16_t max = 0;
+ for (Export &e : config->exports)
+ max = std::max(max, e.ordinal);
+ for (Export &e : config->exports)
+ if (e.ordinal == 0)
+ e.ordinal = ++max;
}
// Parses a string in the form of "key=value" and check
// if value matches previous values for the same key.
-void checkFailIfMismatch(StringRef Arg) {
- StringRef K, V;
- std::tie(K, V) = Arg.split('=');
- if (K.empty() || V.empty())
- fatal("/failifmismatch: invalid argument: " + Arg);
- StringRef Existing = Config->MustMatch[K];
- if (!Existing.empty() && V != Existing)
- fatal("/failifmismatch: mismatch detected: " + Existing + " and " + V +
- " for key " + K);
- Config->MustMatch[K] = V;
+void checkFailIfMismatch(StringRef arg, InputFile *source) {
+ StringRef k, v;
+ std::tie(k, v) = arg.split('=');
+ if (k.empty() || v.empty())
+ fatal("/failifmismatch: invalid argument: " + arg);
+ std::pair<StringRef, InputFile *> existing = config->mustMatch[k];
+ if (!existing.first.empty() && v != existing.first) {
+ std::string sourceStr = source ? toString(source) : "cmd-line";
+ std::string existingStr =
+ existing.second ? toString(existing.second) : "cmd-line";
+ fatal("/failifmismatch: mismatch detected for '" + k + "':\n>>> " +
+ existingStr + " has value " + existing.first + "\n>>> " + sourceStr +
+ " has value " + v);
+ }
+ config->mustMatch[k] = {v, source};
}
// Convert Windows resource files (.res files) to a .obj file.
-MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs) {
- object::WindowsResourceParser Parser;
-
- for (MemoryBufferRef MB : MBs) {
- std::unique_ptr<object::Binary> Bin = check(object::createBinary(MB));
- object::WindowsResource *RF = dyn_cast<object::WindowsResource>(Bin.get());
- if (!RF)
+// Does what cvtres.exe does, but in-process and cross-platform.
+MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs) {
+ object::WindowsResourceParser parser;
+
+ for (MemoryBufferRef mb : mbs) {
+ std::unique_ptr<object::Binary> bin = check(object::createBinary(mb));
+ object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get());
+ if (!rf)
fatal("cannot compile non-resource file as resource");
- if (auto EC = Parser.parse(RF))
- fatal("failed to parse .res file: " + toString(std::move(EC)));
+
+ std::vector<std::string> duplicates;
+ if (auto ec = parser.parse(rf, duplicates))
+ fatal(toString(std::move(ec)));
+
+ for (const auto &dupeDiag : duplicates)
+ if (config->forceMultipleRes)
+ warn(dupeDiag);
+ else
+ error(dupeDiag);
}
- Expected<std::unique_ptr<MemoryBuffer>> E =
- llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser);
- if (!E)
- fatal("failed to write .res to COFF: " + toString(E.takeError()));
+ Expected<std::unique_ptr<MemoryBuffer>> e =
+ llvm::object::writeWindowsResourceCOFF(config->machine, parser,
+ config->timestamp);
+ if (!e)
+ fatal("failed to write .res to COFF: " + toString(e.takeError()));
- MemoryBufferRef MBRef = **E;
- make<std::unique_ptr<MemoryBuffer>>(std::move(*E)); // take ownership
- return MBRef;
+ MemoryBufferRef mbref = **e;
+ make<std::unique_ptr<MemoryBuffer>>(std::move(*e)); // take ownership
+ return mbref;
}
// Create OptTable
@@ -721,7 +739,7 @@ MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs) {
#undef PREFIX
// Create table mapping all options defined in Options.td
-static const llvm::opt::OptTable::Info InfoTable[] = {
+static const llvm::opt::OptTable::Info infoTable[] = {
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
{X1, X2, X10, X11, OPT_##ID, llvm::opt::Option::KIND##Class, \
X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
@@ -729,36 +747,36 @@ static const llvm::opt::OptTable::Info InfoTable[] = {
#undef OPTION
};
-COFFOptTable::COFFOptTable() : OptTable(InfoTable, true) {}
+COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {}
// Set color diagnostics according to --color-diagnostics={auto,always,never}
// or --no-color-diagnostics flags.
-static void handleColorDiagnostics(opt::InputArgList &Args) {
- auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
+static void handleColorDiagnostics(opt::InputArgList &args) {
+ auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
OPT_no_color_diagnostics);
- if (!Arg)
+ if (!arg)
return;
- if (Arg->getOption().getID() == OPT_color_diagnostics) {
- errorHandler().ColorDiagnostics = true;
- } else if (Arg->getOption().getID() == OPT_no_color_diagnostics) {
- errorHandler().ColorDiagnostics = false;
+ if (arg->getOption().getID() == OPT_color_diagnostics) {
+ errorHandler().colorDiagnostics = true;
+ } else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
+ errorHandler().colorDiagnostics = false;
} else {
- StringRef S = Arg->getValue();
- if (S == "always")
- errorHandler().ColorDiagnostics = true;
- else if (S == "never")
- errorHandler().ColorDiagnostics = false;
- else if (S != "auto")
- error("unknown option: --color-diagnostics=" + S);
- }
-}
-
-static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
- if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) {
- StringRef S = Arg->getValue();
- if (S != "windows" && S != "posix")
- error("invalid response file quoting: " + S);
- if (S == "windows")
+ StringRef s = arg->getValue();
+ if (s == "always")
+ errorHandler().colorDiagnostics = true;
+ else if (s == "never")
+ errorHandler().colorDiagnostics = false;
+ else if (s != "auto")
+ error("unknown option: --color-diagnostics=" + s);
+ }
+}
+
+static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
+ if (auto *arg = args.getLastArg(OPT_rsp_quoting)) {
+ StringRef s = arg->getValue();
+ if (s != "windows" && s != "posix")
+ error("invalid response file quoting: " + s);
+ if (s == "windows")
return cl::TokenizeWindowsCommandLine;
return cl::TokenizeGNUCommandLine;
}
@@ -767,104 +785,111 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
}
// Parses a given list of options.
-opt::InputArgList ArgParser::parse(ArrayRef<const char *> Argv) {
+opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
// Make InputArgList from string vectors.
- unsigned MissingIndex;
- unsigned MissingCount;
+ unsigned missingIndex;
+ unsigned missingCount;
// We need to get the quoting style for response files before parsing all
// options so we parse here before and ignore all the options but
// --rsp-quoting.
- opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount);
+ opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount);
// Expand response files (arguments in the form of @<filename>)
// and then parse the argument again.
- SmallVector<const char *, 256> ExpandedArgv(Argv.data(), Argv.data() + Argv.size());
- cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), ExpandedArgv);
- Args = Table.ParseArgs(makeArrayRef(ExpandedArgv).drop_front(), MissingIndex,
- MissingCount);
+ SmallVector<const char *, 256> expandedArgv(argv.data(),
+ argv.data() + argv.size());
+ cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
+ args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex,
+ missingCount);
// Print the real command line if response files are expanded.
- if (Args.hasArg(OPT_verbose) && Argv.size() != ExpandedArgv.size()) {
- std::string Msg = "Command line:";
- for (const char *S : ExpandedArgv)
- Msg += " " + std::string(S);
- message(Msg);
+ if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) {
+ std::string msg = "Command line:";
+ for (const char *s : expandedArgv)
+ msg += " " + std::string(s);
+ message(msg);
}
// Save the command line after response file expansion so we can write it to
// the PDB if necessary.
- Config->Argv = {ExpandedArgv.begin(), ExpandedArgv.end()};
+ config->argv = {expandedArgv.begin(), expandedArgv.end()};
// Handle /WX early since it converts missing argument warnings to errors.
- errorHandler().FatalWarnings = Args.hasFlag(OPT_WX, OPT_WX_no, false);
+ errorHandler().fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false);
- if (MissingCount)
- fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
+ if (missingCount)
+ fatal(Twine(args.getArgString(missingIndex)) + ": missing argument");
- handleColorDiagnostics(Args);
+ handleColorDiagnostics(args);
- for (auto *Arg : Args.filtered(OPT_UNKNOWN))
- warn("ignoring unknown argument: " + Arg->getSpelling());
+ for (auto *arg : args.filtered(OPT_UNKNOWN)) {
+ std::string nearest;
+ if (table.findNearest(arg->getAsString(args), nearest) > 1)
+ warn("ignoring unknown argument '" + arg->getAsString(args) + "'");
+ else
+ warn("ignoring unknown argument '" + arg->getAsString(args) +
+ "', did you mean '" + nearest + "'");
+ }
- if (Args.hasArg(OPT_lib))
+ if (args.hasArg(OPT_lib))
warn("ignoring /lib since it's not the first argument");
- return Args;
+ return args;
}
// Tokenizes and parses a given string as command line in .drective section.
// /EXPORT options are processed in fastpath.
std::pair<opt::InputArgList, std::vector<StringRef>>
-ArgParser::parseDirectives(StringRef S) {
- std::vector<StringRef> Exports;
- SmallVector<const char *, 16> Rest;
+ArgParser::parseDirectives(StringRef s) {
+ std::vector<StringRef> exports;
+ SmallVector<const char *, 16> rest;
- for (StringRef Tok : tokenize(S)) {
- if (Tok.startswith_lower("/export:") || Tok.startswith_lower("-export:"))
- Exports.push_back(Tok.substr(strlen("/export:")));
+ for (StringRef tok : tokenize(s)) {
+ if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:"))
+ exports.push_back(tok.substr(strlen("/export:")));
else
- Rest.push_back(Tok.data());
+ rest.push_back(tok.data());
}
// Make InputArgList from unparsed string vectors.
- unsigned MissingIndex;
- unsigned MissingCount;
+ unsigned missingIndex;
+ unsigned missingCount;
- opt::InputArgList Args = Table.ParseArgs(Rest, MissingIndex, MissingCount);
+ opt::InputArgList args = table.ParseArgs(rest, missingIndex, missingCount);
- if (MissingCount)
- fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
- for (auto *Arg : Args.filtered(OPT_UNKNOWN))
- warn("ignoring unknown argument: " + Arg->getSpelling());
- return {std::move(Args), std::move(Exports)};
+ if (missingCount)
+ fatal(Twine(args.getArgString(missingIndex)) + ": missing argument");
+ for (auto *arg : args.filtered(OPT_UNKNOWN))
+ warn("ignoring unknown argument: " + arg->getAsString(args));
+ return {std::move(args), std::move(exports)};
}
// link.exe has an interesting feature. If LINK or _LINK_ environment
// variables exist, their contents are handled as command line strings.
// So you can pass extra arguments using them.
-opt::InputArgList ArgParser::parseLINK(std::vector<const char *> Argv) {
+opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) {
// Concatenate LINK env and command line arguments, and then parse them.
- if (Optional<std::string> S = Process::GetEnv("LINK")) {
- std::vector<const char *> V = tokenize(*S);
- Argv.insert(std::next(Argv.begin()), V.begin(), V.end());
+ if (Optional<std::string> s = Process::GetEnv("LINK")) {
+ std::vector<const char *> v = tokenize(*s);
+ argv.insert(std::next(argv.begin()), v.begin(), v.end());
}
- if (Optional<std::string> S = Process::GetEnv("_LINK_")) {
- std::vector<const char *> V = tokenize(*S);
- Argv.insert(std::next(Argv.begin()), V.begin(), V.end());
+ if (Optional<std::string> s = Process::GetEnv("_LINK_")) {
+ std::vector<const char *> v = tokenize(*s);
+ argv.insert(std::next(argv.begin()), v.begin(), v.end());
}
- return parse(Argv);
+ return parse(argv);
}
-std::vector<const char *> ArgParser::tokenize(StringRef S) {
- SmallVector<const char *, 16> Tokens;
- cl::TokenizeWindowsCommandLine(S, Saver, Tokens);
- return std::vector<const char *>(Tokens.begin(), Tokens.end());
+std::vector<const char *> ArgParser::tokenize(StringRef s) {
+ SmallVector<const char *, 16> tokens;
+ cl::TokenizeWindowsCommandLine(s, saver, tokens);
+ return std::vector<const char *>(tokens.begin(), tokens.end());
}
-void printHelp(const char *Argv0) {
+void printHelp(const char *argv0) {
COFFOptTable().PrintHelp(outs(),
- (std::string(Argv0) + " [options] file...").c_str(),
+ (std::string(argv0) + " [options] file...").c_str(),
"LLVM Linker", false);
}
diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp
index 34ea360fa925..2b2818de3889 100644
--- a/COFF/ICF.cpp
+++ b/COFF/ICF.cpp
@@ -1,9 +1,8 @@
//===- ICF.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -38,33 +37,32 @@ using namespace llvm;
namespace lld {
namespace coff {
-static Timer ICFTimer("ICF", Timer::root());
+static Timer icfTimer("ICF", Timer::root());
class ICF {
public:
- void run(ArrayRef<Chunk *> V);
+ void run(ArrayRef<Chunk *> v);
private:
- void segregate(size_t Begin, size_t End, bool Constant);
+ void segregate(size_t begin, size_t end, bool constant);
- bool assocEquals(const SectionChunk *A, const SectionChunk *B);
+ bool assocEquals(const SectionChunk *a, const SectionChunk *b);
- bool equalsConstant(const SectionChunk *A, const SectionChunk *B);
- bool equalsVariable(const SectionChunk *A, const SectionChunk *B);
+ bool equalsConstant(const SectionChunk *a, const SectionChunk *b);
+ bool equalsVariable(const SectionChunk *a, const SectionChunk *b);
- uint32_t getHash(SectionChunk *C);
- bool isEligible(SectionChunk *C);
+ bool isEligible(SectionChunk *c);
- size_t findBoundary(size_t Begin, size_t End);
+ size_t findBoundary(size_t begin, size_t end);
- void forEachClassRange(size_t Begin, size_t End,
- std::function<void(size_t, size_t)> Fn);
+ void forEachClassRange(size_t begin, size_t end,
+ std::function<void(size_t, size_t)> fn);
- void forEachClass(std::function<void(size_t, size_t)> Fn);
+ void forEachClass(std::function<void(size_t, size_t)> fn);
- std::vector<SectionChunk *> Chunks;
- int Cnt = 0;
- std::atomic<bool> Repeat = {false};
+ std::vector<SectionChunk *> chunks;
+ int cnt = 0;
+ std::atomic<bool> repeat = {false};
};
// Returns true if section S is subject of ICF.
@@ -78,143 +76,144 @@ private:
// merge read-only sections in a couple of cases where the address of the
// section is insignificant to the user program and the behaviour matches that
// of the Visual C++ linker.
-bool ICF::isEligible(SectionChunk *C) {
+bool ICF::isEligible(SectionChunk *c) {
// Non-comdat chunks, dead chunks, and writable chunks are not elegible.
- bool Writable = C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
- if (!C->isCOMDAT() || !C->Live || Writable)
+ bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
+ if (!c->isCOMDAT() || !c->live || writable)
return false;
// Code sections are eligible.
- if (C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
+ if (c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
return true;
// .pdata and .xdata unwind info sections are eligible.
- StringRef OutSecName = C->getSectionName().split('$').first;
- if (OutSecName == ".pdata" || OutSecName == ".xdata")
+ StringRef outSecName = c->getSectionName().split('$').first;
+ if (outSecName == ".pdata" || outSecName == ".xdata")
return true;
// So are vtables.
- if (C->Sym && C->Sym->getName().startswith("??_7"))
+ if (c->sym && c->sym->getName().startswith("??_7"))
return true;
// Anything else not in an address-significance table is eligible.
- return !C->KeepUnique;
+ return !c->keepUnique;
}
// Split an equivalence class into smaller classes.
-void ICF::segregate(size_t Begin, size_t End, bool Constant) {
- while (Begin < End) {
+void ICF::segregate(size_t begin, size_t end, bool constant) {
+ while (begin < end) {
// Divide [Begin, End) into two. Let Mid be the start index of the
// second group.
- auto Bound = std::stable_partition(
- Chunks.begin() + Begin + 1, Chunks.begin() + End, [&](SectionChunk *S) {
- if (Constant)
- return equalsConstant(Chunks[Begin], S);
- return equalsVariable(Chunks[Begin], S);
+ auto bound = std::stable_partition(
+ chunks.begin() + begin + 1, chunks.begin() + end, [&](SectionChunk *s) {
+ if (constant)
+ return equalsConstant(chunks[begin], s);
+ return equalsVariable(chunks[begin], s);
});
- size_t Mid = Bound - Chunks.begin();
+ size_t mid = bound - chunks.begin();
// Split [Begin, End) into [Begin, Mid) and [Mid, End). We use Mid as an
// equivalence class ID because every group ends with a unique index.
- for (size_t I = Begin; I < Mid; ++I)
- Chunks[I]->Class[(Cnt + 1) % 2] = Mid;
+ for (size_t i = begin; i < mid; ++i)
+ chunks[i]->eqClass[(cnt + 1) % 2] = mid;
// If we created a group, we need to iterate the main loop again.
- if (Mid != End)
- Repeat = true;
+ if (mid != end)
+ repeat = true;
- Begin = Mid;
+ begin = mid;
}
}
// Returns true if two sections' associative children are equal.
-bool ICF::assocEquals(const SectionChunk *A, const SectionChunk *B) {
- auto ChildClasses = [&](const SectionChunk *SC) {
- std::vector<uint32_t> Classes;
- for (const SectionChunk *C : SC->children())
- if (!C->SectionName.startswith(".debug") &&
- C->SectionName != ".gfids$y" && C->SectionName != ".gljmp$y")
- Classes.push_back(C->Class[Cnt % 2]);
- return Classes;
+bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) {
+ auto childClasses = [&](const SectionChunk *sc) {
+ std::vector<uint32_t> classes;
+ for (const SectionChunk &c : sc->children())
+ if (!c.getSectionName().startswith(".debug") &&
+ c.getSectionName() != ".gfids$y" && c.getSectionName() != ".gljmp$y")
+ classes.push_back(c.eqClass[cnt % 2]);
+ return classes;
};
- return ChildClasses(A) == ChildClasses(B);
+ return childClasses(a) == childClasses(b);
}
// Compare "non-moving" part of two sections, namely everything
// except relocation targets.
-bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
- if (A->Relocs.size() != B->Relocs.size())
+bool ICF::equalsConstant(const SectionChunk *a, const SectionChunk *b) {
+ if (a->relocsSize != b->relocsSize)
return false;
// Compare relocations.
- auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
- if (R1.Type != R2.Type ||
- R1.VirtualAddress != R2.VirtualAddress) {
+ auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {
+ if (r1.Type != r2.Type ||
+ r1.VirtualAddress != r2.VirtualAddress) {
return false;
}
- Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex);
- Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex);
- if (B1 == B2)
+ Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);
+ Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);
+ if (b1 == b2)
return true;
- if (auto *D1 = dyn_cast<DefinedRegular>(B1))
- if (auto *D2 = dyn_cast<DefinedRegular>(B2))
- return D1->getValue() == D2->getValue() &&
- D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2];
+ if (auto *d1 = dyn_cast<DefinedRegular>(b1))
+ if (auto *d2 = dyn_cast<DefinedRegular>(b2))
+ return d1->getValue() == d2->getValue() &&
+ d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];
return false;
};
- if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq))
+ if (!std::equal(a->getRelocs().begin(), a->getRelocs().end(),
+ b->getRelocs().begin(), eq))
return false;
// Compare section attributes and contents.
- return A->getOutputCharacteristics() == B->getOutputCharacteristics() &&
- A->SectionName == B->SectionName &&
- A->Header->SizeOfRawData == B->Header->SizeOfRawData &&
- A->Checksum == B->Checksum && A->getContents() == B->getContents() &&
- assocEquals(A, B);
+ return a->getOutputCharacteristics() == b->getOutputCharacteristics() &&
+ a->getSectionName() == b->getSectionName() &&
+ a->header->SizeOfRawData == b->header->SizeOfRawData &&
+ a->checksum == b->checksum && a->getContents() == b->getContents() &&
+ assocEquals(a, b);
}
// Compare "moving" part of two sections, namely relocation targets.
-bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
+bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) {
// Compare relocations.
- auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
- Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex);
- Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex);
- if (B1 == B2)
+ auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {
+ Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);
+ Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);
+ if (b1 == b2)
return true;
- if (auto *D1 = dyn_cast<DefinedRegular>(B1))
- if (auto *D2 = dyn_cast<DefinedRegular>(B2))
- return D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2];
+ if (auto *d1 = dyn_cast<DefinedRegular>(b1))
+ if (auto *d2 = dyn_cast<DefinedRegular>(b2))
+ return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];
return false;
};
- return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(),
- Eq) &&
- assocEquals(A, B);
+ return std::equal(a->getRelocs().begin(), a->getRelocs().end(),
+ b->getRelocs().begin(), eq) &&
+ assocEquals(a, b);
}
// Find the first Chunk after Begin that has a different class from Begin.
-size_t ICF::findBoundary(size_t Begin, size_t End) {
- for (size_t I = Begin + 1; I < End; ++I)
- if (Chunks[Begin]->Class[Cnt % 2] != Chunks[I]->Class[Cnt % 2])
- return I;
- return End;
+size_t ICF::findBoundary(size_t begin, size_t end) {
+ for (size_t i = begin + 1; i < end; ++i)
+ if (chunks[begin]->eqClass[cnt % 2] != chunks[i]->eqClass[cnt % 2])
+ return i;
+ return end;
}
-void ICF::forEachClassRange(size_t Begin, size_t End,
- std::function<void(size_t, size_t)> Fn) {
- while (Begin < End) {
- size_t Mid = findBoundary(Begin, End);
- Fn(Begin, Mid);
- Begin = Mid;
+void ICF::forEachClassRange(size_t begin, size_t end,
+ std::function<void(size_t, size_t)> fn) {
+ while (begin < end) {
+ size_t mid = findBoundary(begin, end);
+ fn(begin, mid);
+ begin = mid;
}
}
// Call Fn on each class group.
-void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) {
+void ICF::forEachClass(std::function<void(size_t, size_t)> fn) {
// If the number of sections are too small to use threading,
// call Fn sequentially.
- if (Chunks.size() < 1024) {
- forEachClassRange(0, Chunks.size(), Fn);
- ++Cnt;
+ if (chunks.size() < 1024) {
+ forEachClassRange(0, chunks.size(), fn);
+ ++cnt;
return;
}
@@ -222,95 +221,97 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) {
// The sharding must be completed before any calls to Fn are made
// so that Fn can modify the Chunks in its shard without causing data
// races.
- const size_t NumShards = 256;
- size_t Step = Chunks.size() / NumShards;
- size_t Boundaries[NumShards + 1];
- Boundaries[0] = 0;
- Boundaries[NumShards] = Chunks.size();
- parallelForEachN(1, NumShards, [&](size_t I) {
- Boundaries[I] = findBoundary((I - 1) * Step, Chunks.size());
+ const size_t numShards = 256;
+ size_t step = chunks.size() / numShards;
+ size_t boundaries[numShards + 1];
+ boundaries[0] = 0;
+ boundaries[numShards] = chunks.size();
+ parallelForEachN(1, numShards, [&](size_t i) {
+ boundaries[i] = findBoundary((i - 1) * step, chunks.size());
});
- parallelForEachN(1, NumShards + 1, [&](size_t I) {
- if (Boundaries[I - 1] < Boundaries[I]) {
- forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn);
+ parallelForEachN(1, numShards + 1, [&](size_t i) {
+ if (boundaries[i - 1] < boundaries[i]) {
+ forEachClassRange(boundaries[i - 1], boundaries[i], fn);
}
});
- ++Cnt;
+ ++cnt;
}
// Merge identical COMDAT sections.
// Two sections are considered the same if their section headers,
// contents and relocations are all the same.
-void ICF::run(ArrayRef<Chunk *> Vec) {
- ScopedTimer T(ICFTimer);
+void ICF::run(ArrayRef<Chunk *> vec) {
+ ScopedTimer t(icfTimer);
// Collect only mergeable sections and group by hash value.
- uint32_t NextId = 1;
- for (Chunk *C : Vec) {
- if (auto *SC = dyn_cast<SectionChunk>(C)) {
- if (isEligible(SC))
- Chunks.push_back(SC);
+ uint32_t nextId = 1;
+ for (Chunk *c : vec) {
+ if (auto *sc = dyn_cast<SectionChunk>(c)) {
+ if (isEligible(sc))
+ chunks.push_back(sc);
else
- SC->Class[0] = NextId++;
+ sc->eqClass[0] = nextId++;
}
}
// Make sure that ICF doesn't merge sections that are being handled by string
// tail merging.
- for (auto &P : MergeChunk::Instances)
- for (SectionChunk *SC : P.second->Sections)
- SC->Class[0] = NextId++;
+ for (MergeChunk *mc : MergeChunk::instances)
+ if (mc)
+ for (SectionChunk *sc : mc->sections)
+ sc->eqClass[0] = nextId++;
// Initially, we use hash values to partition sections.
- parallelForEach(Chunks, [&](SectionChunk *SC) {
- SC->Class[1] = xxHash64(SC->getContents());
+ parallelForEach(chunks, [&](SectionChunk *sc) {
+ sc->eqClass[0] = xxHash64(sc->getContents());
});
// Combine the hashes of the sections referenced by each section into its
// hash.
- parallelForEach(Chunks, [&](SectionChunk *SC) {
- uint32_t Hash = SC->Class[1];
- for (Symbol *B : SC->symbols())
- if (auto *Sym = dyn_cast_or_null<DefinedRegular>(B))
- Hash ^= Sym->getChunk()->Class[1];
- // Set MSB to 1 to avoid collisions with non-hash classs.
- SC->Class[0] = Hash | (1U << 31);
- });
+ for (unsigned cnt = 0; cnt != 2; ++cnt) {
+ parallelForEach(chunks, [&](SectionChunk *sc) {
+ uint32_t hash = sc->eqClass[cnt % 2];
+ for (Symbol *b : sc->symbols())
+ if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
+ hash += sym->getChunk()->eqClass[cnt % 2];
+ // Set MSB to 1 to avoid collisions with non-hash classs.
+ sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
+ });
+ }
// From now on, sections in Chunks are ordered so that sections in
// the same group are consecutive in the vector.
- std::stable_sort(Chunks.begin(), Chunks.end(),
- [](SectionChunk *A, SectionChunk *B) {
- return A->Class[0] < B->Class[0];
- });
+ llvm::stable_sort(chunks, [](const SectionChunk *a, const SectionChunk *b) {
+ return a->eqClass[0] < b->eqClass[0];
+ });
// Compare static contents and assign unique IDs for each static content.
- forEachClass([&](size_t Begin, size_t End) { segregate(Begin, End, true); });
+ forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); });
// Split groups by comparing relocations until convergence is obtained.
do {
- Repeat = false;
+ repeat = false;
forEachClass(
- [&](size_t Begin, size_t End) { segregate(Begin, End, false); });
- } while (Repeat);
+ [&](size_t begin, size_t end) { segregate(begin, end, false); });
+ } while (repeat);
- log("ICF needed " + Twine(Cnt) + " iterations");
+ log("ICF needed " + Twine(cnt) + " iterations");
// Merge sections in the same classs.
- forEachClass([&](size_t Begin, size_t End) {
- if (End - Begin == 1)
+ forEachClass([&](size_t begin, size_t end) {
+ if (end - begin == 1)
return;
- log("Selected " + Chunks[Begin]->getDebugName());
- for (size_t I = Begin + 1; I < End; ++I) {
- log(" Removed " + Chunks[I]->getDebugName());
- Chunks[Begin]->replace(Chunks[I]);
+ log("Selected " + chunks[begin]->getDebugName());
+ for (size_t i = begin + 1; i < end; ++i) {
+ log(" Removed " + chunks[i]->getDebugName());
+ chunks[begin]->replace(chunks[i]);
}
});
}
// Entry point to ICF.
-void doICF(ArrayRef<Chunk *> Chunks) { ICF().run(Chunks); }
+void doICF(ArrayRef<Chunk *> chunks) { ICF().run(chunks); }
} // namespace coff
} // namespace lld
diff --git a/COFF/ICF.h b/COFF/ICF.h
index 9c54e0c9ec2d..0b3c8fa2ff2e 100644
--- a/COFF/ICF.h
+++ b/COFF/ICF.h
@@ -1,9 +1,8 @@
//===- ICF.h --------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -18,7 +17,7 @@ namespace coff {
class Chunk;
-void doICF(ArrayRef<Chunk *> Chunks);
+void doICF(ArrayRef<Chunk *> chunks);
} // namespace coff
} // namespace lld
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index 236c90ef0388..c00d5c5b494e 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -1,15 +1,15 @@
//===- InputFiles.cpp -----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
#include "Chunks.h"
#include "Config.h"
+#include "DebugTypes.h"
#include "Driver.h"
#include "SymbolTable.h"
#include "Symbols.h"
@@ -20,6 +20,10 @@
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
+#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/Casting.h"
@@ -35,6 +39,7 @@
using namespace llvm;
using namespace llvm::COFF;
+using namespace llvm::codeview;
using namespace llvm::object;
using namespace llvm::support::endian;
@@ -44,80 +49,80 @@ using llvm::support::ulittle32_t;
namespace lld {
namespace coff {
-std::vector<ObjFile *> ObjFile::Instances;
-std::vector<ImportFile *> ImportFile::Instances;
-std::vector<BitcodeFile *> BitcodeFile::Instances;
+std::vector<ObjFile *> ObjFile::instances;
+std::vector<ImportFile *> ImportFile::instances;
+std::vector<BitcodeFile *> BitcodeFile::instances;
/// Checks that Source is compatible with being a weak alias to Target.
/// If Source is Undefined and has no weak alias set, makes it a weak
/// alias to Target.
-static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F,
- Symbol *Source, Symbol *Target) {
- if (auto *U = dyn_cast<Undefined>(Source)) {
- if (U->WeakAlias && U->WeakAlias != Target) {
+static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f,
+ Symbol *source, Symbol *target) {
+ if (auto *u = dyn_cast<Undefined>(source)) {
+ if (u->weakAlias && u->weakAlias != target) {
// Weak aliases as produced by GCC are named in the form
// .weak.<weaksymbol>.<othersymbol>, where <othersymbol> is the name
// of another symbol emitted near the weak symbol.
// Just use the definition from the first object file that defined
// this weak symbol.
- if (Config->MinGW)
+ if (config->mingw)
return;
- Symtab->reportDuplicate(Source, F);
+ symtab->reportDuplicate(source, f);
}
- U->WeakAlias = Target;
+ u->weakAlias = target;
}
}
-ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
+ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
void ArchiveFile::parse() {
// Parse a MemoryBufferRef as an archive file.
- File = CHECK(Archive::create(MB), this);
+ file = CHECK(Archive::create(mb), this);
// Read the symbol table to construct Lazy objects.
- for (const Archive::Symbol &Sym : File->symbols())
- Symtab->addLazy(this, Sym);
+ for (const Archive::Symbol &sym : file->symbols())
+ symtab->addLazy(this, sym);
}
// Returns a buffer pointing to a member file containing a given symbol.
-void ArchiveFile::addMember(const Archive::Symbol *Sym) {
- const Archive::Child &C =
- CHECK(Sym->getMember(),
- "could not get the member for symbol " + Sym->getName());
+void ArchiveFile::addMember(const Archive::Symbol *sym) {
+ const Archive::Child &c =
+ CHECK(sym->getMember(),
+ "could not get the member for symbol " + sym->getName());
// Return an empty buffer if we have already returned the same buffer.
- if (!Seen.insert(C.getChildOffset()).second)
+ if (!seen.insert(c.getChildOffset()).second)
return;
- Driver->enqueueArchiveMember(C, Sym->getName(), getName());
+ driver->enqueueArchiveMember(c, sym->getName(), getName());
}
-std::vector<MemoryBufferRef> getArchiveMembers(Archive *File) {
- std::vector<MemoryBufferRef> V;
- Error Err = Error::success();
- for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
- Archive::Child C =
- CHECK(COrErr,
- File->getFileName() + ": could not get the child of the archive");
- MemoryBufferRef MBRef =
- CHECK(C.getMemoryBufferRef(),
- File->getFileName() +
+std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
+ std::vector<MemoryBufferRef> v;
+ Error err = Error::success();
+ for (const ErrorOr<Archive::Child> &cOrErr : file->children(err)) {
+ Archive::Child c =
+ CHECK(cOrErr,
+ file->getFileName() + ": could not get the child of the archive");
+ MemoryBufferRef mbref =
+ CHECK(c.getMemoryBufferRef(),
+ file->getFileName() +
": could not get the buffer for a child of the archive");
- V.push_back(MBRef);
+ v.push_back(mbref);
}
- if (Err)
- fatal(File->getFileName() +
- ": Archive::children failed: " + toString(std::move(Err)));
- return V;
+ if (err)
+ fatal(file->getFileName() +
+ ": Archive::children failed: " + toString(std::move(err)));
+ return v;
}
void ObjFile::parse() {
// Parse a memory buffer as a COFF file.
- std::unique_ptr<Binary> Bin = CHECK(createBinary(MB), this);
+ std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
- if (auto *Obj = dyn_cast<COFFObjectFile>(Bin.get())) {
- Bin.release();
- COFFObj.reset(Obj);
+ if (auto *obj = dyn_cast<COFFObjectFile>(bin.get())) {
+ bin.release();
+ coffObj.reset(obj);
} else {
fatal(toString(this) + " is not a COFF file");
}
@@ -125,6 +130,15 @@ void ObjFile::parse() {
// Read section and symbol tables.
initializeChunks();
initializeSymbols();
+ initializeFlags();
+ initializeDependencies();
+}
+
+const coff_section* ObjFile::getSection(uint32_t i) {
+ const coff_section *sec;
+ if (auto ec = coffObj->getSection(i, sec))
+ fatal("getSection failed: #" + Twine(i) + ": " + ec.message());
+ return sec;
}
// We set SectionChunk pointers in the SparseChunks vector to this value
@@ -133,45 +147,42 @@ void ObjFile::parse() {
// an associative section definition together with the parent comdat's leader,
// we set the pointer to either nullptr (to mark the section as discarded) or a
// valid SectionChunk for that section.
-static SectionChunk *const PendingComdat = reinterpret_cast<SectionChunk *>(1);
+static SectionChunk *const pendingComdat = reinterpret_cast<SectionChunk *>(1);
void ObjFile::initializeChunks() {
- uint32_t NumSections = COFFObj->getNumberOfSections();
- Chunks.reserve(NumSections);
- SparseChunks.resize(NumSections + 1);
- for (uint32_t I = 1; I < NumSections + 1; ++I) {
- const coff_section *Sec;
- if (auto EC = COFFObj->getSection(I, Sec))
- fatal("getSection failed: #" + Twine(I) + ": " + EC.message());
-
- if (Sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
- SparseChunks[I] = PendingComdat;
+ uint32_t numSections = coffObj->getNumberOfSections();
+ chunks.reserve(numSections);
+ sparseChunks.resize(numSections + 1);
+ for (uint32_t i = 1; i < numSections + 1; ++i) {
+ const coff_section *sec = getSection(i);
+ if (sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
+ sparseChunks[i] = pendingComdat;
else
- SparseChunks[I] = readSection(I, nullptr, "");
+ sparseChunks[i] = readSection(i, nullptr, "");
}
}
-SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
- const coff_aux_section_definition *Def,
- StringRef LeaderName) {
- const coff_section *Sec;
- if (auto EC = COFFObj->getSection(SectionNumber, Sec))
- fatal("getSection failed: #" + Twine(SectionNumber) + ": " + EC.message());
-
- StringRef Name;
- if (auto EC = COFFObj->getSectionName(Sec, Name))
- fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " +
- EC.message());
-
- if (Name == ".drectve") {
- ArrayRef<uint8_t> Data;
- COFFObj->getSectionContents(Sec, Data);
- Directives = std::string((const char *)Data.data(), Data.size());
+SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
+ const coff_aux_section_definition *def,
+ StringRef leaderName) {
+ const coff_section *sec = getSection(sectionNumber);
+
+ StringRef name;
+ if (Expected<StringRef> e = coffObj->getSectionName(sec))
+ name = *e;
+ else
+ fatal("getSectionName failed: #" + Twine(sectionNumber) + ": " +
+ toString(e.takeError()));
+
+ if (name == ".drectve") {
+ ArrayRef<uint8_t> data;
+ cantFail(coffObj->getSectionContents(sec, data));
+ directives = StringRef((const char *)data.data(), data.size());
return nullptr;
}
- if (Name == ".llvm_addrsig") {
- AddrsigSec = Sec;
+ if (name == ".llvm_addrsig") {
+ addrsigSec = sec;
return nullptr;
}
@@ -186,377 +197,648 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
// and then write it to a separate .pdb file.
// Ignore DWARF debug info unless /debug is given.
- if (!Config->Debug && Name.startswith(".debug_"))
+ if (!config->debug && name.startswith(".debug_"))
return nullptr;
- if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
+ if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
return nullptr;
- auto *C = make<SectionChunk>(this, Sec);
- if (Def)
- C->Checksum = Def->CheckSum;
+ auto *c = make<SectionChunk>(this, sec);
+ if (def)
+ c->checksum = def->CheckSum;
+
+ // link.exe uses the presence of .rsrc$01 for LNK4078, so match that.
+ if (name == ".rsrc$01")
+ isResourceObjFile = true;
// CodeView sections are stored to a different vector because they are not
// linked in the regular manner.
- if (C->isCodeView())
- DebugChunks.push_back(C);
- else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gfids$y")
- GuardFidChunks.push_back(C);
- else if (Config->GuardCF != GuardCFLevel::Off && Name == ".gljmp$y")
- GuardLJmpChunks.push_back(C);
- else if (Name == ".sxdata")
- SXDataChunks.push_back(C);
- else if (Config->TailMerge && Sec->NumberOfRelocations == 0 &&
- Name == ".rdata" && LeaderName.startswith("??_C@"))
+ if (c->isCodeView())
+ debugChunks.push_back(c);
+ else if (name == ".gfids$y")
+ guardFidChunks.push_back(c);
+ else if (name == ".gljmp$y")
+ guardLJmpChunks.push_back(c);
+ else if (name == ".sxdata")
+ sXDataChunks.push_back(c);
+ else if (config->tailMerge && sec->NumberOfRelocations == 0 &&
+ name == ".rdata" && leaderName.startswith("??_C@"))
// COFF sections that look like string literal sections (i.e. no
// relocations, in .rdata, leader symbol name matches the MSVC name mangling
// for string literals) are subject to string tail merging.
- MergeChunk::addSection(C);
+ MergeChunk::addSection(c);
else
- Chunks.push_back(C);
+ chunks.push_back(c);
- return C;
+ return c;
}
void ObjFile::readAssociativeDefinition(
- COFFSymbolRef Sym, const coff_aux_section_definition *Def) {
- readAssociativeDefinition(Sym, Def, Def->getNumber(Sym.isBigObj()));
+ COFFSymbolRef sym, const coff_aux_section_definition *def) {
+ readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj()));
}
-void ObjFile::readAssociativeDefinition(COFFSymbolRef Sym,
- const coff_aux_section_definition *Def,
- uint32_t ParentSection) {
- SectionChunk *Parent = SparseChunks[ParentSection];
+void ObjFile::readAssociativeDefinition(COFFSymbolRef sym,
+ const coff_aux_section_definition *def,
+ uint32_t parentIndex) {
+ SectionChunk *parent = sparseChunks[parentIndex];
+ int32_t sectionNumber = sym.getSectionNumber();
+
+ auto diag = [&]() {
+ StringRef name, parentName;
+ coffObj->getSymbolName(sym, name);
+
+ const coff_section *parentSec = getSection(parentIndex);
+ if (Expected<StringRef> e = coffObj->getSectionName(parentSec))
+ parentName = *e;
+ error(toString(this) + ": associative comdat " + name + " (sec " +
+ Twine(sectionNumber) + ") has invalid reference to section " +
+ parentName + " (sec " + Twine(parentIndex) + ")");
+ };
- // If the parent is pending, it probably means that its section definition
- // appears after us in the symbol table. Leave the associated section as
- // pending; we will handle it during the second pass in initializeSymbols().
- if (Parent == PendingComdat)
+ if (parent == pendingComdat) {
+ // This can happen if an associative comdat refers to another associative
+ // comdat that appears after it (invalid per COFF spec) or to a section
+ // without any symbols.
+ diag();
return;
+ }
// Check whether the parent is prevailing. If it is, so are we, and we read
// the section; otherwise mark it as discarded.
- int32_t SectionNumber = Sym.getSectionNumber();
- if (Parent) {
- SparseChunks[SectionNumber] = readSection(SectionNumber, Def, "");
- if (SparseChunks[SectionNumber])
- Parent->addAssociative(SparseChunks[SectionNumber]);
+ if (parent) {
+ SectionChunk *c = readSection(sectionNumber, def, "");
+ sparseChunks[sectionNumber] = c;
+ if (c) {
+ c->selection = IMAGE_COMDAT_SELECT_ASSOCIATIVE;
+ parent->addAssociative(c);
+ }
} else {
- SparseChunks[SectionNumber] = nullptr;
+ sparseChunks[sectionNumber] = nullptr;
}
}
void ObjFile::recordPrevailingSymbolForMingw(
- COFFSymbolRef Sym, DenseMap<StringRef, uint32_t> &PrevailingSectionMap) {
+ COFFSymbolRef sym, DenseMap<StringRef, uint32_t> &prevailingSectionMap) {
// For comdat symbols in executable sections, where this is the copy
// of the section chunk we actually include instead of discarding it,
// add the symbol to a map to allow using it for implicitly
// associating .[px]data$<func> sections to it.
- int32_t SectionNumber = Sym.getSectionNumber();
- SectionChunk *SC = SparseChunks[SectionNumber];
- if (SC && SC->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) {
- StringRef Name;
- COFFObj->getSymbolName(Sym, Name);
- PrevailingSectionMap[Name] = SectionNumber;
+ int32_t sectionNumber = sym.getSectionNumber();
+ SectionChunk *sc = sparseChunks[sectionNumber];
+ if (sc && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) {
+ StringRef name;
+ coffObj->getSymbolName(sym, name);
+ if (getMachineType() == I386)
+ name.consume_front("_");
+ prevailingSectionMap[name] = sectionNumber;
}
}
void ObjFile::maybeAssociateSEHForMingw(
- COFFSymbolRef Sym, const coff_aux_section_definition *Def,
- const DenseMap<StringRef, uint32_t> &PrevailingSectionMap) {
- StringRef Name;
- COFFObj->getSymbolName(Sym, Name);
- if (Name.consume_front(".pdata$") || Name.consume_front(".xdata$")) {
- // For MinGW, treat .[px]data$<func> as implicitly associative to
- // the symbol <func>.
- auto ParentSym = PrevailingSectionMap.find(Name);
- if (ParentSym != PrevailingSectionMap.end())
- readAssociativeDefinition(Sym, Def, ParentSym->second);
+ COFFSymbolRef sym, const coff_aux_section_definition *def,
+ const DenseMap<StringRef, uint32_t> &prevailingSectionMap) {
+ StringRef name;
+ coffObj->getSymbolName(sym, name);
+ if (name.consume_front(".pdata$") || name.consume_front(".xdata$") ||
+ name.consume_front(".eh_frame$")) {
+ // For MinGW, treat .[px]data$<func> and .eh_frame$<func> as implicitly
+ // associative to the symbol <func>.
+ auto parentSym = prevailingSectionMap.find(name);
+ if (parentSym != prevailingSectionMap.end())
+ readAssociativeDefinition(sym, def, parentSym->second);
}
}
-Symbol *ObjFile::createRegular(COFFSymbolRef Sym) {
- SectionChunk *SC = SparseChunks[Sym.getSectionNumber()];
- if (Sym.isExternal()) {
- StringRef Name;
- COFFObj->getSymbolName(Sym, Name);
- if (SC)
- return Symtab->addRegular(this, Name, Sym.getGeneric(), SC);
+Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
+ SectionChunk *sc = sparseChunks[sym.getSectionNumber()];
+ if (sym.isExternal()) {
+ StringRef name;
+ coffObj->getSymbolName(sym, name);
+ if (sc)
+ return symtab->addRegular(this, name, sym.getGeneric(), sc);
// For MinGW symbols named .weak.* that point to a discarded section,
// don't create an Undefined symbol. If nothing ever refers to the symbol,
// everything should be fine. If something actually refers to the symbol
// (e.g. the undefined weak alias), linking will fail due to undefined
// references at the end.
- if (Config->MinGW && Name.startswith(".weak."))
+ if (config->mingw && name.startswith(".weak."))
return nullptr;
- return Symtab->addUndefined(Name, this, false);
+ return symtab->addUndefined(name, this, false);
}
- if (SC)
+ if (sc)
return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
- /*IsExternal*/ false, Sym.getGeneric(), SC);
+ /*IsExternal*/ false, sym.getGeneric(), sc);
return nullptr;
}
void ObjFile::initializeSymbols() {
- uint32_t NumSymbols = COFFObj->getNumberOfSymbols();
- Symbols.resize(NumSymbols);
-
- SmallVector<std::pair<Symbol *, uint32_t>, 8> WeakAliases;
- std::vector<uint32_t> PendingIndexes;
- PendingIndexes.reserve(NumSymbols);
-
- DenseMap<StringRef, uint32_t> PrevailingSectionMap;
- std::vector<const coff_aux_section_definition *> ComdatDefs(
- COFFObj->getNumberOfSections() + 1);
-
- for (uint32_t I = 0; I < NumSymbols; ++I) {
- COFFSymbolRef COFFSym = check(COFFObj->getSymbol(I));
- bool PrevailingComdat;
- if (COFFSym.isUndefined()) {
- Symbols[I] = createUndefined(COFFSym);
- } else if (COFFSym.isWeakExternal()) {
- Symbols[I] = createUndefined(COFFSym);
- uint32_t TagIndex = COFFSym.getAux<coff_aux_weak_external>()->TagIndex;
- WeakAliases.emplace_back(Symbols[I], TagIndex);
- } else if (Optional<Symbol *> OptSym =
- createDefined(COFFSym, ComdatDefs, PrevailingComdat)) {
- Symbols[I] = *OptSym;
- if (Config->MinGW && PrevailingComdat)
- recordPrevailingSymbolForMingw(COFFSym, PrevailingSectionMap);
+ uint32_t numSymbols = coffObj->getNumberOfSymbols();
+ symbols.resize(numSymbols);
+
+ SmallVector<std::pair<Symbol *, uint32_t>, 8> weakAliases;
+ std::vector<uint32_t> pendingIndexes;
+ pendingIndexes.reserve(numSymbols);
+
+ DenseMap<StringRef, uint32_t> prevailingSectionMap;
+ std::vector<const coff_aux_section_definition *> comdatDefs(
+ coffObj->getNumberOfSections() + 1);
+
+ for (uint32_t i = 0; i < numSymbols; ++i) {
+ COFFSymbolRef coffSym = check(coffObj->getSymbol(i));
+ bool prevailingComdat;
+ if (coffSym.isUndefined()) {
+ symbols[i] = createUndefined(coffSym);
+ } else if (coffSym.isWeakExternal()) {
+ symbols[i] = createUndefined(coffSym);
+ uint32_t tagIndex = coffSym.getAux<coff_aux_weak_external>()->TagIndex;
+ weakAliases.emplace_back(symbols[i], tagIndex);
+ } else if (Optional<Symbol *> optSym =
+ createDefined(coffSym, comdatDefs, prevailingComdat)) {
+ symbols[i] = *optSym;
+ if (config->mingw && prevailingComdat)
+ recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap);
} else {
// createDefined() returns None if a symbol belongs to a section that
// was pending at the point when the symbol was read. This can happen in
// two cases:
// 1) section definition symbol for a comdat leader;
- // 2) symbol belongs to a comdat section associated with a section whose
- // section definition symbol appears later in the symbol table.
+ // 2) symbol belongs to a comdat section associated with another section.
// In both of these cases, we can expect the section to be resolved by
// the time we finish visiting the remaining symbols in the symbol
// table. So we postpone the handling of this symbol until that time.
- PendingIndexes.push_back(I);
+ pendingIndexes.push_back(i);
}
- I += COFFSym.getNumberOfAuxSymbols();
+ i += coffSym.getNumberOfAuxSymbols();
}
- for (uint32_t I : PendingIndexes) {
- COFFSymbolRef Sym = check(COFFObj->getSymbol(I));
- if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) {
- if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
- readAssociativeDefinition(Sym, Def);
- else if (Config->MinGW)
- maybeAssociateSEHForMingw(Sym, Def, PrevailingSectionMap);
+ for (uint32_t i : pendingIndexes) {
+ COFFSymbolRef sym = check(coffObj->getSymbol(i));
+ if (const coff_aux_section_definition *def = sym.getSectionDefinition()) {
+ if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
+ readAssociativeDefinition(sym, def);
+ else if (config->mingw)
+ maybeAssociateSEHForMingw(sym, def, prevailingSectionMap);
}
- if (SparseChunks[Sym.getSectionNumber()] == PendingComdat) {
- StringRef Name;
- COFFObj->getSymbolName(Sym, Name);
- log("comdat section " + Name +
+ if (sparseChunks[sym.getSectionNumber()] == pendingComdat) {
+ StringRef name;
+ coffObj->getSymbolName(sym, name);
+ log("comdat section " + name +
" without leader and unassociated, discarding");
continue;
}
- Symbols[I] = createRegular(Sym);
+ symbols[i] = createRegular(sym);
}
- for (auto &KV : WeakAliases) {
- Symbol *Sym = KV.first;
- uint32_t Idx = KV.second;
- checkAndSetWeakAlias(Symtab, this, Sym, Symbols[Idx]);
+ for (auto &kv : weakAliases) {
+ Symbol *sym = kv.first;
+ uint32_t idx = kv.second;
+ checkAndSetWeakAlias(symtab, this, sym, symbols[idx]);
}
}
-Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) {
- StringRef Name;
- COFFObj->getSymbolName(Sym, Name);
- return Symtab->addUndefined(Name, this, Sym.isWeakExternal());
+Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
+ StringRef name;
+ coffObj->getSymbolName(sym, name);
+ return symtab->addUndefined(name, this, sym.isWeakExternal());
+}
+
+void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
+ bool &prevailing, DefinedRegular *leader) {
+ if (prevailing)
+ return;
+ // There's already an existing comdat for this symbol: `Leader`.
+ // Use the comdats's selection field to determine if the new
+ // symbol in `Sym` should be discarded, produce a duplicate symbol
+ // error, etc.
+
+ SectionChunk *leaderChunk = nullptr;
+ COMDATType leaderSelection = IMAGE_COMDAT_SELECT_ANY;
+
+ if (leader->data) {
+ leaderChunk = leader->getChunk();
+ leaderSelection = leaderChunk->selection;
+ } else {
+ // FIXME: comdats from LTO files don't know their selection; treat them
+ // as "any".
+ selection = leaderSelection;
+ }
+
+ if ((selection == IMAGE_COMDAT_SELECT_ANY &&
+ leaderSelection == IMAGE_COMDAT_SELECT_LARGEST) ||
+ (selection == IMAGE_COMDAT_SELECT_LARGEST &&
+ leaderSelection == IMAGE_COMDAT_SELECT_ANY)) {
+ // cl.exe picks "any" for vftables when building with /GR- and
+ // "largest" when building with /GR. To be able to link object files
+ // compiled with each flag, "any" and "largest" are merged as "largest".
+ leaderSelection = selection = IMAGE_COMDAT_SELECT_LARGEST;
+ }
+
+ // Other than that, comdat selections must match. This is a bit more
+ // strict than link.exe which allows merging "any" and "largest" if "any"
+ // is the first symbol the linker sees, and it allows merging "largest"
+ // with everything (!) if "largest" is the first symbol the linker sees.
+ // Making this symmetric independent of which selection is seen first
+ // seems better though.
+ // (This behavior matches ModuleLinker::getComdatResult().)
+ if (selection != leaderSelection) {
+ log(("conflicting comdat type for " + toString(*leader) + ": " +
+ Twine((int)leaderSelection) + " in " + toString(leader->getFile()) +
+ " and " + Twine((int)selection) + " in " + toString(this))
+ .str());
+ symtab->reportDuplicate(leader, this);
+ return;
+ }
+
+ switch (selection) {
+ case IMAGE_COMDAT_SELECT_NODUPLICATES:
+ symtab->reportDuplicate(leader, this);
+ break;
+
+ case IMAGE_COMDAT_SELECT_ANY:
+ // Nothing to do.
+ break;
+
+ case IMAGE_COMDAT_SELECT_SAME_SIZE:
+ if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData)
+ symtab->reportDuplicate(leader, this);
+ break;
+
+ case IMAGE_COMDAT_SELECT_EXACT_MATCH: {
+ SectionChunk newChunk(this, getSection(sym));
+ // link.exe only compares section contents here and doesn't complain
+ // if the two comdat sections have e.g. different alignment.
+ // Match that.
+ if (leaderChunk->getContents() != newChunk.getContents())
+ symtab->reportDuplicate(leader, this);
+ break;
+ }
+
+ case IMAGE_COMDAT_SELECT_ASSOCIATIVE:
+ // createDefined() is never called for IMAGE_COMDAT_SELECT_ASSOCIATIVE.
+ // (This means lld-link doesn't produce duplicate symbol errors for
+ // associative comdats while link.exe does, but associate comdats
+ // are never extern in practice.)
+ llvm_unreachable("createDefined not called for associative comdats");
+
+ case IMAGE_COMDAT_SELECT_LARGEST:
+ if (leaderChunk->getSize() < getSection(sym)->SizeOfRawData) {
+ // Replace the existing comdat symbol with the new one.
+ StringRef name;
+ coffObj->getSymbolName(sym, name);
+ // FIXME: This is incorrect: With /opt:noref, the previous sections
+ // make it into the final executable as well. Correct handling would
+ // be to undo reading of the whole old section that's being replaced,
+ // or doing one pass that determines what the final largest comdat
+ // is for all IMAGE_COMDAT_SELECT_LARGEST comdats and then reading
+ // only the largest one.
+ replaceSymbol<DefinedRegular>(leader, this, name, /*IsCOMDAT*/ true,
+ /*IsExternal*/ true, sym.getGeneric(),
+ nullptr);
+ prevailing = true;
+ }
+ break;
+
+ case IMAGE_COMDAT_SELECT_NEWEST:
+ llvm_unreachable("should have been rejected earlier");
+ }
}
Optional<Symbol *> ObjFile::createDefined(
- COFFSymbolRef Sym,
- std::vector<const coff_aux_section_definition *> &ComdatDefs,
- bool &Prevailing) {
- Prevailing = false;
- auto GetName = [&]() {
- StringRef S;
- COFFObj->getSymbolName(Sym, S);
- return S;
+ COFFSymbolRef sym,
+ std::vector<const coff_aux_section_definition *> &comdatDefs,
+ bool &prevailing) {
+ prevailing = false;
+ auto getName = [&]() {
+ StringRef s;
+ coffObj->getSymbolName(sym, s);
+ return s;
};
- if (Sym.isCommon()) {
- auto *C = make<CommonChunk>(Sym);
- Chunks.push_back(C);
- return Symtab->addCommon(this, GetName(), Sym.getValue(), Sym.getGeneric(),
- C);
+ if (sym.isCommon()) {
+ auto *c = make<CommonChunk>(sym);
+ chunks.push_back(c);
+ return symtab->addCommon(this, getName(), sym.getValue(), sym.getGeneric(),
+ c);
}
- if (Sym.isAbsolute()) {
- StringRef Name = GetName();
+ if (sym.isAbsolute()) {
+ StringRef name = getName();
// Skip special symbols.
- if (Name == "@comp.id")
+ if (name == "@comp.id")
return nullptr;
- if (Name == "@feat.00") {
- Feat00Flags = Sym.getValue();
+ if (name == "@feat.00") {
+ feat00Flags = sym.getValue();
return nullptr;
}
- if (Sym.isExternal())
- return Symtab->addAbsolute(Name, Sym);
- return make<DefinedAbsolute>(Name, Sym);
+ if (sym.isExternal())
+ return symtab->addAbsolute(name, sym);
+ return make<DefinedAbsolute>(name, sym);
}
- int32_t SectionNumber = Sym.getSectionNumber();
- if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
+ int32_t sectionNumber = sym.getSectionNumber();
+ if (sectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
return nullptr;
- if (llvm::COFF::isReservedSectionNumber(SectionNumber))
- fatal(toString(this) + ": " + GetName() +
- " should not refer to special section " + Twine(SectionNumber));
-
- if ((uint32_t)SectionNumber >= SparseChunks.size())
- fatal(toString(this) + ": " + GetName() +
- " should not refer to non-existent section " + Twine(SectionNumber));
-
- // Handle comdat leader symbols.
- if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) {
- ComdatDefs[SectionNumber] = nullptr;
- Symbol *Leader;
- if (Sym.isExternal()) {
- std::tie(Leader, Prevailing) =
- Symtab->addComdat(this, GetName(), Sym.getGeneric());
+ if (llvm::COFF::isReservedSectionNumber(sectionNumber))
+ fatal(toString(this) + ": " + getName() +
+ " should not refer to special section " + Twine(sectionNumber));
+
+ if ((uint32_t)sectionNumber >= sparseChunks.size())
+ fatal(toString(this) + ": " + getName() +
+ " should not refer to non-existent section " + Twine(sectionNumber));
+
+ // Comdat handling.
+ // A comdat symbol consists of two symbol table entries.
+ // The first symbol entry has the name of the section (e.g. .text), fixed
+ // values for the other fields, and one auxilliary record.
+ // The second symbol entry has the name of the comdat symbol, called the
+ // "comdat leader".
+ // When this function is called for the first symbol entry of a comdat,
+ // it sets comdatDefs and returns None, and when it's called for the second
+ // symbol entry it reads comdatDefs and then sets it back to nullptr.
+
+ // Handle comdat leader.
+ if (const coff_aux_section_definition *def = comdatDefs[sectionNumber]) {
+ comdatDefs[sectionNumber] = nullptr;
+ DefinedRegular *leader;
+
+ if (sym.isExternal()) {
+ std::tie(leader, prevailing) =
+ symtab->addComdat(this, getName(), sym.getGeneric());
} else {
- Leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
- /*IsExternal*/ false, Sym.getGeneric());
- Prevailing = true;
+ leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
+ /*IsExternal*/ false, sym.getGeneric());
+ prevailing = true;
}
- if (Prevailing) {
- SectionChunk *C = readSection(SectionNumber, Def, GetName());
- SparseChunks[SectionNumber] = C;
- C->Sym = cast<DefinedRegular>(Leader);
- cast<DefinedRegular>(Leader)->Data = &C->Repl;
- } else {
- SparseChunks[SectionNumber] = nullptr;
+ if (def->Selection < (int)IMAGE_COMDAT_SELECT_NODUPLICATES ||
+ // Intentionally ends at IMAGE_COMDAT_SELECT_LARGEST: link.exe
+ // doesn't understand IMAGE_COMDAT_SELECT_NEWEST either.
+ def->Selection > (int)IMAGE_COMDAT_SELECT_LARGEST) {
+ fatal("unknown comdat type " + std::to_string((int)def->Selection) +
+ " for " + getName() + " in " + toString(this));
}
- return Leader;
- }
+ COMDATType selection = (COMDATType)def->Selection;
- // Read associative section definitions and prepare to handle the comdat
- // leader symbol by setting the section's ComdatDefs pointer if we encounter a
- // non-associative comdat.
- if (SparseChunks[SectionNumber] == PendingComdat) {
- if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) {
- if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
- readAssociativeDefinition(Sym, Def);
- else
- ComdatDefs[SectionNumber] = Def;
+ if (leader->isCOMDAT)
+ handleComdatSelection(sym, selection, prevailing, leader);
+
+ if (prevailing) {
+ SectionChunk *c = readSection(sectionNumber, def, getName());
+ sparseChunks[sectionNumber] = c;
+ c->sym = cast<DefinedRegular>(leader);
+ c->selection = selection;
+ cast<DefinedRegular>(leader)->data = &c->repl;
+ } else {
+ sparseChunks[sectionNumber] = nullptr;
}
+ return leader;
}
- // readAssociativeDefinition() writes to SparseChunks, so need to check again.
- if (SparseChunks[SectionNumber] == PendingComdat)
+ // Prepare to handle the comdat leader symbol by setting the section's
+ // ComdatDefs pointer if we encounter a non-associative comdat.
+ if (sparseChunks[sectionNumber] == pendingComdat) {
+ if (const coff_aux_section_definition *def = sym.getSectionDefinition()) {
+ if (def->Selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE)
+ comdatDefs[sectionNumber] = def;
+ }
return None;
+ }
- return createRegular(Sym);
+ return createRegular(sym);
}
MachineTypes ObjFile::getMachineType() {
- if (COFFObj)
- return static_cast<MachineTypes>(COFFObj->getMachine());
+ if (coffObj)
+ return static_cast<MachineTypes>(coffObj->getMachine());
return IMAGE_FILE_MACHINE_UNKNOWN;
}
-StringRef ltrim1(StringRef S, const char *Chars) {
- if (!S.empty() && strchr(Chars, S[0]))
- return S.substr(1);
- return S;
+ArrayRef<uint8_t> ObjFile::getDebugSection(StringRef secName) {
+ if (SectionChunk *sec = SectionChunk::findByName(debugChunks, secName))
+ return sec->consumeDebugMagic();
+ return {};
+}
+
+// OBJ files systematically store critical informations in a .debug$S stream,
+// even if the TU was compiled with no debug info. At least two records are
+// always there. S_OBJNAME stores a 32-bit signature, which is loaded into the
+// PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is
+// currently used to initialize the hotPatchable member.
+void ObjFile::initializeFlags() {
+ ArrayRef<uint8_t> data = getDebugSection(".debug$S");
+ if (data.empty())
+ return;
+
+ DebugSubsectionArray subsections;
+
+ BinaryStreamReader reader(data, support::little);
+ ExitOnError exitOnErr;
+ exitOnErr(reader.readArray(subsections, data.size()));
+
+ for (const DebugSubsectionRecord &ss : subsections) {
+ if (ss.kind() != DebugSubsectionKind::Symbols)
+ continue;
+
+ unsigned offset = 0;
+
+ // Only parse the first two records. We are only looking for S_OBJNAME
+ // and S_COMPILE3, and they usually appear at the beginning of the
+ // stream.
+ for (unsigned i = 0; i < 2; ++i) {
+ Expected<CVSymbol> sym = readSymbolFromStream(ss.getRecordData(), offset);
+ if (!sym) {
+ consumeError(sym.takeError());
+ return;
+ }
+ if (sym->kind() == SymbolKind::S_COMPILE3) {
+ auto cs =
+ cantFail(SymbolDeserializer::deserializeAs<Compile3Sym>(sym.get()));
+ hotPatchable =
+ (cs.Flags & CompileSym3Flags::HotPatch) != CompileSym3Flags::None;
+ }
+ if (sym->kind() == SymbolKind::S_OBJNAME) {
+ auto objName = cantFail(SymbolDeserializer::deserializeAs<ObjNameSym>(
+ sym.get()));
+ pchSignature = objName.Signature;
+ }
+ offset += sym->length();
+ }
+ }
+}
+
+// Depending on the compilation flags, OBJs can refer to external files,
+// necessary to merge this OBJ into the final PDB. We currently support two
+// types of external files: Precomp/PCH OBJs, when compiling with /Yc and /Yu.
+// And PDB type servers, when compiling with /Zi. This function extracts these
+// dependencies and makes them available as a TpiSource interface (see
+// DebugTypes.h). Both cases only happen with cl.exe: clang-cl produces regular
+// output even with /Yc and /Yu and with /Zi.
+void ObjFile::initializeDependencies() {
+ if (!config->debug)
+ return;
+
+ bool isPCH = false;
+
+ ArrayRef<uint8_t> data = getDebugSection(".debug$P");
+ if (!data.empty())
+ isPCH = true;
+ else
+ data = getDebugSection(".debug$T");
+
+ if (data.empty())
+ return;
+
+ CVTypeArray types;
+ BinaryStreamReader reader(data, support::little);
+ cantFail(reader.readArray(types, reader.getLength()));
+
+ CVTypeArray::Iterator firstType = types.begin();
+ if (firstType == types.end())
+ return;
+
+ debugTypes.emplace(types);
+
+ if (isPCH) {
+ debugTypesObj = makePrecompSource(this);
+ return;
+ }
+
+ if (firstType->kind() == LF_TYPESERVER2) {
+ TypeServer2Record ts = cantFail(
+ TypeDeserializer::deserializeAs<TypeServer2Record>(firstType->data()));
+ debugTypesObj = makeUseTypeServerSource(this, &ts);
+ return;
+ }
+
+ if (firstType->kind() == LF_PRECOMP) {
+ PrecompRecord precomp = cantFail(
+ TypeDeserializer::deserializeAs<PrecompRecord>(firstType->data()));
+ debugTypesObj = makeUsePrecompSource(this, &precomp);
+ return;
+ }
+
+ debugTypesObj = makeTpiSource(this);
+}
+
+StringRef ltrim1(StringRef s, const char *chars) {
+ if (!s.empty() && strchr(chars, s[0]))
+ return s.substr(1);
+ return s;
}
void ImportFile::parse() {
- const char *Buf = MB.getBufferStart();
- const char *End = MB.getBufferEnd();
- const auto *Hdr = reinterpret_cast<const coff_import_header *>(Buf);
+ const char *buf = mb.getBufferStart();
+ const auto *hdr = reinterpret_cast<const coff_import_header *>(buf);
// Check if the total size is valid.
- if ((size_t)(End - Buf) != (sizeof(*Hdr) + Hdr->SizeOfData))
+ if (mb.getBufferSize() != sizeof(*hdr) + hdr->SizeOfData)
fatal("broken import library");
// Read names and create an __imp_ symbol.
- StringRef Name = Saver.save(StringRef(Buf + sizeof(*Hdr)));
- StringRef ImpName = Saver.save("__imp_" + Name);
- const char *NameStart = Buf + sizeof(coff_import_header) + Name.size() + 1;
- DLLName = StringRef(NameStart);
- StringRef ExtName;
- switch (Hdr->getNameType()) {
+ StringRef name = saver.save(StringRef(buf + sizeof(*hdr)));
+ StringRef impName = saver.save("__imp_" + name);
+ const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1;
+ dllName = StringRef(nameStart);
+ StringRef extName;
+ switch (hdr->getNameType()) {
case IMPORT_ORDINAL:
- ExtName = "";
+ extName = "";
break;
case IMPORT_NAME:
- ExtName = Name;
+ extName = name;
break;
case IMPORT_NAME_NOPREFIX:
- ExtName = ltrim1(Name, "?@_");
+ extName = ltrim1(name, "?@_");
break;
case IMPORT_NAME_UNDECORATE:
- ExtName = ltrim1(Name, "?@_");
- ExtName = ExtName.substr(0, ExtName.find('@'));
+ extName = ltrim1(name, "?@_");
+ extName = extName.substr(0, extName.find('@'));
break;
}
- this->Hdr = Hdr;
- ExternalName = ExtName;
+ this->hdr = hdr;
+ externalName = extName;
- ImpSym = Symtab->addImportData(ImpName, this);
+ impSym = symtab->addImportData(impName, this);
// If this was a duplicate, we logged an error but may continue;
- // in this case, ImpSym is nullptr.
- if (!ImpSym)
+ // in this case, impSym is nullptr.
+ if (!impSym)
return;
- if (Hdr->getType() == llvm::COFF::IMPORT_CONST)
- static_cast<void>(Symtab->addImportData(Name, this));
+ if (hdr->getType() == llvm::COFF::IMPORT_CONST)
+ static_cast<void>(symtab->addImportData(name, this));
// If type is function, we need to create a thunk which jump to an
// address pointed by the __imp_ symbol. (This allows you to call
// DLL functions just like regular non-DLL functions.)
- if (Hdr->getType() == llvm::COFF::IMPORT_CODE)
- ThunkSym = Symtab->addImportThunk(
- Name, cast_or_null<DefinedImportData>(ImpSym), Hdr->Machine);
+ if (hdr->getType() == llvm::COFF::IMPORT_CODE)
+ thunkSym = symtab->addImportThunk(
+ name, cast_or_null<DefinedImportData>(impSym), hdr->Machine);
+}
+
+BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
+ uint64_t offsetInArchive)
+ : InputFile(BitcodeKind, mb) {
+ std::string path = mb.getBufferIdentifier().str();
+ if (config->thinLTOIndexOnly)
+ path = replaceThinLTOSuffix(mb.getBufferIdentifier());
+
+ // ThinLTO assumes that all MemoryBufferRefs given to it have a unique
+ // name. If two archives define two members with the same name, this
+ // causes a collision which result in only one of the objects being taken
+ // into consideration at LTO time (which very likely causes undefined
+ // symbols later in the link stage). So we append file offset to make
+ // filename unique.
+ MemoryBufferRef mbref(
+ mb.getBuffer(),
+ saver.save(archiveName + path +
+ (archiveName.empty() ? "" : utostr(offsetInArchive))));
+
+ obj = check(lto::InputFile::create(mbref));
}
void BitcodeFile::parse() {
- Obj = check(lto::InputFile::create(MemoryBufferRef(
- MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier()))));
- std::vector<std::pair<Symbol *, bool>> Comdat(Obj->getComdatTable().size());
- for (size_t I = 0; I != Obj->getComdatTable().size(); ++I)
- Comdat[I] = Symtab->addComdat(this, Saver.save(Obj->getComdatTable()[I]));
- for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) {
- StringRef SymName = Saver.save(ObjSym.getName());
- int ComdatIndex = ObjSym.getComdatIndex();
- Symbol *Sym;
- if (ObjSym.isUndefined()) {
- Sym = Symtab->addUndefined(SymName, this, false);
- } else if (ObjSym.isCommon()) {
- Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize());
- } else if (ObjSym.isWeak() && ObjSym.isIndirect()) {
+ std::vector<std::pair<Symbol *, bool>> comdat(obj->getComdatTable().size());
+ for (size_t i = 0; i != obj->getComdatTable().size(); ++i)
+ // FIXME: lto::InputFile doesn't keep enough data to do correct comdat
+ // selection handling.
+ comdat[i] = symtab->addComdat(this, saver.save(obj->getComdatTable()[i]));
+ for (const lto::InputFile::Symbol &objSym : obj->symbols()) {
+ StringRef symName = saver.save(objSym.getName());
+ int comdatIndex = objSym.getComdatIndex();
+ Symbol *sym;
+ if (objSym.isUndefined()) {
+ sym = symtab->addUndefined(symName, this, false);
+ } else if (objSym.isCommon()) {
+ sym = symtab->addCommon(this, symName, objSym.getCommonSize());
+ } else if (objSym.isWeak() && objSym.isIndirect()) {
// Weak external.
- Sym = Symtab->addUndefined(SymName, this, true);
- std::string Fallback = ObjSym.getCOFFWeakExternalFallback();
- Symbol *Alias = Symtab->addUndefined(Saver.save(Fallback));
- checkAndSetWeakAlias(Symtab, this, Sym, Alias);
- } else if (ComdatIndex != -1) {
- if (SymName == Obj->getComdatTable()[ComdatIndex])
- Sym = Comdat[ComdatIndex].first;
- else if (Comdat[ComdatIndex].second)
- Sym = Symtab->addRegular(this, SymName);
+ sym = symtab->addUndefined(symName, this, true);
+ std::string fallback = objSym.getCOFFWeakExternalFallback();
+ Symbol *alias = symtab->addUndefined(saver.save(fallback));
+ checkAndSetWeakAlias(symtab, this, sym, alias);
+ } else if (comdatIndex != -1) {
+ if (symName == obj->getComdatTable()[comdatIndex])
+ sym = comdat[comdatIndex].first;
+ else if (comdat[comdatIndex].second)
+ sym = symtab->addRegular(this, symName);
else
- Sym = Symtab->addUndefined(SymName, this, false);
+ sym = symtab->addUndefined(symName, this, false);
} else {
- Sym = Symtab->addRegular(this, SymName);
+ sym = symtab->addRegular(this, symName);
}
- Symbols.push_back(Sym);
+ symbols.push_back(sym);
+ if (objSym.isUsed())
+ config->gcroot.push_back(sym);
}
- Directives = Obj->getCOFFLinkerOpts();
+ directives = obj->getCOFFLinkerOpts();
}
MachineTypes BitcodeFile::getMachineType() {
- switch (Triple(Obj->getTargetTriple()).getArch()) {
+ switch (Triple(obj->getTargetTriple()).getArch()) {
case Triple::x86_64:
return AMD64;
case Triple::x86:
@@ -569,22 +851,31 @@ MachineTypes BitcodeFile::getMachineType() {
return IMAGE_FILE_MACHINE_UNKNOWN;
}
}
+
+std::string replaceThinLTOSuffix(StringRef path) {
+ StringRef suffix = config->thinLTOObjectSuffixReplace.first;
+ StringRef repl = config->thinLTOObjectSuffixReplace.second;
+
+ if (path.consume_back(suffix))
+ return (path + repl).str();
+ return path;
+}
} // namespace coff
} // namespace lld
// Returns the last element of a path, which is supposed to be a filename.
-static StringRef getBasename(StringRef Path) {
- return sys::path::filename(Path, sys::path::Style::windows);
+static StringRef getBasename(StringRef path) {
+ return sys::path::filename(path, sys::path::Style::windows);
}
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
-std::string lld::toString(const coff::InputFile *File) {
- if (!File)
+std::string lld::toString(const coff::InputFile *file) {
+ if (!file)
return "<internal>";
- if (File->ParentName.empty())
- return File->getName();
+ if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
+ return file->getName();
- return (getBasename(File->ParentName) + "(" + getBasename(File->getName()) +
+ return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
")")
.str();
}
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index ec802f2d0300..dfad9814a397 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -1,9 +1,8 @@
//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -33,7 +32,7 @@ class DbiModuleDescriptorBuilder;
namespace lld {
namespace coff {
-std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *File);
+std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *file);
using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
using llvm::COFF::MachineTypes;
@@ -47,20 +46,22 @@ class Chunk;
class Defined;
class DefinedImportData;
class DefinedImportThunk;
+class DefinedRegular;
class Lazy;
class SectionChunk;
class Symbol;
class Undefined;
+class TpiSource;
// The root class of input files.
class InputFile {
public:
enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind };
- Kind kind() const { return FileKind; }
+ Kind kind() const { return fileKind; }
virtual ~InputFile() {}
// Returns the filename.
- StringRef getName() const { return MB.getBufferIdentifier(); }
+ StringRef getName() const { return mb.getBufferIdentifier(); }
// Reads a file (the constructor doesn't do that).
virtual void parse() = 0;
@@ -68,158 +69,195 @@ public:
// Returns the CPU type this file was compiled to.
virtual MachineTypes getMachineType() { return IMAGE_FILE_MACHINE_UNKNOWN; }
- MemoryBufferRef MB;
+ MemoryBufferRef mb;
// An archive file name if this file is created from an archive.
- StringRef ParentName;
+ StringRef parentName;
// Returns .drectve section contents if exist.
- StringRef getDirectives() { return StringRef(Directives).trim(); }
+ StringRef getDirectives() { return directives; }
protected:
- InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
+ InputFile(Kind k, MemoryBufferRef m) : mb(m), fileKind(k) {}
- std::string Directives;
+ StringRef directives;
private:
- const Kind FileKind;
+ const Kind fileKind;
};
// .lib or .a file.
class ArchiveFile : public InputFile {
public:
- explicit ArchiveFile(MemoryBufferRef M);
- static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
+ explicit ArchiveFile(MemoryBufferRef m);
+ static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
void parse() override;
// Enqueues an archive member load for the given symbol. If we've already
// enqueued a load for the same archive member, this function does nothing,
// which ensures that we don't load the same member more than once.
- void addMember(const Archive::Symbol *Sym);
+ void addMember(const Archive::Symbol *sym);
private:
- std::unique_ptr<Archive> File;
- std::string Filename;
- llvm::DenseSet<uint64_t> Seen;
+ std::unique_ptr<Archive> file;
+ llvm::DenseSet<uint64_t> seen;
};
// .obj or .o file. This may be a member of an archive file.
class ObjFile : public InputFile {
public:
- explicit ObjFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {}
- static bool classof(const InputFile *F) { return F->kind() == ObjectKind; }
+ explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {}
+ static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
void parse() override;
MachineTypes getMachineType() override;
- ArrayRef<Chunk *> getChunks() { return Chunks; }
- ArrayRef<SectionChunk *> getDebugChunks() { return DebugChunks; }
- ArrayRef<SectionChunk *> getSXDataChunks() { return SXDataChunks; }
- ArrayRef<SectionChunk *> getGuardFidChunks() { return GuardFidChunks; }
- ArrayRef<SectionChunk *> getGuardLJmpChunks() { return GuardLJmpChunks; }
- ArrayRef<Symbol *> getSymbols() { return Symbols; }
-
- // Returns a Symbol object for the SymbolIndex'th symbol in the
+ ArrayRef<Chunk *> getChunks() { return chunks; }
+ ArrayRef<SectionChunk *> getDebugChunks() { return debugChunks; }
+ ArrayRef<SectionChunk *> getSXDataChunks() { return sXDataChunks; }
+ ArrayRef<SectionChunk *> getGuardFidChunks() { return guardFidChunks; }
+ ArrayRef<SectionChunk *> getGuardLJmpChunks() { return guardLJmpChunks; }
+ ArrayRef<Symbol *> getSymbols() { return symbols; }
+
+ ArrayRef<uint8_t> getDebugSection(StringRef secName);
+
+ // Returns a Symbol object for the symbolIndex'th symbol in the
// underlying object file.
- Symbol *getSymbol(uint32_t SymbolIndex) {
- return Symbols[SymbolIndex];
+ Symbol *getSymbol(uint32_t symbolIndex) {
+ return symbols[symbolIndex];
}
// Returns the underlying COFF file.
- COFFObjectFile *getCOFFObj() { return COFFObj.get(); }
+ COFFObjectFile *getCOFFObj() { return coffObj.get(); }
- // Whether the object was already merged into the final PDB or not
- bool wasProcessedForPDB() const { return !!ModuleDBI; }
+ // Add a symbol for a range extension thunk. Return the new symbol table
+ // index. This index can be used to modify a relocation.
+ uint32_t addRangeThunkSymbol(Symbol *thunk) {
+ symbols.push_back(thunk);
+ return symbols.size() - 1;
+ }
- static std::vector<ObjFile *> Instances;
+ static std::vector<ObjFile *> instances;
// Flags in the absolute @feat.00 symbol if it is present. These usually
// indicate if an object was compiled with certain security features enabled
// like stack guard, safeseh, /guard:cf, or other things.
- uint32_t Feat00Flags = 0;
+ uint32_t feat00Flags = 0;
// True if this object file is compatible with SEH. COFF-specific and
// x86-only. COFF spec 5.10.1. The .sxdata section.
- bool hasSafeSEH() { return Feat00Flags & 0x1; }
+ bool hasSafeSEH() { return feat00Flags & 0x1; }
// True if this file was compiled with /guard:cf.
- bool hasGuardCF() { return Feat00Flags & 0x800; }
+ bool hasGuardCF() { return feat00Flags & 0x800; }
// Pointer to the PDB module descriptor builder. Various debug info records
// will reference object files by "module index", which is here. Things like
// source files and section contributions are also recorded here. Will be null
// if we are not producing a PDB.
- llvm::pdb::DbiModuleDescriptorBuilder *ModuleDBI = nullptr;
+ llvm::pdb::DbiModuleDescriptorBuilder *moduleDBI = nullptr;
- const coff_section *AddrsigSec = nullptr;
+ const coff_section *addrsigSec = nullptr;
// When using Microsoft precompiled headers, this is the PCH's key.
// The same key is used by both the precompiled object, and objects using the
// precompiled object. Any difference indicates out-of-date objects.
- llvm::Optional<uint32_t> PCHSignature;
+ llvm::Optional<uint32_t> pchSignature;
+
+ // Whether this is an object file created from .res files.
+ bool isResourceObjFile = false;
+
+ // Whether this file was compiled with /hotpatch.
+ bool hotPatchable = false;
+
+ // Whether the object was already merged into the final PDB.
+ bool mergedIntoPDB = false;
+
+ // If the OBJ has a .debug$T stream, this tells how it will be handled.
+ TpiSource *debugTypesObj = nullptr;
+
+ // The .debug$T stream if there's one.
+ llvm::Optional<llvm::codeview::CVTypeArray> debugTypes;
private:
+ const coff_section* getSection(uint32_t i);
+ const coff_section *getSection(COFFSymbolRef sym) {
+ return getSection(sym.getSectionNumber());
+ }
+
void initializeChunks();
void initializeSymbols();
+ void initializeFlags();
+ void initializeDependencies();
SectionChunk *
- readSection(uint32_t SectionNumber,
- const llvm::object::coff_aux_section_definition *Def,
- StringRef LeaderName);
+ readSection(uint32_t sectionNumber,
+ const llvm::object::coff_aux_section_definition *def,
+ StringRef leaderName);
void readAssociativeDefinition(
- COFFSymbolRef COFFSym,
- const llvm::object::coff_aux_section_definition *Def);
+ COFFSymbolRef coffSym,
+ const llvm::object::coff_aux_section_definition *def);
void readAssociativeDefinition(
- COFFSymbolRef COFFSym,
- const llvm::object::coff_aux_section_definition *Def,
- uint32_t ParentSection);
+ COFFSymbolRef coffSym,
+ const llvm::object::coff_aux_section_definition *def,
+ uint32_t parentSection);
void recordPrevailingSymbolForMingw(
- COFFSymbolRef COFFSym,
- llvm::DenseMap<StringRef, uint32_t> &PrevailingSectionMap);
+ COFFSymbolRef coffSym,
+ llvm::DenseMap<StringRef, uint32_t> &prevailingSectionMap);
void maybeAssociateSEHForMingw(
- COFFSymbolRef Sym, const llvm::object::coff_aux_section_definition *Def,
- const llvm::DenseMap<StringRef, uint32_t> &PrevailingSectionMap);
+ COFFSymbolRef sym, const llvm::object::coff_aux_section_definition *def,
+ const llvm::DenseMap<StringRef, uint32_t> &prevailingSectionMap);
+
+ // Given a new symbol Sym with comdat selection Selection, if the new
+ // symbol is not (yet) Prevailing and the existing comdat leader set to
+ // Leader, emits a diagnostic if the new symbol and its selection doesn't
+ // match the existing symbol and its selection. If either old or new
+ // symbol have selection IMAGE_COMDAT_SELECT_LARGEST, Sym might replace
+ // the existing leader. In that case, Prevailing is set to true.
+ void handleComdatSelection(COFFSymbolRef sym,
+ llvm::COFF::COMDATType &selection,
+ bool &prevailing, DefinedRegular *leader);
llvm::Optional<Symbol *>
- createDefined(COFFSymbolRef Sym,
+ createDefined(COFFSymbolRef sym,
std::vector<const llvm::object::coff_aux_section_definition *>
- &ComdatDefs,
- bool &PrevailingComdat);
- Symbol *createRegular(COFFSymbolRef Sym);
- Symbol *createUndefined(COFFSymbolRef Sym);
+ &comdatDefs,
+ bool &prevailingComdat);
+ Symbol *createRegular(COFFSymbolRef sym);
+ Symbol *createUndefined(COFFSymbolRef sym);
- std::unique_ptr<COFFObjectFile> COFFObj;
+ std::unique_ptr<COFFObjectFile> coffObj;
// List of all chunks defined by this file. This includes both section
// chunks and non-section chunks for common symbols.
- std::vector<Chunk *> Chunks;
+ std::vector<Chunk *> chunks;
// CodeView debug info sections.
- std::vector<SectionChunk *> DebugChunks;
+ std::vector<SectionChunk *> debugChunks;
// Chunks containing symbol table indices of exception handlers. Only used for
// 32-bit x86.
- std::vector<SectionChunk *> SXDataChunks;
+ std::vector<SectionChunk *> sXDataChunks;
// Chunks containing symbol table indices of address taken symbols and longjmp
// targets. These are not linked into the final binary when /guard:cf is set.
- std::vector<SectionChunk *> GuardFidChunks;
- std::vector<SectionChunk *> GuardLJmpChunks;
+ std::vector<SectionChunk *> guardFidChunks;
+ std::vector<SectionChunk *> guardLJmpChunks;
// This vector contains the same chunks as Chunks, but they are
// indexed such that you can get a SectionChunk by section index.
// Nonexistent section indices are filled with null pointers.
// (Because section number is 1-based, the first slot is always a
// null pointer.)
- std::vector<SectionChunk *> SparseChunks;
+ std::vector<SectionChunk *> sparseChunks;
// This vector contains a list of all symbols defined or referenced by this
// file. They are indexed such that you can get a Symbol by symbol
// index. Nonexistent indices (which are occupied by auxiliary
// symbols in the real symbol table) are filled with null pointers.
- std::vector<Symbol *> Symbols;
+ std::vector<Symbol *> symbols;
};
// This type represents import library members that contain DLL names
@@ -227,23 +265,23 @@ private:
// for details about the format.
class ImportFile : public InputFile {
public:
- explicit ImportFile(MemoryBufferRef M) : InputFile(ImportKind, M) {}
+ explicit ImportFile(MemoryBufferRef m) : InputFile(ImportKind, m) {}
- static bool classof(const InputFile *F) { return F->kind() == ImportKind; }
+ static bool classof(const InputFile *f) { return f->kind() == ImportKind; }
- static std::vector<ImportFile *> Instances;
+ static std::vector<ImportFile *> instances;
- Symbol *ImpSym = nullptr;
- Symbol *ThunkSym = nullptr;
- std::string DLLName;
+ Symbol *impSym = nullptr;
+ Symbol *thunkSym = nullptr;
+ std::string dllName;
private:
void parse() override;
public:
- StringRef ExternalName;
- const coff_import_header *Hdr;
- Chunk *Location = nullptr;
+ StringRef externalName;
+ const coff_import_header *hdr;
+ Chunk *location = nullptr;
// We want to eliminate dllimported symbols if no one actually refers them.
// These "Live" bits are used to keep track of which import library members
@@ -253,28 +291,31 @@ public:
// symbols provided by this import library member. We also track whether the
// imported symbol is used separately from whether the thunk is used in order
// to avoid creating unnecessary thunks.
- bool Live = !Config->DoGC;
- bool ThunkLive = !Config->DoGC;
+ bool live = !config->doGC;
+ bool thunkLive = !config->doGC;
};
// Used for LTO.
class BitcodeFile : public InputFile {
public:
- explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {}
- static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
- ArrayRef<Symbol *> getSymbols() { return Symbols; }
+ BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
+ uint64_t offsetInArchive);
+ static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
+ ArrayRef<Symbol *> getSymbols() { return symbols; }
MachineTypes getMachineType() override;
- static std::vector<BitcodeFile *> Instances;
- std::unique_ptr<llvm::lto::InputFile> Obj;
+ static std::vector<BitcodeFile *> instances;
+ std::unique_ptr<llvm::lto::InputFile> obj;
private:
void parse() override;
- std::vector<Symbol *> Symbols;
+ std::vector<Symbol *> symbols;
};
+
+std::string replaceThinLTOSuffix(StringRef path);
} // namespace coff
-std::string toString(const coff::InputFile *File);
+std::string toString(const coff::InputFile *file);
} // namespace lld
#endif
diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp
index 92d9ff0937c0..eb3c60d66077 100644
--- a/COFF/LTO.cpp
+++ b/COFF/LTO.cpp
@@ -1,9 +1,8 @@
//===- LTO.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -11,6 +10,7 @@
#include "Config.h"
#include "InputFiles.h"
#include "Symbols.h"
+#include "lld/Common/Args.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
@@ -18,6 +18,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/LTO/Caching.h"
#include "llvm/LTO/Config.h"
@@ -41,112 +42,165 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::coff;
-static std::unique_ptr<lto::LTO> createLTO() {
- lto::Config C;
- C.Options = InitTargetOptionsFromCodeGenFlags();
+// Creates an empty file to and returns a raw_fd_ostream to write to it.
+static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
+ std::error_code ec;
+ auto ret =
+ llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None);
+ if (ec) {
+ error("cannot open " + file + ": " + ec.message());
+ return nullptr;
+ }
+ return ret;
+}
+
+static std::string getThinLTOOutputFile(StringRef path) {
+ return lto::getThinLTOOutputFile(path,
+ config->thinLTOPrefixReplace.first,
+ config->thinLTOPrefixReplace.second);
+}
+
+static lto::Config createConfig() {
+ lto::Config c;
+ c.Options = initTargetOptionsFromCodeGenFlags();
// Always emit a section per function/datum with LTO. LLVM LTO should get most
// of the benefit of linker GC, but there are still opportunities for ICF.
- C.Options.FunctionSections = true;
- C.Options.DataSections = true;
+ c.Options.FunctionSections = true;
+ c.Options.DataSections = true;
// Use static reloc model on 32-bit x86 because it usually results in more
// compact code, and because there are also known code generation bugs when
// using the PIC model (see PR34306).
- if (Config->Machine == COFF::IMAGE_FILE_MACHINE_I386)
- C.RelocModel = Reloc::Static;
+ if (config->machine == COFF::IMAGE_FILE_MACHINE_I386)
+ c.RelocModel = Reloc::Static;
else
- C.RelocModel = Reloc::PIC_;
- C.DisableVerify = true;
- C.DiagHandler = diagnosticHandler;
- C.OptLevel = Config->LTOO;
- C.CPU = GetCPUStr();
- C.MAttrs = GetMAttrs();
-
- if (Config->SaveTemps)
- checkError(C.addSaveTemps(std::string(Config->OutputFile) + ".",
+ c.RelocModel = Reloc::PIC_;
+ c.DisableVerify = true;
+ c.DiagHandler = diagnosticHandler;
+ c.OptLevel = config->ltoo;
+ c.CPU = getCPUStr();
+ c.MAttrs = getMAttrs();
+ c.CGOptLevel = args::getCGOptLevel(config->ltoo);
+
+ if (config->saveTemps)
+ checkError(c.addSaveTemps(std::string(config->outputFile) + ".",
/*UseInputModulePath*/ true));
- lto::ThinBackend Backend;
- if (Config->ThinLTOJobs != 0)
- Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs);
- return llvm::make_unique<lto::LTO>(std::move(C), Backend,
- Config->LTOPartitions);
+ return c;
}
-BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
+BitcodeCompiler::BitcodeCompiler() {
+ // Initialize indexFile.
+ if (!config->thinLTOIndexOnlyArg.empty())
+ indexFile = openFile(config->thinLTOIndexOnlyArg);
+
+ // Initialize ltoObj.
+ lto::ThinBackend backend;
+ if (config->thinLTOIndexOnly) {
+ auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
+ backend = lto::createWriteIndexesThinBackend(
+ config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second,
+ config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
+ } else if (config->thinLTOJobs != 0) {
+ backend = lto::createInProcessThinBackend(config->thinLTOJobs);
+ }
+
+ ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend,
+ config->ltoPartitions);
+}
BitcodeCompiler::~BitcodeCompiler() = default;
-static void undefine(Symbol *S) { replaceSymbol<Undefined>(S, S->getName()); }
+static void undefine(Symbol *s) { replaceSymbol<Undefined>(s, s->getName()); }
+
+void BitcodeCompiler::add(BitcodeFile &f) {
+ lto::InputFile &obj = *f.obj;
+ unsigned symNum = 0;
+ std::vector<Symbol *> symBodies = f.getSymbols();
+ std::vector<lto::SymbolResolution> resols(symBodies.size());
-void BitcodeCompiler::add(BitcodeFile &F) {
- lto::InputFile &Obj = *F.Obj;
- unsigned SymNum = 0;
- std::vector<Symbol *> SymBodies = F.getSymbols();
- std::vector<lto::SymbolResolution> Resols(SymBodies.size());
+ if (config->thinLTOIndexOnly)
+ thinIndices.insert(obj.getName());
// Provide a resolution to the LTO API for each symbol.
- for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
- Symbol *Sym = SymBodies[SymNum];
- lto::SymbolResolution &R = Resols[SymNum];
- ++SymNum;
+ for (const lto::InputFile::Symbol &objSym : obj.symbols()) {
+ Symbol *sym = symBodies[symNum];
+ lto::SymbolResolution &r = resols[symNum];
+ ++symNum;
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
// reports two symbols for module ASM defined. Without this check, lld
// flags an undefined in IR with a definition in ASM as prevailing.
// Once IRObjectFile is fixed to report only one symbol this hack can
// be removed.
- R.Prevailing = !ObjSym.isUndefined() && Sym->getFile() == &F;
- R.VisibleToRegularObj = Sym->IsUsedInRegularObj;
- if (R.Prevailing)
- undefine(Sym);
+ r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f;
+ r.VisibleToRegularObj = sym->isUsedInRegularObj;
+ if (r.Prevailing)
+ undefine(sym);
}
- checkError(LTOObj->add(std::move(F.Obj), Resols));
+ checkError(ltoObj->add(std::move(f.obj), resols));
}
// Merge all the bitcode files we have seen, codegen the result
// and return the resulting objects.
std::vector<StringRef> BitcodeCompiler::compile() {
- unsigned MaxTasks = LTOObj->getMaxTasks();
- Buf.resize(MaxTasks);
- Files.resize(MaxTasks);
+ unsigned maxTasks = ltoObj->getMaxTasks();
+ buf.resize(maxTasks);
+ files.resize(maxTasks);
// The /lldltocache option specifies the path to a directory in which to cache
// native object files for ThinLTO incremental builds. If a path was
// specified, configure LTO to use it as the cache directory.
- lto::NativeObjectCache Cache;
- if (!Config->LTOCache.empty())
- Cache = check(lto::localCache(
- Config->LTOCache, [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
- Files[Task] = std::move(MB);
+ lto::NativeObjectCache cache;
+ if (!config->ltoCache.empty())
+ cache = check(lto::localCache(
+ config->ltoCache, [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
+ files[task] = std::move(mb);
}));
- checkError(LTOObj->run(
- [&](size_t Task) {
+ checkError(ltoObj->run(
+ [&](size_t task) {
return llvm::make_unique<lto::NativeObjectStream>(
- llvm::make_unique<raw_svector_ostream>(Buf[Task]));
+ llvm::make_unique<raw_svector_ostream>(buf[task]));
},
- Cache));
+ cache));
+
+ // Emit empty index files for non-indexed files
+ for (StringRef s : thinIndices) {
+ std::string path = getThinLTOOutputFile(s);
+ openFile(path + ".thinlto.bc");
+ if (config->thinLTOEmitImportsFiles)
+ openFile(path + ".imports");
+ }
+
+ // ThinLTO with index only option is required to generate only the index
+ // files. After that, we exit from linker and ThinLTO backend runs in a
+ // distributed environment.
+ if (config->thinLTOIndexOnly) {
+ if (indexFile)
+ indexFile->close();
+ return {};
+ }
- if (!Config->LTOCache.empty())
- pruneCache(Config->LTOCache, Config->LTOCachePolicy);
+ if (!config->ltoCache.empty())
+ pruneCache(config->ltoCache, config->ltoCachePolicy);
- std::vector<StringRef> Ret;
- for (unsigned I = 0; I != MaxTasks; ++I) {
- if (Buf[I].empty())
+ std::vector<StringRef> ret;
+ for (unsigned i = 0; i != maxTasks; ++i) {
+ if (buf[i].empty())
continue;
- if (Config->SaveTemps) {
- if (I == 0)
- saveBuffer(Buf[I], Config->OutputFile + ".lto.obj");
+ if (config->saveTemps) {
+ if (i == 0)
+ saveBuffer(buf[i], config->outputFile + ".lto.obj");
else
- saveBuffer(Buf[I], Config->OutputFile + Twine(I) + ".lto.obj");
+ saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.obj");
}
- Ret.emplace_back(Buf[I].data(), Buf[I].size());
+ ret.emplace_back(buf[i].data(), buf[i].size());
}
- for (std::unique_ptr<MemoryBuffer> &File : Files)
- if (File)
- Ret.push_back(File->getBuffer());
+ for (std::unique_ptr<MemoryBuffer> &file : files)
+ if (file)
+ ret.push_back(file->getBuffer());
- return Ret;
+ return ret;
}
diff --git a/COFF/LTO.h b/COFF/LTO.h
index f00924654780..2a0cfa061c95 100644
--- a/COFF/LTO.h
+++ b/COFF/LTO.h
@@ -1,9 +1,8 @@
//===- LTO.h ----------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -22,7 +21,9 @@
#define LLD_COFF_LTO_H
#include "lld/Common/LLVM.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <vector>
@@ -43,13 +44,15 @@ public:
BitcodeCompiler();
~BitcodeCompiler();
- void add(BitcodeFile &F);
+ void add(BitcodeFile &f);
std::vector<StringRef> compile();
private:
- std::unique_ptr<llvm::lto::LTO> LTOObj;
- std::vector<SmallString<0>> Buf;
- std::vector<std::unique_ptr<MemoryBuffer>> Files;
+ std::unique_ptr<llvm::lto::LTO> ltoObj;
+ std::vector<SmallString<0>> buf;
+ std::vector<std::unique_ptr<MemoryBuffer>> files;
+ std::unique_ptr<llvm::raw_fd_ostream> indexFile;
+ llvm::DenseSet<StringRef> thinIndices;
};
}
}
diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp
index fd4894250223..f98cf8fa6130 100644
--- a/COFF/MapFile.cpp
+++ b/COFF/MapFile.cpp
@@ -1,9 +1,8 @@
//===- MapFile.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -24,7 +23,7 @@
#include "Symbols.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
-#include "llvm/Support/Parallel.h"
+#include "lld/Common/Threads.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -33,93 +32,93 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::coff;
-typedef DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>
- SymbolMapTy;
+using SymbolMapTy =
+ DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
-static const std::string Indent8 = " "; // 8 spaces
-static const std::string Indent16 = " "; // 16 spaces
+static const std::string indent8 = " "; // 8 spaces
+static const std::string indent16 = " "; // 16 spaces
// Print out the first three columns of a line.
-static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size,
- uint64_t Align) {
- OS << format("%08llx %08llx %5lld ", Addr, Size, Align);
+static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
+ uint64_t align) {
+ os << format("%08llx %08llx %5lld ", addr, size, align);
}
// Returns a list of all symbols that we want to print out.
static std::vector<DefinedRegular *> getSymbols() {
- std::vector<DefinedRegular *> V;
- for (ObjFile *File : ObjFile::Instances)
- for (Symbol *B : File->getSymbols())
- if (auto *Sym = dyn_cast_or_null<DefinedRegular>(B))
- if (Sym && !Sym->getCOFFSymbol().isSectionDefinition())
- V.push_back(Sym);
- return V;
+ std::vector<DefinedRegular *> v;
+ for (ObjFile *file : ObjFile::instances)
+ for (Symbol *b : file->getSymbols())
+ if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
+ if (sym && !sym->getCOFFSymbol().isSectionDefinition())
+ v.push_back(sym);
+ return v;
}
// Returns a map from sections to their symbols.
-static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) {
- SymbolMapTy Ret;
- for (DefinedRegular *S : Syms)
- Ret[S->getChunk()].push_back(S);
+static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
+ SymbolMapTy ret;
+ for (DefinedRegular *s : syms)
+ ret[s->getChunk()].push_back(s);
// Sort symbols by address.
- for (auto &It : Ret) {
- SmallVectorImpl<DefinedRegular *> &V = It.second;
- std::sort(V.begin(), V.end(), [](DefinedRegular *A, DefinedRegular *B) {
- return A->getRVA() < B->getRVA();
+ for (auto &it : ret) {
+ SmallVectorImpl<DefinedRegular *> &v = it.second;
+ std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
+ return a->getRVA() < b->getRVA();
});
}
- return Ret;
+ return ret;
}
// Construct a map from symbols to their stringified representations.
static DenseMap<DefinedRegular *, std::string>
-getSymbolStrings(ArrayRef<DefinedRegular *> Syms) {
- std::vector<std::string> Str(Syms.size());
- for_each_n(parallel::par, (size_t)0, Syms.size(), [&](size_t I) {
- raw_string_ostream OS(Str[I]);
- writeHeader(OS, Syms[I]->getRVA(), 0, 0);
- OS << Indent16 << toString(*Syms[I]);
+getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
+ std::vector<std::string> str(syms.size());
+ parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
+ raw_string_ostream os(str[i]);
+ writeHeader(os, syms[i]->getRVA(), 0, 0);
+ os << indent16 << toString(*syms[i]);
});
- DenseMap<DefinedRegular *, std::string> Ret;
- for (size_t I = 0, E = Syms.size(); I < E; ++I)
- Ret[Syms[I]] = std::move(Str[I]);
- return Ret;
+ DenseMap<DefinedRegular *, std::string> ret;
+ for (size_t i = 0, e = syms.size(); i < e; ++i)
+ ret[syms[i]] = std::move(str[i]);
+ return ret;
}
-void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
- if (Config->MapFile.empty())
+void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
+ if (config->mapFile.empty())
return;
- std::error_code EC;
- raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None);
- if (EC)
- fatal("cannot open " + Config->MapFile + ": " + EC.message());
+ std::error_code ec;
+ raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None);
+ if (ec)
+ fatal("cannot open " + config->mapFile + ": " + ec.message());
// Collect symbol info that we want to print out.
- std::vector<DefinedRegular *> Syms = getSymbols();
- SymbolMapTy SectionSyms = getSectionSyms(Syms);
- DenseMap<DefinedRegular *, std::string> SymStr = getSymbolStrings(Syms);
+ std::vector<DefinedRegular *> syms = getSymbols();
+ SymbolMapTy sectionSyms = getSectionSyms(syms);
+ DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
// Print out the header line.
- OS << "Address Size Align Out In Symbol\n";
+ os << "Address Size Align Out In Symbol\n";
// Print out file contents.
- for (OutputSection *Sec : OutputSections) {
- writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize);
- OS << Sec->Name << '\n';
+ for (OutputSection *sec : outputSections) {
+ writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
+ os << sec->name << '\n';
- for (Chunk *C : Sec->Chunks) {
- auto *SC = dyn_cast<SectionChunk>(C);
- if (!SC)
+ for (Chunk *c : sec->chunks) {
+ auto *sc = dyn_cast<SectionChunk>(c);
+ if (!sc)
continue;
- writeHeader(OS, SC->getRVA(), SC->getSize(), SC->Alignment);
- OS << Indent8 << SC->File->getName() << ":(" << SC->getSectionName()
+ writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment());
+ os << indent8 << sc->file->getName() << ":(" << sc->getSectionName()
<< ")\n";
- for (DefinedRegular *Sym : SectionSyms[SC])
- OS << SymStr[Sym] << '\n';
+ for (DefinedRegular *sym : sectionSyms[sc])
+ os << symStr[sym] << '\n';
}
}
}
diff --git a/COFF/MapFile.h b/COFF/MapFile.h
index 0d0d68ce3ead..2bf01bd07285 100644
--- a/COFF/MapFile.h
+++ b/COFF/MapFile.h
@@ -1,9 +1,8 @@
//===- MapFile.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -15,7 +14,7 @@
namespace lld {
namespace coff {
class OutputSection;
-void writeMapFile(llvm::ArrayRef<OutputSection *> OutputSections);
+void writeMapFile(llvm::ArrayRef<OutputSection *> outputSections);
}
}
diff --git a/COFF/MarkLive.cpp b/COFF/MarkLive.cpp
index 18b1c9c2529f..6d34cb864e3c 100644
--- a/COFF/MarkLive.cpp
+++ b/COFF/MarkLive.cpp
@@ -1,9 +1,8 @@
//===- MarkLive.cpp -------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -16,57 +15,57 @@
namespace lld {
namespace coff {
-static Timer GCTimer("GC", Timer::root());
+static Timer gctimer("GC", Timer::root());
// Set live bit on for each reachable chunk. Unmarked (unreachable)
// COMDAT chunks will be ignored by Writer, so they will be excluded
// from the final output.
-void markLive(ArrayRef<Chunk *> Chunks) {
- ScopedTimer T(GCTimer);
+void markLive(ArrayRef<Chunk *> chunks) {
+ ScopedTimer t(gctimer);
// We build up a worklist of sections which have been marked as live. We only
// push into the worklist when we discover an unmarked section, and we mark
// as we push, so sections never appear twice in the list.
- SmallVector<SectionChunk *, 256> Worklist;
+ SmallVector<SectionChunk *, 256> worklist;
// COMDAT section chunks are dead by default. Add non-COMDAT chunks.
- for (Chunk *C : Chunks)
- if (auto *SC = dyn_cast<SectionChunk>(C))
- if (SC->Live)
- Worklist.push_back(SC);
+ for (Chunk *c : chunks)
+ if (auto *sc = dyn_cast<SectionChunk>(c))
+ if (sc->live)
+ worklist.push_back(sc);
- auto Enqueue = [&](SectionChunk *C) {
- if (C->Live)
+ auto enqueue = [&](SectionChunk *c) {
+ if (c->live)
return;
- C->Live = true;
- Worklist.push_back(C);
+ c->live = true;
+ worklist.push_back(c);
};
- auto AddSym = [&](Symbol *B) {
- if (auto *Sym = dyn_cast<DefinedRegular>(B))
- Enqueue(Sym->getChunk());
- else if (auto *Sym = dyn_cast<DefinedImportData>(B))
- Sym->File->Live = true;
- else if (auto *Sym = dyn_cast<DefinedImportThunk>(B))
- Sym->WrappedSym->File->Live = Sym->WrappedSym->File->ThunkLive = true;
+ auto addSym = [&](Symbol *b) {
+ if (auto *sym = dyn_cast<DefinedRegular>(b))
+ enqueue(sym->getChunk());
+ else if (auto *sym = dyn_cast<DefinedImportData>(b))
+ sym->file->live = true;
+ else if (auto *sym = dyn_cast<DefinedImportThunk>(b))
+ sym->wrappedSym->file->live = sym->wrappedSym->file->thunkLive = true;
};
// Add GC root chunks.
- for (Symbol *B : Config->GCRoot)
- AddSym(B);
+ for (Symbol *b : config->gcroot)
+ addSym(b);
- while (!Worklist.empty()) {
- SectionChunk *SC = Worklist.pop_back_val();
- assert(SC->Live && "We mark as live when pushing onto the worklist!");
+ while (!worklist.empty()) {
+ SectionChunk *sc = worklist.pop_back_val();
+ assert(sc->live && "We mark as live when pushing onto the worklist!");
// Mark all symbols listed in the relocation table for this section.
- for (Symbol *B : SC->symbols())
- if (B)
- AddSym(B);
+ for (Symbol *b : sc->symbols())
+ if (b)
+ addSym(b);
// Mark associative sections if any.
- for (SectionChunk *C : SC->children())
- Enqueue(C);
+ for (SectionChunk &c : sc->children())
+ enqueue(&c);
}
}
diff --git a/COFF/MarkLive.h b/COFF/MarkLive.h
index 5b652dd48196..e4e4c31c7c79 100644
--- a/COFF/MarkLive.h
+++ b/COFF/MarkLive.h
@@ -1,9 +1,8 @@
//===- MarkLive.h -----------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -16,7 +15,9 @@
namespace lld {
namespace coff {
-void markLive(ArrayRef<Chunk *> Chunks);
+class Chunk;
+
+void markLive(ArrayRef<Chunk *> chunks);
} // namespace coff
} // namespace lld
diff --git a/COFF/MinGW.cpp b/COFF/MinGW.cpp
index b2c8c4eadca4..2ca8ca0c058c 100644
--- a/COFF/MinGW.cpp
+++ b/COFF/MinGW.cpp
@@ -1,9 +1,8 @@
//===- MinGW.cpp ----------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -19,8 +18,35 @@ using namespace lld::coff;
using namespace llvm;
using namespace llvm::COFF;
-void AutoExporter::initSymbolExcludes() {
- ExcludeSymbolPrefixes = {
+AutoExporter::AutoExporter() {
+ excludeLibs = {
+ "libgcc",
+ "libgcc_s",
+ "libstdc++",
+ "libmingw32",
+ "libmingwex",
+ "libg2c",
+ "libsupc++",
+ "libobjc",
+ "libgcj",
+ "libclang_rt.builtins",
+ "libclang_rt.builtins-aarch64",
+ "libclang_rt.builtins-arm",
+ "libclang_rt.builtins-i386",
+ "libclang_rt.builtins-x86_64",
+ "libc++",
+ "libc++abi",
+ "libunwind",
+ "libmsvcrt",
+ "libucrtbase",
+ };
+
+ excludeObjects = {
+ "crt0.o", "crt1.o", "crt1u.o", "crt2.o", "crt2u.o", "dllcrt1.o",
+ "dllcrt2.o", "gcrt0.o", "gcrt1.o", "gcrt2.o", "crtbegin.o", "crtend.o",
+ };
+
+ excludeSymbolPrefixes = {
// Import symbols
"__imp_",
"__IMPORT_DESCRIPTOR_",
@@ -32,12 +58,14 @@ void AutoExporter::initSymbolExcludes() {
// Artifical symbols such as .refptr
".",
};
- ExcludeSymbolSuffixes = {
+
+ excludeSymbolSuffixes = {
"_iname",
"_NULL_THUNK_DATA",
};
- if (Config->Machine == I386) {
- ExcludeSymbols = {
+
+ if (config->machine == I386) {
+ excludeSymbols = {
"__NULL_IMPORT_DESCRIPTOR",
"__pei386_runtime_relocator",
"_do_pseudo_reloc",
@@ -52,9 +80,9 @@ void AutoExporter::initSymbolExcludes() {
"_DllEntryPoint@12",
"_DllMainCRTStartup@12",
};
- ExcludeSymbolPrefixes.insert("__head_");
+ excludeSymbolPrefixes.insert("__head_");
} else {
- ExcludeSymbols = {
+ excludeSymbols = {
"__NULL_IMPORT_DESCRIPTOR",
"_pei386_runtime_relocator",
"do_pseudo_reloc",
@@ -69,108 +97,70 @@ void AutoExporter::initSymbolExcludes() {
"DllEntryPoint",
"DllMainCRTStartup",
};
- ExcludeSymbolPrefixes.insert("_head_");
+ excludeSymbolPrefixes.insert("_head_");
}
}
-AutoExporter::AutoExporter() {
- ExcludeLibs = {
- "libgcc",
- "libgcc_s",
- "libstdc++",
- "libmingw32",
- "libmingwex",
- "libg2c",
- "libsupc++",
- "libobjc",
- "libgcj",
- "libclang_rt.builtins",
- "libclang_rt.builtins-aarch64",
- "libclang_rt.builtins-arm",
- "libclang_rt.builtins-i386",
- "libclang_rt.builtins-x86_64",
- "libc++",
- "libc++abi",
- "libunwind",
- "libmsvcrt",
- "libucrtbase",
- };
- ExcludeObjects = {
- "crt0.o",
- "crt1.o",
- "crt1u.o",
- "crt2.o",
- "crt2u.o",
- "dllcrt1.o",
- "dllcrt2.o",
- "gcrt0.o",
- "gcrt1.o",
- "gcrt2.o",
- "crtbegin.o",
- "crtend.o",
- };
-}
-
-void AutoExporter::addWholeArchive(StringRef Path) {
- StringRef LibName = sys::path::filename(Path);
+void AutoExporter::addWholeArchive(StringRef path) {
+ StringRef libName = sys::path::filename(path);
// Drop the file extension, to match the processing below.
- LibName = LibName.substr(0, LibName.rfind('.'));
- ExcludeLibs.erase(LibName);
+ libName = libName.substr(0, libName.rfind('.'));
+ excludeLibs.erase(libName);
}
-bool AutoExporter::shouldExport(Defined *Sym) const {
- if (!Sym || !Sym->isLive() || !Sym->getChunk())
+bool AutoExporter::shouldExport(Defined *sym) const {
+ if (!sym || !sym->isLive() || !sym->getChunk())
return false;
// Only allow the symbol kinds that make sense to export; in particular,
// disallow import symbols.
- if (!isa<DefinedRegular>(Sym) && !isa<DefinedCommon>(Sym))
+ if (!isa<DefinedRegular>(sym) && !isa<DefinedCommon>(sym))
return false;
- if (ExcludeSymbols.count(Sym->getName()))
+ if (excludeSymbols.count(sym->getName()))
return false;
- for (StringRef Prefix : ExcludeSymbolPrefixes.keys())
- if (Sym->getName().startswith(Prefix))
+ for (StringRef prefix : excludeSymbolPrefixes.keys())
+ if (sym->getName().startswith(prefix))
return false;
- for (StringRef Suffix : ExcludeSymbolSuffixes.keys())
- if (Sym->getName().endswith(Suffix))
+ for (StringRef suffix : excludeSymbolSuffixes.keys())
+ if (sym->getName().endswith(suffix))
return false;
// If a corresponding __imp_ symbol exists and is defined, don't export it.
- if (Symtab->find(("__imp_" + Sym->getName()).str()))
+ if (symtab->find(("__imp_" + sym->getName()).str()))
return false;
// Check that file is non-null before dereferencing it, symbols not
// originating in regular object files probably shouldn't be exported.
- if (!Sym->getFile())
+ if (!sym->getFile())
return false;
- StringRef LibName = sys::path::filename(Sym->getFile()->ParentName);
+ StringRef libName = sys::path::filename(sym->getFile()->parentName);
// Drop the file extension.
- LibName = LibName.substr(0, LibName.rfind('.'));
- if (!LibName.empty())
- return !ExcludeLibs.count(LibName);
+ libName = libName.substr(0, libName.rfind('.'));
+ if (!libName.empty())
+ return !excludeLibs.count(libName);
- StringRef FileName = sys::path::filename(Sym->getFile()->getName());
- return !ExcludeObjects.count(FileName);
+ StringRef fileName = sys::path::filename(sym->getFile()->getName());
+ return !excludeObjects.count(fileName);
}
-void coff::writeDefFile(StringRef Name) {
- std::error_code EC;
- raw_fd_ostream OS(Name, EC, sys::fs::F_None);
- if (EC)
- fatal("cannot open " + Name + ": " + EC.message());
-
- OS << "EXPORTS\n";
- for (Export &E : Config->Exports) {
- OS << " " << E.ExportName << " "
- << "@" << E.Ordinal;
- if (auto *Def = dyn_cast_or_null<Defined>(E.Sym)) {
- if (Def && Def->getChunk() &&
- !(Def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
- OS << " DATA";
+void coff::writeDefFile(StringRef name) {
+ std::error_code ec;
+ raw_fd_ostream os(name, ec, sys::fs::F_None);
+ if (ec)
+ fatal("cannot open " + name + ": " + ec.message());
+
+ os << "EXPORTS\n";
+ for (Export &e : config->exports) {
+ os << " " << e.exportName << " "
+ << "@" << e.ordinal;
+ if (auto *def = dyn_cast_or_null<Defined>(e.sym)) {
+ if (def && def->getChunk() &&
+ !(def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
+ os << " DATA";
}
- OS << "\n";
+ os << "\n";
}
}
diff --git a/COFF/MinGW.h b/COFF/MinGW.h
index f9c5e3e5c2cc..578a277561ad 100644
--- a/COFF/MinGW.h
+++ b/COFF/MinGW.h
@@ -1,9 +1,8 @@
//===- MinGW.h --------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -23,20 +22,18 @@ class AutoExporter {
public:
AutoExporter();
- void initSymbolExcludes();
-
- void addWholeArchive(StringRef Path);
+ void addWholeArchive(StringRef path);
- llvm::StringSet<> ExcludeSymbols;
- llvm::StringSet<> ExcludeSymbolPrefixes;
- llvm::StringSet<> ExcludeSymbolSuffixes;
- llvm::StringSet<> ExcludeLibs;
- llvm::StringSet<> ExcludeObjects;
+ llvm::StringSet<> excludeSymbols;
+ llvm::StringSet<> excludeSymbolPrefixes;
+ llvm::StringSet<> excludeSymbolSuffixes;
+ llvm::StringSet<> excludeLibs;
+ llvm::StringSet<> excludeObjects;
- bool shouldExport(Defined *Sym) const;
+ bool shouldExport(Defined *sym) const;
};
-void writeDefFile(StringRef Name);
+void writeDefFile(StringRef name);
} // namespace coff
} // namespace lld
diff --git a/COFF/Options.td b/COFF/Options.td
index acf1bc5c8b1d..024b7be8f78d 100644
--- a/COFF/Options.td
+++ b/COFF/Options.td
@@ -3,11 +3,11 @@ include "llvm/Option/OptParser.td"
// link.exe accepts options starting with either a dash or a slash.
// Flag that takes no arguments.
-class F<string name> : Flag<["/", "-", "-?"], name>;
+class F<string name> : Flag<["/", "-", "/?", "-?"], name>;
// Flag that takes one argument after ":".
class P<string name, string help> :
- Joined<["/", "-", "-?"], name#":">, HelpText<help>;
+ Joined<["/", "-", "/?", "-?"], name#":">, HelpText<help>;
// Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the
// flag on and using it suffixed by ":no" turns it off.
@@ -32,6 +32,9 @@ def errorlimit : P<"errorlimit",
def export : P<"export", "Export a function">;
// No help text because /failifmismatch is not intended to be used by the user.
def failifmismatch : P<"failifmismatch", "">;
+def filealign : P<"filealign", "Section alignment in the output file">;
+def functionpadmin : F<"functionpadmin">;
+def functionpadmin_opt : P<"functionpadmin", "Prepares an image for hotpatching">;
def guard : P<"guard", "Control flow guard">;
def heap : P<"heap", "Size of the heap">;
def ignore : P<"ignore", "Specify warning codes to ignore">;
@@ -64,7 +67,8 @@ def timestamp : P<"timestamp", "Specify the PE header timestamp">;
def version : P<"version", "Specify a version number in the PE header">;
def wholearchive_file : P<"wholearchive", "Include all object files from this archive">;
-def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>;
+def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">,
+ Alias<nodefaultlib>;
def manifest : F<"manifest">, HelpText<"Create .manifest file">;
def manifest_colon : P<
@@ -82,11 +86,11 @@ def manifestinput : P<
// We cannot use multiclass P because class name "incl" is different
// from its command line option name. We do this because "include" is
// a reserved keyword in tablegen.
-def incl : Joined<["/", "-"], "include:">,
+def incl : Joined<["/", "-", "/?", "-?"], "include:">,
HelpText<"Force symbol to be added to symbol table as undefined one">;
// "def" is also a keyword.
-def deffile : Joined<["/", "-"], "def:">,
+def deffile : Joined<["/", "-", "/?", "-?"], "def:">,
HelpText<"Use module-definition file">;
def debug : F<"debug">, HelpText<"Embed a symbol table in the image">;
@@ -101,8 +105,12 @@ def noentry : F<"noentry">,
def profile : F<"profile">;
def repro : F<"Brepro">,
HelpText<"Use a hash of the executable as the PE header timestamp">;
-def swaprun_cd : F<"swaprun:cd">;
-def swaprun_net : F<"swaprun:net">;
+def swaprun : P<"swaprun",
+ "Comma-separated list of 'cd' or 'net'">;
+def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>,
+ HelpText<"Make loader run output binary from swap instead of from CD">;
+def swaprun_net : F<"swaprun:net">, Alias<swaprun>, AliasArgs<["net"]>,
+ HelpText<"Make loader run output binary from swap instead of from network">;
def verbose : F<"verbose">;
def wholearchive_flag : F<"wholearchive">;
@@ -112,6 +120,8 @@ def force_unresolved : F<"force:unresolved">,
HelpText<"Allow undefined symbols when creating executables">;
def force_multiple : F<"force:multiple">,
HelpText<"Allow multiply defined symbols when creating executables">;
+def force_multipleres : F<"force:multipleres">,
+ HelpText<"Allow multiply defined resources when creating executables">;
defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">;
defm allowbind : B<"allowbind", "Enable DLL binding (default)",
@@ -147,37 +157,58 @@ defm tsaware : B<"tsaware",
"Create non-Terminal Server aware executable">;
def help : F<"help">;
-def help_q : Flag<["/?", "-?"], "">, Alias<help>;
+
+// /?? and -?? must be before /? and -? to not confuse lib/Options.
+def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
// LLD extensions
+def exclude_all_symbols : F<"exclude-all-symbols">;
def export_all_symbols : F<"export-all-symbols">;
+defm demangle : B<"demangle",
+ "Demangle symbols in output (default)",
+ "Do not demangle symbols in output">;
+def include_optional : Joined<["/", "-", "/?", "-?"], "includeoptional:">,
+ HelpText<"Add symbol as undefined, but allow it to remain undefined">;
def kill_at : F<"kill-at">;
def lldmingw : F<"lldmingw">;
-def output_def : Joined<["/", "-"], "output-def:">;
+def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">;
def pdb_source_path : P<"pdbsourcepath",
"Base path used to make relative source file path absolute in PDB">;
def rsp_quoting : Joined<["--"], "rsp-quoting=">,
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
+def thinlto_emit_imports_files :
+ F<"thinlto-emit-imports-files">,
+ HelpText<"Emit .imports files with -thinlto-index-only">;
+def thinlto_index_only :
+ F<"thinlto-index-only">,
+ HelpText<"Instead of linking, emit ThinLTO index files">;
+def thinlto_index_only_arg : P<
+ "thinlto-index-only",
+ "-thinlto-index-only and also write native module names to file">;
+def thinlto_object_suffix_replace : P<
+ "thinlto-object-suffix-replace",
+ "'old;new' replace old suffix with new suffix in ThinLTO index">;
+def thinlto_prefix_replace: P<
+ "thinlto-prefix-replace",
+ "'old;new' replace old prefix with new prefix in ThinLTO outputs">;
def dash_dash_version : Flag<["--"], "version">,
HelpText<"Print version information">;
+defm threads: B<"threads",
+ "Run the linker multi-threaded (default)",
+ "Do not run the linker multi-threaded">;
// Flags for debugging
def lldmap : F<"lldmap">;
-def lldmap_file : Joined<["/", "-"], "lldmap:">;
+def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">;
def show_timing : F<"time">;
+def summary : F<"summary">;
//==============================================================================
// The flags below do nothing. They are defined only for link.exe compatibility.
//==============================================================================
-class QF<string name> : Joined<["/", "-", "-?"], name#":">;
+class QF<string name> : Joined<["/", "-", "/?", "-?"], name#":">;
-multiclass QB<string name> {
- def "" : F<name>;
- def _no : F<name#":no">;
-}
-
-def functionpadmin : F<"functionpadmin">;
def ignoreidl : F<"ignoreidl">;
def nologo : F<"nologo">;
def throwingnew : F<"throwingnew">;
diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp
index 7862b6ce4cc5..a55e5136e040 100644
--- a/COFF/PDB.cpp
+++ b/COFF/PDB.cpp
@@ -1,21 +1,23 @@
//===- PDB.cpp ------------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "PDB.h"
#include "Chunks.h"
#include "Config.h"
+#include "DebugTypes.h"
#include "Driver.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "TypeMerger.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Timer.h"
+#include "lld/Common/Threads.h"
#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
@@ -53,7 +55,6 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JamCRC.h"
-#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
#include <memory>
@@ -65,44 +66,34 @@ using namespace llvm::codeview;
using llvm::object::coff_section;
-static ExitOnError ExitOnErr;
+static ExitOnError exitOnErr;
-static Timer TotalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
+static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
-static Timer AddObjectsTimer("Add Objects", TotalPdbLinkTimer);
-static Timer TypeMergingTimer("Type Merging", AddObjectsTimer);
-static Timer SymbolMergingTimer("Symbol Merging", AddObjectsTimer);
-static Timer GlobalsLayoutTimer("Globals Stream Layout", TotalPdbLinkTimer);
-static Timer TpiStreamLayoutTimer("TPI Stream Layout", TotalPdbLinkTimer);
-static Timer DiskCommitTimer("Commit to Disk", TotalPdbLinkTimer);
+static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer);
+static Timer typeMergingTimer("Type Merging", addObjectsTimer);
+static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer);
+static Timer globalsLayoutTimer("Globals Stream Layout", totalPdbLinkTimer);
+static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer);
+static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer);
namespace {
-/// Map from type index and item index in a type server PDB to the
-/// corresponding index in the destination PDB.
-struct CVIndexMap {
- SmallVector<TypeIndex, 0> TPIMap;
- SmallVector<TypeIndex, 0> IPIMap;
- bool IsTypeServerMap = false;
- bool IsPrecompiledTypeMap = false;
-};
-
class DebugSHandler;
class PDBLinker {
friend DebugSHandler;
public:
- PDBLinker(SymbolTable *Symtab)
- : Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc),
- IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) {
+ PDBLinker(SymbolTable *symtab)
+ : alloc(), symtab(symtab), builder(alloc), tMerger(alloc) {
// This isn't strictly necessary, but link.exe usually puts an empty string
// as the first "valid" string in the string table, so we do the same in
// order to maintain as much byte-for-byte compatibility as possible.
- PDBStrTab.insert("");
+ pdbStrTab.insert("");
}
/// Emit the basic PDB structure: initial streams, headers, etc.
- void initialize(llvm::codeview::DebugInfo *BuildId);
+ void initialize(llvm::codeview::DebugInfo *buildId);
/// Add natvis files specified on the command line.
void addNatvisFiles();
@@ -110,10 +101,13 @@ public:
/// Link CodeView from each object file in the symbol table into the PDB.
void addObjectsToPDB();
+ /// Link info for each import file in the symbol table into the PDB.
+ void addImportFilesToPDB(ArrayRef<OutputSection *> outputSections);
+
/// Link CodeView from a single object file into the target (output) PDB.
/// When a precompiled headers object is linked, its TPI map might be provided
/// externally.
- void addObjFile(ObjFile *File, CVIndexMap *ExternIndexMap = nullptr);
+ void addObjFile(ObjFile *file, CVIndexMap *externIndexMap = nullptr);
/// Produce a mapping from the type and item indices used in the object
/// file to those in the destination PDB.
@@ -125,20 +119,17 @@ public:
///
/// If the object does not use a type server PDB (compiled with /Z7), we merge
/// all the type and item records from the .debug$S stream and fill in the
- /// caller-provided ObjectIndexMap.
- Expected<const CVIndexMap &> mergeDebugT(ObjFile *File,
- CVIndexMap *ObjectIndexMap);
+ /// caller-provided objectIndexMap.
+ Expected<const CVIndexMap &> mergeDebugT(ObjFile *file,
+ CVIndexMap *objectIndexMap);
/// Reads and makes available a PDB.
- Expected<const CVIndexMap &> maybeMergeTypeServerPDB(ObjFile *File,
- const CVType &FirstType);
+ Expected<const CVIndexMap &> maybeMergeTypeServerPDB(ObjFile *file);
/// Merges a precompiled headers TPI map into the current TPI map. The
/// precompiled headers object will also be loaded and remapped in the
/// process.
- Expected<const CVIndexMap &>
- mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType,
- CVIndexMap *ObjectIndexMap);
+ Error mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap);
/// Reads and makes available a precompiled headers object.
///
@@ -149,109 +140,87 @@ public:
///
/// If the precompiled headers object was already loaded, this function will
/// simply return its (remapped) TPI map.
- Expected<const CVIndexMap &> aquirePrecompObj(ObjFile *File,
- PrecompRecord Precomp);
+ Expected<const CVIndexMap &> aquirePrecompObj(ObjFile *file);
/// Adds a precompiled headers object signature -> TPI mapping.
std::pair<CVIndexMap &, bool /*already there*/>
- registerPrecompiledHeaders(uint32_t Signature);
+ registerPrecompiledHeaders(uint32_t signature);
- void mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap,
- std::vector<ulittle32_t *> &StringTableRefs,
- BinaryStreamRef SymData);
+ void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
+ std::vector<ulittle32_t *> &stringTableRefs,
+ BinaryStreamRef symData);
/// Add the section map and section contributions to the PDB.
- void addSections(ArrayRef<OutputSection *> OutputSections,
- ArrayRef<uint8_t> SectionTable);
-
- /// Get the type table or the global type table if /DEBUG:GHASH is enabled.
- TypeCollection &getTypeTable() {
- if (Config->DebugGHashes)
- return GlobalTypeTable;
- return TypeTable;
- }
-
- /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled.
- TypeCollection &getIDTable() {
- if (Config->DebugGHashes)
- return GlobalIDTable;
- return IDTable;
- }
+ void addSections(ArrayRef<OutputSection *> outputSections,
+ ArrayRef<uint8_t> sectionTable);
/// Write the PDB to disk and store the Guid generated for it in *Guid.
- void commit(codeview::GUID *Guid);
-
-private:
- BumpPtrAllocator Alloc;
-
- SymbolTable *Symtab;
+ void commit(codeview::GUID *guid);
- pdb::PDBFileBuilder Builder;
+ // Print statistics regarding the final PDB
+ void printStats();
- /// Type records that will go into the PDB TPI stream.
- MergingTypeTableBuilder TypeTable;
+private:
+ BumpPtrAllocator alloc;
- /// Item records that will go into the PDB IPI stream.
- MergingTypeTableBuilder IDTable;
+ SymbolTable *symtab;
- /// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH)
- GlobalTypeTableBuilder GlobalTypeTable;
+ pdb::PDBFileBuilder builder;
- /// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH)
- GlobalTypeTableBuilder GlobalIDTable;
+ TypeMerger tMerger;
/// PDBs use a single global string table for filenames in the file checksum
/// table.
- DebugStringTableSubsection PDBStrTab;
-
- llvm::SmallString<128> NativePath;
+ DebugStringTableSubsection pdbStrTab;
- /// A list of other PDBs which are loaded during the linking process and which
- /// we need to keep around since the linking operation may reference pointers
- /// inside of these PDBs.
- llvm::SmallVector<std::unique_ptr<pdb::NativeSession>, 2> LoadedPDBs;
+ llvm::SmallString<128> nativePath;
- std::vector<pdb::SecMapEntry> SectionMap;
+ std::vector<pdb::SecMapEntry> sectionMap;
/// Type index mappings of type server PDBs that we've loaded so far.
- std::map<codeview::GUID, CVIndexMap> TypeServerIndexMappings;
+ std::map<codeview::GUID, CVIndexMap> typeServerIndexMappings;
/// Type index mappings of precompiled objects type map that we've loaded so
/// far.
- std::map<uint32_t, CVIndexMap> PrecompTypeIndexMappings;
+ std::map<uint32_t, CVIndexMap> precompTypeIndexMappings;
- /// List of TypeServer PDBs which cannot be loaded.
- /// Cached to prevent repeated load attempts.
- std::map<codeview::GUID, std::string> MissingTypeServerPDBs;
+ // For statistics
+ uint64_t globalSymbols = 0;
+ uint64_t moduleSymbols = 0;
+ uint64_t publicSymbols = 0;
};
class DebugSHandler {
- PDBLinker &Linker;
+ PDBLinker &linker;
/// The object file whose .debug$S sections we're processing.
- ObjFile &File;
+ ObjFile &file;
/// The result of merging type indices.
- const CVIndexMap &IndexMap;
+ const CVIndexMap &indexMap;
/// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by
/// index from other records in the .debug$S section. All of these strings
/// need to be added to the global PDB string table, and all references to
/// these strings need to have their indices re-written to refer to the
/// global PDB string table.
- DebugStringTableSubsectionRef CVStrTab;
+ DebugStringTableSubsectionRef cVStrTab;
/// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to
/// by other records in the .debug$S section and need to be merged into the
/// PDB.
- DebugChecksumsSubsectionRef Checksums;
+ DebugChecksumsSubsectionRef checksums;
+
+ /// The DEBUG_S_INLINEELINES subsection. There can be only one of these per
+ /// object file.
+ DebugInlineeLinesSubsectionRef inlineeLines;
/// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of
/// these and they need not appear in any specific order. However, they
/// contain string table references which need to be re-written, so we
/// collect them all here and re-write them after all subsections have been
/// discovered and processed.
- std::vector<DebugFrameDataSubsectionRef> NewFpoFrames;
+ std::vector<DebugFrameDataSubsectionRef> newFpoFrames;
/// Pointers to raw memory that we determine have string table references
/// that need to be re-written. We first process all .debug$S subsections
@@ -259,13 +228,17 @@ class DebugSHandler {
/// up this list as we go. At the end, we use the string table (which must
/// have been discovered by now else it is an error) to re-write these
/// references.
- std::vector<ulittle32_t *> StringTableReferences;
+ std::vector<ulittle32_t *> stringTableReferences;
public:
- DebugSHandler(PDBLinker &Linker, ObjFile &File, const CVIndexMap &IndexMap)
- : Linker(Linker), File(File), IndexMap(IndexMap) {}
+ DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap &indexMap)
+ : linker(linker), file(file), indexMap(indexMap) {}
+
+ void handleDebugS(lld::coff::SectionChunk &debugS);
+
+ std::shared_ptr<DebugInlineeLinesSubsection>
+ mergeInlineeLines(DebugChecksumsSubsection *newChecksums);
- void handleDebugS(lld::coff::SectionChunk &DebugS);
void finish();
};
}
@@ -273,7 +246,7 @@ public:
// Visual Studio's debugger requires absolute paths in various places in the
// PDB to work without additional configuration:
// https://docs.microsoft.com/en-us/visualstudio/debugger/debug-source-files-common-properties-solution-property-pages-dialog-box
-static void pdbMakeAbsolute(SmallVectorImpl<char> &FileName) {
+static void pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
// The default behavior is to produce paths that are valid within the context
// of the machine that you perform the link on. If the linker is running on
// a POSIX system, we will output absolute POSIX paths. If the linker is
@@ -281,408 +254,256 @@ static void pdbMakeAbsolute(SmallVectorImpl<char> &FileName) {
// user desires any other kind of behavior, they should explicitly pass
// /pdbsourcepath, in which case we will treat the exact string the user
// passed in as the gospel and not normalize, canonicalize it.
- if (sys::path::is_absolute(FileName, sys::path::Style::windows) ||
- sys::path::is_absolute(FileName, sys::path::Style::posix))
+ if (sys::path::is_absolute(fileName, sys::path::Style::windows) ||
+ sys::path::is_absolute(fileName, sys::path::Style::posix))
return;
// It's not absolute in any path syntax. Relative paths necessarily refer to
// the local file system, so we can make it native without ending up with a
// nonsensical path.
- sys::path::native(FileName);
- if (Config->PDBSourcePath.empty()) {
- sys::fs::make_absolute(FileName);
+ if (config->pdbSourcePath.empty()) {
+ sys::path::native(fileName);
+ sys::fs::make_absolute(fileName);
return;
}
- // Only apply native and dot removal to the relative file path. We want to
- // leave the path the user specified untouched since we assume they specified
- // it for a reason.
- sys::path::remove_dots(FileName, /*remove_dot_dots=*/true);
-
- SmallString<128> AbsoluteFileName = Config->PDBSourcePath;
- sys::path::append(AbsoluteFileName, FileName);
- FileName = std::move(AbsoluteFileName);
-}
-
-static SectionChunk *findByName(ArrayRef<SectionChunk *> Sections,
- StringRef Name) {
- for (SectionChunk *C : Sections)
- if (C->getSectionName() == Name)
- return C;
- return nullptr;
-}
-static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> Data,
- StringRef SecName) {
- // First 4 bytes are section magic.
- if (Data.size() < 4)
- fatal(SecName + " too short");
- if (support::endian::read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC)
- fatal(SecName + " has an invalid magic");
- return Data.slice(4);
-}
-
-static ArrayRef<uint8_t> getDebugSection(ObjFile *File, StringRef SecName) {
- if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName))
- return consumeDebugMagic(Sec->getContents(), SecName);
- return {};
+ // Try to guess whether /PDBSOURCEPATH is a unix path or a windows path.
+ // Since PDB's are more of a Windows thing, we make this conservative and only
+ // decide that it's a unix path if we're fairly certain. Specifically, if
+ // it starts with a forward slash.
+ SmallString<128> absoluteFileName = config->pdbSourcePath;
+ sys::path::Style guessedStyle = absoluteFileName.startswith("/")
+ ? sys::path::Style::posix
+ : sys::path::Style::windows;
+ sys::path::append(absoluteFileName, guessedStyle, fileName);
+ sys::path::native(absoluteFileName, guessedStyle);
+ sys::path::remove_dots(absoluteFileName, true, guessedStyle);
+
+ fileName = std::move(absoluteFileName);
}
// A COFF .debug$H section is currently a clang extension. This function checks
// if a .debug$H section is in a format that we expect / understand, so that we
// can ignore any sections which are coincidentally also named .debug$H but do
// not contain a format we recognize.
-static bool canUseDebugH(ArrayRef<uint8_t> DebugH) {
- if (DebugH.size() < sizeof(object::debug_h_header))
+static bool canUseDebugH(ArrayRef<uint8_t> debugH) {
+ if (debugH.size() < sizeof(object::debug_h_header))
return false;
- auto *Header =
- reinterpret_cast<const object::debug_h_header *>(DebugH.data());
- DebugH = DebugH.drop_front(sizeof(object::debug_h_header));
- return Header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
- Header->Version == 0 &&
- Header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) &&
- (DebugH.size() % 8 == 0);
+ auto *header =
+ reinterpret_cast<const object::debug_h_header *>(debugH.data());
+ debugH = debugH.drop_front(sizeof(object::debug_h_header));
+ return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
+ header->Version == 0 &&
+ header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) &&
+ (debugH.size() % 8 == 0);
}
-static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *File) {
- SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$H");
- if (!Sec)
+static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *file) {
+ SectionChunk *sec =
+ SectionChunk::findByName(file->getDebugChunks(), ".debug$H");
+ if (!sec)
return llvm::None;
- ArrayRef<uint8_t> Contents = Sec->getContents();
- if (!canUseDebugH(Contents))
+ ArrayRef<uint8_t> contents = sec->getContents();
+ if (!canUseDebugH(contents))
return None;
- return Contents;
+ return contents;
}
static ArrayRef<GloballyHashedType>
-getHashesFromDebugH(ArrayRef<uint8_t> DebugH) {
- assert(canUseDebugH(DebugH));
+getHashesFromDebugH(ArrayRef<uint8_t> debugH) {
+ assert(canUseDebugH(debugH));
- DebugH = DebugH.drop_front(sizeof(object::debug_h_header));
- uint32_t Count = DebugH.size() / sizeof(GloballyHashedType);
- return {reinterpret_cast<const GloballyHashedType *>(DebugH.data()), Count};
+ debugH = debugH.drop_front(sizeof(object::debug_h_header));
+ uint32_t count = debugH.size() / sizeof(GloballyHashedType);
+ return {reinterpret_cast<const GloballyHashedType *>(debugH.data()), count};
}
-static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
- TypeCollection &TypeTable) {
+static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder,
+ TypeCollection &typeTable) {
// Start the TPI or IPI stream header.
- TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
+ tpiBuilder.setVersionHeader(pdb::PdbTpiV80);
// Flatten the in memory type table and hash each type.
- TypeTable.ForEachRecord([&](TypeIndex TI, const CVType &Type) {
- auto Hash = pdb::hashTypeRecord(Type);
- if (auto E = Hash.takeError())
+ typeTable.ForEachRecord([&](TypeIndex ti, const CVType &type) {
+ auto hash = pdb::hashTypeRecord(type);
+ if (auto e = hash.takeError())
fatal("type hashing error");
- TpiBuilder.addTypeRecord(Type.RecordData, *Hash);
- });
-}
-
-// OBJs usually start their symbol stream with a S_OBJNAME record. This record
-// also contains the signature/key of the current PCH session. The signature
-// must be same for all objects which depend on the precompiled object.
-// Recompiling the precompiled headers will generate a new PCH key and thus
-// invalidate all the dependent objects.
-static uint32_t extractPCHSignature(ObjFile *File) {
- auto DbgIt = find_if(File->getDebugChunks(), [](SectionChunk *C) {
- return C->getSectionName() == ".debug$S";
+ tpiBuilder.addTypeRecord(type.RecordData, *hash);
});
- if (!DbgIt)
- return 0;
-
- ArrayRef<uint8_t> Contents =
- consumeDebugMagic((*DbgIt)->getContents(), ".debug$S");
- DebugSubsectionArray Subsections;
- BinaryStreamReader Reader(Contents, support::little);
- ExitOnErr(Reader.readArray(Subsections, Contents.size()));
-
- for (const DebugSubsectionRecord &SS : Subsections) {
- if (SS.kind() != DebugSubsectionKind::Symbols)
- continue;
-
- // If it's there, the S_OBJNAME record shall come first in the stream.
- Expected<CVSymbol> Sym = readSymbolFromStream(SS.getRecordData(), 0);
- if (!Sym) {
- consumeError(Sym.takeError());
- continue;
- }
- if (auto ObjName = SymbolDeserializer::deserializeAs<ObjNameSym>(Sym.get()))
- return ObjName->Signature;
- }
- return 0;
}
Expected<const CVIndexMap &>
-PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) {
- ScopedTimer T(TypeMergingTimer);
+PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) {
+ ScopedTimer t(typeMergingTimer);
- bool IsPrecompiledHeader = false;
-
- ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
- if (Data.empty()) {
- // Try again, Microsoft precompiled headers use .debug$P instead of
- // .debug$T
- Data = getDebugSection(File, ".debug$P");
- IsPrecompiledHeader = true;
- }
- if (Data.empty())
- return *ObjectIndexMap; // no debug info
+ if (!file->debugTypesObj)
+ return *objectIndexMap; // no Types stream
// Precompiled headers objects need to save the index map for further
// reference by other objects which use the precompiled headers.
- if (IsPrecompiledHeader) {
- uint32_t PCHSignature = extractPCHSignature(File);
- if (PCHSignature == 0)
+ if (file->debugTypesObj->kind == TpiSource::PCH) {
+ uint32_t pchSignature = file->pchSignature.getValueOr(0);
+ if (pchSignature == 0)
fatal("No signature found for the precompiled headers OBJ (" +
- File->getName() + ")");
+ file->getName() + ")");
// When a precompiled headers object comes first on the command-line, we
// update the mapping here. Otherwise, if an object referencing the
// precompiled headers object comes first, the mapping is created in
// aquirePrecompObj(), thus we would skip this block.
- if (!ObjectIndexMap->IsPrecompiledTypeMap) {
- auto R = registerPrecompiledHeaders(PCHSignature);
- if (R.second)
+ if (!objectIndexMap->isPrecompiledTypeMap) {
+ auto r = registerPrecompiledHeaders(pchSignature);
+ if (r.second)
fatal(
"A precompiled headers OBJ with the same signature was already "
"provided! (" +
- File->getName() + ")");
+ file->getName() + ")");
- ObjectIndexMap = &R.first;
+ objectIndexMap = &r.first;
}
}
- BinaryByteStream Stream(Data, support::little);
- CVTypeArray Types;
- BinaryStreamReader Reader(Stream);
- if (auto EC = Reader.readArray(Types, Reader.getLength()))
- fatal("Reader::readArray failed: " + toString(std::move(EC)));
-
- auto FirstType = Types.begin();
- if (FirstType == Types.end())
- return *ObjectIndexMap;
-
- if (FirstType->kind() == LF_TYPESERVER2) {
+ if (file->debugTypesObj->kind == TpiSource::UsingPDB) {
// Look through type servers. If we've already seen this type server,
// don't merge any type information.
- return maybeMergeTypeServerPDB(File, *FirstType);
- } else if (FirstType->kind() == LF_PRECOMP) {
+ return maybeMergeTypeServerPDB(file);
+ }
+
+ CVTypeArray &types = *file->debugTypes;
+
+ if (file->debugTypesObj->kind == TpiSource::UsingPCH) {
// This object was compiled with /Yu, so process the corresponding
// precompiled headers object (/Yc) first. Some type indices in the current
// object are referencing data in the precompiled headers object, so we need
// both to be loaded.
- auto E = mergeInPrecompHeaderObj(File, *FirstType, ObjectIndexMap);
- if (!E)
- return E.takeError();
-
- // Drop LF_PRECOMP record from the input stream, as it needs to be replaced
- // with the precompiled headers object type stream.
- // Note that we can't just call Types.drop_front(), as we explicitly want to
- // rebase the stream.
- Types.setUnderlyingStream(
- Types.getUnderlyingStream().drop_front(FirstType->RecordData.size()));
+ Error e = mergeInPrecompHeaderObj(file, objectIndexMap);
+ if (e)
+ return std::move(e);
+
+ // Drop LF_PRECOMP record from the input stream, as it has been replaced
+ // with the precompiled headers Type stream in the mergeInPrecompHeaderObj()
+ // call above. Note that we can't just call Types.drop_front(), as we
+ // explicitly want to rebase the stream.
+ CVTypeArray::Iterator firstType = types.begin();
+ types.setUnderlyingStream(
+ types.getUnderlyingStream().drop_front(firstType->RecordData.size()));
}
// Fill in the temporary, caller-provided ObjectIndexMap.
- if (Config->DebugGHashes) {
- ArrayRef<GloballyHashedType> Hashes;
- std::vector<GloballyHashedType> OwnedHashes;
- if (Optional<ArrayRef<uint8_t>> DebugH = getDebugH(File))
- Hashes = getHashesFromDebugH(*DebugH);
+ if (config->debugGHashes) {
+ ArrayRef<GloballyHashedType> hashes;
+ std::vector<GloballyHashedType> ownedHashes;
+ if (Optional<ArrayRef<uint8_t>> debugH = getDebugH(file))
+ hashes = getHashesFromDebugH(*debugH);
else {
- OwnedHashes = GloballyHashedType::hashTypes(Types);
- Hashes = OwnedHashes;
+ ownedHashes = GloballyHashedType::hashTypes(types);
+ hashes = ownedHashes;
}
- if (auto Err = mergeTypeAndIdRecords(GlobalIDTable, GlobalTypeTable,
- ObjectIndexMap->TPIMap, Types, Hashes,
- File->PCHSignature))
+ if (auto err = mergeTypeAndIdRecords(
+ tMerger.globalIDTable, tMerger.globalTypeTable,
+ objectIndexMap->tpiMap, types, hashes, file->pchSignature))
fatal("codeview::mergeTypeAndIdRecords failed: " +
- toString(std::move(Err)));
+ toString(std::move(err)));
} else {
- if (auto Err =
- mergeTypeAndIdRecords(IDTable, TypeTable, ObjectIndexMap->TPIMap,
- Types, File->PCHSignature))
+ if (auto err = mergeTypeAndIdRecords(tMerger.iDTable, tMerger.typeTable,
+ objectIndexMap->tpiMap, types,
+ file->pchSignature))
fatal("codeview::mergeTypeAndIdRecords failed: " +
- toString(std::move(Err)));
+ toString(std::move(err)));
}
- return *ObjectIndexMap;
+ return *objectIndexMap;
}
-static Expected<std::unique_ptr<pdb::NativeSession>>
-tryToLoadPDB(const codeview::GUID &GuidFromObj, StringRef TSPath) {
- // Ensure the file exists before anything else. We want to return ENOENT,
- // "file not found", even if the path points to a removable device (in which
- // case the return message would be EAGAIN, "resource unavailable try again")
- if (!llvm::sys::fs::exists(TSPath))
- return errorCodeToError(std::error_code(ENOENT, std::generic_category()));
-
- ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile(
- TSPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false);
- if (!MBOrErr)
- return errorCodeToError(MBOrErr.getError());
-
- std::unique_ptr<pdb::IPDBSession> ThisSession;
- if (auto EC = pdb::NativeSession::createFromPdb(
- MemoryBuffer::getMemBuffer(Driver->takeBuffer(std::move(*MBOrErr)),
- /*RequiresNullTerminator=*/false),
- ThisSession))
- return std::move(EC);
-
- std::unique_ptr<pdb::NativeSession> NS(
- static_cast<pdb::NativeSession *>(ThisSession.release()));
- pdb::PDBFile &File = NS->getPDBFile();
- auto ExpectedInfo = File.getPDBInfoStream();
- // All PDB Files should have an Info stream.
- if (!ExpectedInfo)
- return ExpectedInfo.takeError();
-
- // Just because a file with a matching name was found and it was an actual
- // PDB file doesn't mean it matches. For it to match the InfoStream's GUID
- // must match the GUID specified in the TypeServer2 record.
- if (ExpectedInfo->getGuid() != GuidFromObj)
- return make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date);
-
- return std::move(NS);
-}
+Expected<const CVIndexMap &> PDBLinker::maybeMergeTypeServerPDB(ObjFile *file) {
+ Expected<llvm::pdb::NativeSession *> pdbSession = findTypeServerSource(file);
+ if (!pdbSession)
+ return pdbSession.takeError();
-Expected<const CVIndexMap &>
-PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, const CVType &FirstType) {
- TypeServer2Record TS;
- if (auto EC =
- TypeDeserializer::deserializeAs(const_cast<CVType &>(FirstType), TS))
- fatal("error reading record: " + toString(std::move(EC)));
-
- const codeview::GUID &TSId = TS.getGuid();
- StringRef TSPath = TS.getName();
-
- // First, check if the PDB has previously failed to load.
- auto PrevErr = MissingTypeServerPDBs.find(TSId);
- if (PrevErr != MissingTypeServerPDBs.end())
- return createFileError(
- TSPath,
- make_error<StringError>(PrevErr->second, inconvertibleErrorCode()));
+ pdb::PDBFile &pdbFile = pdbSession.get()->getPDBFile();
+ pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream());
- // Second, check if we already loaded a PDB with this GUID. Return the type
- // index mapping if we have it.
- auto Insertion = TypeServerIndexMappings.insert({TSId, CVIndexMap()});
- CVIndexMap &IndexMap = Insertion.first->second;
- if (!Insertion.second)
- return IndexMap;
+ auto it = typeServerIndexMappings.emplace(info.getGuid(), CVIndexMap());
+ CVIndexMap &indexMap = it.first->second;
+ if (!it.second)
+ return indexMap; // already merged
// Mark this map as a type server map.
- IndexMap.IsTypeServerMap = true;
-
- // Check for a PDB at:
- // 1. The given file path
- // 2. Next to the object file or archive file
- auto ExpectedSession = handleExpected(
- tryToLoadPDB(TSId, TSPath),
- [&]() {
- StringRef LocalPath =
- !File->ParentName.empty() ? File->ParentName : File->getName();
- SmallString<128> Path = sys::path::parent_path(LocalPath);
- // Currently, type server PDBs are only created by cl, which only runs
- // on Windows, so we can assume type server paths are Windows style.
- sys::path::append(
- Path, sys::path::filename(TSPath, sys::path::Style::windows));
- return tryToLoadPDB(TSId, Path);
- },
- [&](std::unique_ptr<ECError> EC) -> Error {
- auto SysErr = EC->convertToErrorCode();
- // Only re-try loading if the previous error was "No such file or
- // directory"
- if (SysErr.category() == std::generic_category() &&
- SysErr.value() == ENOENT)
- return Error::success();
- return Error(std::move(EC));
- });
-
- if (auto E = ExpectedSession.takeError()) {
- TypeServerIndexMappings.erase(TSId);
-
- // Flatten the error to a string, for later display, if the error occurs
- // again on the same PDB.
- std::string ErrMsg;
- raw_string_ostream S(ErrMsg);
- S << E;
- MissingTypeServerPDBs.emplace(TSId, S.str());
-
- return createFileError(TSPath, std::move(E));
+ indexMap.isTypeServerMap = true;
+
+ Expected<pdb::TpiStream &> expectedTpi = pdbFile.getPDBTpiStream();
+ if (auto e = expectedTpi.takeError())
+ fatal("Type server does not have TPI stream: " + toString(std::move(e)));
+ pdb::TpiStream *maybeIpi = nullptr;
+ if (pdbFile.hasPDBIpiStream()) {
+ Expected<pdb::TpiStream &> expectedIpi = pdbFile.getPDBIpiStream();
+ if (auto e = expectedIpi.takeError())
+ fatal("Error getting type server IPI stream: " + toString(std::move(e)));
+ maybeIpi = &*expectedIpi;
}
- pdb::NativeSession *Session = ExpectedSession->get();
-
- // Keep a strong reference to this PDB, so that it's safe to hold pointers
- // into the file.
- LoadedPDBs.push_back(std::move(*ExpectedSession));
-
- auto ExpectedTpi = Session->getPDBFile().getPDBTpiStream();
- if (auto E = ExpectedTpi.takeError())
- fatal("Type server does not have TPI stream: " + toString(std::move(E)));
- auto ExpectedIpi = Session->getPDBFile().getPDBIpiStream();
- if (auto E = ExpectedIpi.takeError())
- fatal("Type server does not have TPI stream: " + toString(std::move(E)));
-
- if (Config->DebugGHashes) {
+ if (config->debugGHashes) {
// PDBs do not actually store global hashes, so when merging a type server
// PDB we have to synthesize global hashes. To do this, we first synthesize
// global hashes for the TPI stream, since it is independent, then we
// synthesize hashes for the IPI stream, using the hashes for the TPI stream
// as inputs.
- auto TpiHashes = GloballyHashedType::hashTypes(ExpectedTpi->typeArray());
- auto IpiHashes =
- GloballyHashedType::hashIds(ExpectedIpi->typeArray(), TpiHashes);
-
- Optional<uint32_t> EndPrecomp;
+ auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray());
+ Optional<uint32_t> endPrecomp;
// Merge TPI first, because the IPI stream will reference type indices.
- if (auto Err = mergeTypeRecords(GlobalTypeTable, IndexMap.TPIMap,
- ExpectedTpi->typeArray(), TpiHashes, EndPrecomp))
- fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err)));
+ if (auto err =
+ mergeTypeRecords(tMerger.globalTypeTable, indexMap.tpiMap,
+ expectedTpi->typeArray(), tpiHashes, endPrecomp))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
// Merge IPI.
- if (auto Err =
- mergeIdRecords(GlobalIDTable, IndexMap.TPIMap, IndexMap.IPIMap,
- ExpectedIpi->typeArray(), IpiHashes))
- fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err)));
+ if (maybeIpi) {
+ auto ipiHashes =
+ GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes);
+ if (auto err =
+ mergeIdRecords(tMerger.globalIDTable, indexMap.tpiMap,
+ indexMap.ipiMap, maybeIpi->typeArray(), ipiHashes))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
+ }
} else {
// Merge TPI first, because the IPI stream will reference type indices.
- if (auto Err = mergeTypeRecords(TypeTable, IndexMap.TPIMap,
- ExpectedTpi->typeArray()))
- fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err)));
+ if (auto err = mergeTypeRecords(tMerger.typeTable, indexMap.tpiMap,
+ expectedTpi->typeArray()))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
// Merge IPI.
- if (auto Err = mergeIdRecords(IDTable, IndexMap.TPIMap, IndexMap.IPIMap,
- ExpectedIpi->typeArray()))
- fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err)));
+ if (maybeIpi) {
+ if (auto err = mergeIdRecords(tMerger.iDTable, indexMap.tpiMap,
+ indexMap.ipiMap, maybeIpi->typeArray()))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
+ }
}
- return IndexMap;
+ return indexMap;
}
-Expected<const CVIndexMap &>
-PDBLinker::mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType,
- CVIndexMap *ObjectIndexMap) {
- PrecompRecord Precomp;
- if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(FirstType),
- Precomp))
- fatal("error reading record: " + toString(std::move(EC)));
+Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *file,
+ CVIndexMap *objectIndexMap) {
+ const PrecompRecord &precomp =
+ retrieveDependencyInfo<PrecompRecord>(file->debugTypesObj);
- auto E = aquirePrecompObj(File, Precomp);
- if (!E)
- return E.takeError();
+ Expected<const CVIndexMap &> e = aquirePrecompObj(file);
+ if (!e)
+ return e.takeError();
- const CVIndexMap &PrecompIndexMap = *E;
- assert(PrecompIndexMap.IsPrecompiledTypeMap);
+ const CVIndexMap &precompIndexMap = *e;
+ assert(precompIndexMap.isPrecompiledTypeMap);
- if (PrecompIndexMap.TPIMap.empty())
- return PrecompIndexMap;
+ if (precompIndexMap.tpiMap.empty())
+ return Error::success();
- assert(Precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex);
- assert(Precomp.getTypesCount() <= PrecompIndexMap.TPIMap.size());
+ assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex);
+ assert(precomp.getTypesCount() <= precompIndexMap.tpiMap.size());
// Use the previously remapped index map from the precompiled headers.
- ObjectIndexMap->TPIMap.append(PrecompIndexMap.TPIMap.begin(),
- PrecompIndexMap.TPIMap.begin() +
- Precomp.getTypesCount());
- return *ObjectIndexMap;
+ objectIndexMap->tpiMap.append(precompIndexMap.tpiMap.begin(),
+ precompIndexMap.tpiMap.begin() +
+ precomp.getTypesCount());
+ return Error::success();
}
static bool equals_path(StringRef path1, StringRef path2) {
@@ -694,101 +515,103 @@ static bool equals_path(StringRef path1, StringRef path2) {
}
// Find by name an OBJ provided on the command line
-static ObjFile *findObjByName(StringRef FileNameOnly) {
- SmallString<128> CurrentPath;
+static ObjFile *findObjByName(StringRef fileNameOnly) {
+ SmallString<128> currentPath;
- for (ObjFile *F : ObjFile::Instances) {
- StringRef CurrentFileName = sys::path::filename(F->getName());
+ for (ObjFile *f : ObjFile::instances) {
+ StringRef currentFileName = sys::path::filename(f->getName());
// Compare based solely on the file name (link.exe behavior)
- if (equals_path(CurrentFileName, FileNameOnly))
- return F;
+ if (equals_path(currentFileName, fileNameOnly))
+ return f;
}
return nullptr;
}
std::pair<CVIndexMap &, bool /*already there*/>
-PDBLinker::registerPrecompiledHeaders(uint32_t Signature) {
- auto Insertion = PrecompTypeIndexMappings.insert({Signature, CVIndexMap()});
- CVIndexMap &IndexMap = Insertion.first->second;
- if (!Insertion.second)
- return {IndexMap, true};
+PDBLinker::registerPrecompiledHeaders(uint32_t signature) {
+ auto insertion = precompTypeIndexMappings.insert({signature, CVIndexMap()});
+ CVIndexMap &indexMap = insertion.first->second;
+ if (!insertion.second)
+ return {indexMap, true};
// Mark this map as a precompiled types map.
- IndexMap.IsPrecompiledTypeMap = true;
- return {IndexMap, false};
+ indexMap.isPrecompiledTypeMap = true;
+ return {indexMap, false};
}
-Expected<const CVIndexMap &>
-PDBLinker::aquirePrecompObj(ObjFile *File, PrecompRecord Precomp) {
+Expected<const CVIndexMap &> PDBLinker::aquirePrecompObj(ObjFile *file) {
+ const PrecompRecord &precomp =
+ retrieveDependencyInfo<PrecompRecord>(file->debugTypesObj);
+
// First, check if we already loaded the precompiled headers object with this
// signature. Return the type index mapping if we've already seen it.
- auto R = registerPrecompiledHeaders(Precomp.getSignature());
- if (R.second)
- return R.first;
+ auto r = registerPrecompiledHeaders(precomp.getSignature());
+ if (r.second)
+ return r.first;
- CVIndexMap &IndexMap = R.first;
+ CVIndexMap &indexMap = r.first;
// Cross-compile warning: given that Clang doesn't generate LF_PRECOMP
// records, we assume the OBJ comes from a Windows build of cl.exe. Thusly,
// the paths embedded in the OBJs are in the Windows format.
- SmallString<128> PrecompFileName = sys::path::filename(
- Precomp.getPrecompFilePath(), sys::path::Style::windows);
+ SmallString<128> precompFileName = sys::path::filename(
+ precomp.getPrecompFilePath(), sys::path::Style::windows);
// link.exe requires that a precompiled headers object must always be provided
// on the command-line, even if that's not necessary.
- auto PrecompFile = findObjByName(PrecompFileName);
- if (!PrecompFile)
+ auto precompFile = findObjByName(precompFileName);
+ if (!precompFile)
return createFileError(
- PrecompFileName.str(),
+ precompFileName.str(),
make_error<pdb::PDBError>(pdb::pdb_error_code::external_cmdline_ref));
- addObjFile(PrecompFile, &IndexMap);
+ addObjFile(precompFile, &indexMap);
- if (!PrecompFile->PCHSignature)
- fatal(PrecompFile->getName() + " is not a precompiled headers object");
+ if (!precompFile->pchSignature)
+ fatal(precompFile->getName() + " is not a precompiled headers object");
- if (Precomp.getSignature() != PrecompFile->PCHSignature.getValueOr(0))
+ if (precomp.getSignature() != precompFile->pchSignature.getValueOr(0))
return createFileError(
- Precomp.getPrecompFilePath().str(),
+ precomp.getPrecompFilePath().str(),
make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
- return IndexMap;
+ return indexMap;
}
-static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) {
- if (TI.isSimple())
+static bool remapTypeIndex(TypeIndex &ti, ArrayRef<TypeIndex> typeIndexMap) {
+ if (ti.isSimple())
return true;
- if (TI.toArrayIndex() >= TypeIndexMap.size())
+ if (ti.toArrayIndex() >= typeIndexMap.size())
return false;
- TI = TypeIndexMap[TI.toArrayIndex()];
+ ti = typeIndexMap[ti.toArrayIndex()];
return true;
}
-static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind,
- MutableArrayRef<uint8_t> RecordBytes,
- const CVIndexMap &IndexMap,
- ArrayRef<TiReference> TypeRefs) {
- MutableArrayRef<uint8_t> Contents =
- RecordBytes.drop_front(sizeof(RecordPrefix));
- for (const TiReference &Ref : TypeRefs) {
- unsigned ByteSize = Ref.Count * sizeof(TypeIndex);
- if (Contents.size() < Ref.Offset + ByteSize)
+static void remapTypesInSymbolRecord(ObjFile *file, SymbolKind symKind,
+ MutableArrayRef<uint8_t> recordBytes,
+ const CVIndexMap &indexMap,
+ ArrayRef<TiReference> typeRefs) {
+ MutableArrayRef<uint8_t> contents =
+ recordBytes.drop_front(sizeof(RecordPrefix));
+ for (const TiReference &ref : typeRefs) {
+ unsigned byteSize = ref.Count * sizeof(TypeIndex);
+ if (contents.size() < ref.Offset + byteSize)
fatal("symbol record too short");
// This can be an item index or a type index. Choose the appropriate map.
- ArrayRef<TypeIndex> TypeOrItemMap = IndexMap.TPIMap;
- bool IsItemIndex = Ref.Kind == TiRefKind::IndexRef;
- if (IsItemIndex && IndexMap.IsTypeServerMap)
- TypeOrItemMap = IndexMap.IPIMap;
-
- MutableArrayRef<TypeIndex> TIs(
- reinterpret_cast<TypeIndex *>(Contents.data() + Ref.Offset), Ref.Count);
- for (TypeIndex &TI : TIs) {
- if (!remapTypeIndex(TI, TypeOrItemMap)) {
- log("ignoring symbol record of kind 0x" + utohexstr(SymKind) + " in " +
- File->getName() + " with bad " + (IsItemIndex ? "item" : "type") +
- " index 0x" + utohexstr(TI.getIndex()));
- TI = TypeIndex(SimpleTypeKind::NotTranslated);
+ ArrayRef<TypeIndex> typeOrItemMap = indexMap.tpiMap;
+ bool isItemIndex = ref.Kind == TiRefKind::IndexRef;
+ if (isItemIndex && indexMap.isTypeServerMap)
+ typeOrItemMap = indexMap.ipiMap;
+
+ MutableArrayRef<TypeIndex> tIs(
+ reinterpret_cast<TypeIndex *>(contents.data() + ref.Offset), ref.Count);
+ for (TypeIndex &ti : tIs) {
+ if (!remapTypeIndex(ti, typeOrItemMap)) {
+ log("ignoring symbol record of kind 0x" + utohexstr(symKind) + " in " +
+ file->getName() + " with bad " + (isItemIndex ? "item" : "type") +
+ " index 0x" + utohexstr(ti.getIndex()));
+ ti = TypeIndex(SimpleTypeKind::NotTranslated);
continue;
}
}
@@ -796,26 +619,26 @@ static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind,
}
static void
-recordStringTableReferenceAtOffset(MutableArrayRef<uint8_t> Contents,
- uint32_t Offset,
- std::vector<ulittle32_t *> &StrTableRefs) {
- Contents =
- Contents.drop_front(Offset).take_front(sizeof(support::ulittle32_t));
- ulittle32_t *Index = reinterpret_cast<ulittle32_t *>(Contents.data());
- StrTableRefs.push_back(Index);
+recordStringTableReferenceAtOffset(MutableArrayRef<uint8_t> contents,
+ uint32_t offset,
+ std::vector<ulittle32_t *> &strTableRefs) {
+ contents =
+ contents.drop_front(offset).take_front(sizeof(support::ulittle32_t));
+ ulittle32_t *index = reinterpret_cast<ulittle32_t *>(contents.data());
+ strTableRefs.push_back(index);
}
static void
-recordStringTableReferences(SymbolKind Kind, MutableArrayRef<uint8_t> Contents,
- std::vector<ulittle32_t *> &StrTableRefs) {
+recordStringTableReferences(SymbolKind kind, MutableArrayRef<uint8_t> contents,
+ std::vector<ulittle32_t *> &strTableRefs) {
// For now we only handle S_FILESTATIC, but we may need the same logic for
// S_DEFRANGE and S_DEFRANGE_SUBFIELD. However, I cannot seem to generate any
// PDBs that contain these types of records, so because of the uncertainty
// they are omitted here until we can prove that it's necessary.
- switch (Kind) {
+ switch (kind) {
case SymbolKind::S_FILESTATIC:
// FileStaticSym::ModFileOffset
- recordStringTableReferenceAtOffset(Contents, 8, StrTableRefs);
+ recordStringTableReferenceAtOffset(contents, 8, strTableRefs);
break;
case SymbolKind::S_DEFRANGE:
case SymbolKind::S_DEFRANGE_SUBFIELD:
@@ -827,21 +650,21 @@ recordStringTableReferences(SymbolKind Kind, MutableArrayRef<uint8_t> Contents,
}
}
-static SymbolKind symbolKind(ArrayRef<uint8_t> RecordData) {
- const RecordPrefix *Prefix =
- reinterpret_cast<const RecordPrefix *>(RecordData.data());
- return static_cast<SymbolKind>(uint16_t(Prefix->RecordKind));
+static SymbolKind symbolKind(ArrayRef<uint8_t> recordData) {
+ const RecordPrefix *prefix =
+ reinterpret_cast<const RecordPrefix *>(recordData.data());
+ return static_cast<SymbolKind>(uint16_t(prefix->RecordKind));
}
/// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32
-static void translateIdSymbols(MutableArrayRef<uint8_t> &RecordData,
- TypeCollection &IDTable) {
- RecordPrefix *Prefix = reinterpret_cast<RecordPrefix *>(RecordData.data());
+static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
+ TypeCollection &iDTable) {
+ RecordPrefix *prefix = reinterpret_cast<RecordPrefix *>(recordData.data());
- SymbolKind Kind = symbolKind(RecordData);
+ SymbolKind kind = symbolKind(recordData);
- if (Kind == SymbolKind::S_PROC_ID_END) {
- Prefix->RecordKind = SymbolKind::S_END;
+ if (kind == SymbolKind::S_PROC_ID_END) {
+ prefix->RecordKind = SymbolKind::S_END;
return;
}
@@ -850,89 +673,89 @@ static void translateIdSymbols(MutableArrayRef<uint8_t> &RecordData,
// to the PDB file's ID stream index space, but we need to convert this to a
// symbol that refers to the type stream index space. So we remap again from
// ID index space to type index space.
- if (Kind == SymbolKind::S_GPROC32_ID || Kind == SymbolKind::S_LPROC32_ID) {
- SmallVector<TiReference, 1> Refs;
- auto Content = RecordData.drop_front(sizeof(RecordPrefix));
- CVSymbol Sym(Kind, RecordData);
- discoverTypeIndicesInSymbol(Sym, Refs);
- assert(Refs.size() == 1);
- assert(Refs.front().Count == 1);
-
- TypeIndex *TI =
- reinterpret_cast<TypeIndex *>(Content.data() + Refs[0].Offset);
- // `TI` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in
+ if (kind == SymbolKind::S_GPROC32_ID || kind == SymbolKind::S_LPROC32_ID) {
+ SmallVector<TiReference, 1> refs;
+ auto content = recordData.drop_front(sizeof(RecordPrefix));
+ CVSymbol sym(recordData);
+ discoverTypeIndicesInSymbol(sym, refs);
+ assert(refs.size() == 1);
+ assert(refs.front().Count == 1);
+
+ TypeIndex *ti =
+ reinterpret_cast<TypeIndex *>(content.data() + refs[0].Offset);
+ // `ti` is the index of a FuncIdRecord or MemberFuncIdRecord which lives in
// the IPI stream, whose `FunctionType` member refers to the TPI stream.
// Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and
// in both cases we just need the second type index.
- if (!TI->isSimple() && !TI->isNoneType()) {
- CVType FuncIdData = IDTable.getType(*TI);
- SmallVector<TypeIndex, 2> Indices;
- discoverTypeIndices(FuncIdData, Indices);
- assert(Indices.size() == 2);
- *TI = Indices[1];
+ if (!ti->isSimple() && !ti->isNoneType()) {
+ CVType funcIdData = iDTable.getType(*ti);
+ SmallVector<TypeIndex, 2> indices;
+ discoverTypeIndices(funcIdData, indices);
+ assert(indices.size() == 2);
+ *ti = indices[1];
}
- Kind = (Kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32
+ kind = (kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32
: SymbolKind::S_LPROC32;
- Prefix->RecordKind = uint16_t(Kind);
+ prefix->RecordKind = uint16_t(kind);
}
}
/// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned.
/// The object file may not be aligned.
static MutableArrayRef<uint8_t>
-copyAndAlignSymbol(const CVSymbol &Sym, MutableArrayRef<uint8_t> &AlignedMem) {
- size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb));
- assert(Size >= 4 && "record too short");
- assert(Size <= MaxRecordLength && "record too long");
- assert(AlignedMem.size() >= Size && "didn't preallocate enough");
+copyAndAlignSymbol(const CVSymbol &sym, MutableArrayRef<uint8_t> &alignedMem) {
+ size_t size = alignTo(sym.length(), alignOf(CodeViewContainer::Pdb));
+ assert(size >= 4 && "record too short");
+ assert(size <= MaxRecordLength && "record too long");
+ assert(alignedMem.size() >= size && "didn't preallocate enough");
// Copy the symbol record and zero out any padding bytes.
- MutableArrayRef<uint8_t> NewData = AlignedMem.take_front(Size);
- AlignedMem = AlignedMem.drop_front(Size);
- memcpy(NewData.data(), Sym.data().data(), Sym.length());
- memset(NewData.data() + Sym.length(), 0, Size - Sym.length());
+ MutableArrayRef<uint8_t> newData = alignedMem.take_front(size);
+ alignedMem = alignedMem.drop_front(size);
+ memcpy(newData.data(), sym.data().data(), sym.length());
+ memset(newData.data() + sym.length(), 0, size - sym.length());
// Update the record prefix length. It should point to the beginning of the
// next record.
- auto *Prefix = reinterpret_cast<RecordPrefix *>(NewData.data());
- Prefix->RecordLen = Size - 2;
- return NewData;
+ auto *prefix = reinterpret_cast<RecordPrefix *>(newData.data());
+ prefix->RecordLen = size - 2;
+ return newData;
}
struct ScopeRecord {
- ulittle32_t PtrParent;
- ulittle32_t PtrEnd;
+ ulittle32_t ptrParent;
+ ulittle32_t ptrEnd;
};
struct SymbolScope {
- ScopeRecord *OpeningRecord;
- uint32_t ScopeOffset;
+ ScopeRecord *openingRecord;
+ uint32_t scopeOffset;
};
-static void scopeStackOpen(SmallVectorImpl<SymbolScope> &Stack,
- uint32_t CurOffset, CVSymbol &Sym) {
- assert(symbolOpensScope(Sym.kind()));
- SymbolScope S;
- S.ScopeOffset = CurOffset;
- S.OpeningRecord = const_cast<ScopeRecord *>(
- reinterpret_cast<const ScopeRecord *>(Sym.content().data()));
- S.OpeningRecord->PtrParent = Stack.empty() ? 0 : Stack.back().ScopeOffset;
- Stack.push_back(S);
+static void scopeStackOpen(SmallVectorImpl<SymbolScope> &stack,
+ uint32_t curOffset, CVSymbol &sym) {
+ assert(symbolOpensScope(sym.kind()));
+ SymbolScope s;
+ s.scopeOffset = curOffset;
+ s.openingRecord = const_cast<ScopeRecord *>(
+ reinterpret_cast<const ScopeRecord *>(sym.content().data()));
+ s.openingRecord->ptrParent = stack.empty() ? 0 : stack.back().scopeOffset;
+ stack.push_back(s);
}
-static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack,
- uint32_t CurOffset, ObjFile *File) {
- if (Stack.empty()) {
- warn("symbol scopes are not balanced in " + File->getName());
+static void scopeStackClose(SmallVectorImpl<SymbolScope> &stack,
+ uint32_t curOffset, InputFile *file) {
+ if (stack.empty()) {
+ warn("symbol scopes are not balanced in " + file->getName());
return;
}
- SymbolScope S = Stack.pop_back_val();
- S.OpeningRecord->PtrEnd = CurOffset;
+ SymbolScope s = stack.pop_back_val();
+ s.openingRecord->ptrEnd = curOffset;
}
-static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) {
- switch (Sym.kind()) {
+static bool symbolGoesInModuleStream(const CVSymbol &sym, bool isGlobalScope) {
+ switch (sym.kind()) {
case SymbolKind::S_GDATA32:
case SymbolKind::S_CONSTANT:
// We really should not be seeing S_PROCREF and S_LPROCREF in the first place
@@ -944,7 +767,7 @@ static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) {
return false;
// S_UDT records go in the module stream if it is not a global S_UDT.
case SymbolKind::S_UDT:
- return !IsGlobalScope;
+ return !isGlobalScope;
// S_GDATA32 does not go in the module stream, but S_LDATA32 does.
case SymbolKind::S_LDATA32:
default:
@@ -952,8 +775,8 @@ static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) {
}
}
-static bool symbolGoesInGlobalsStream(const CVSymbol &Sym, bool IsGlobalScope) {
- switch (Sym.kind()) {
+static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) {
+ switch (sym.kind()) {
case SymbolKind::S_CONSTANT:
case SymbolKind::S_GDATA32:
// S_LDATA32 goes in both the module stream and the globals stream.
@@ -968,36 +791,36 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &Sym, bool IsGlobalScope) {
return true;
// S_UDT records go in the globals stream if it is a global S_UDT.
case SymbolKind::S_UDT:
- return IsGlobalScope;
+ return isGlobalScope;
default:
return false;
}
}
-static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, uint16_t ModIndex,
- unsigned SymOffset, const CVSymbol &Sym) {
- switch (Sym.kind()) {
+static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex,
+ unsigned symOffset, const CVSymbol &sym) {
+ switch (sym.kind()) {
case SymbolKind::S_CONSTANT:
case SymbolKind::S_UDT:
case SymbolKind::S_GDATA32:
case SymbolKind::S_LDATA32:
case SymbolKind::S_PROCREF:
case SymbolKind::S_LPROCREF:
- Builder.addGlobalSymbol(Sym);
+ builder.addGlobalSymbol(sym);
break;
case SymbolKind::S_GPROC32:
case SymbolKind::S_LPROC32: {
- SymbolRecordKind K = SymbolRecordKind::ProcRefSym;
- if (Sym.kind() == SymbolKind::S_LPROC32)
- K = SymbolRecordKind::LocalProcRef;
- ProcRefSym PS(K);
- PS.Module = ModIndex;
+ SymbolRecordKind k = SymbolRecordKind::ProcRefSym;
+ if (sym.kind() == SymbolKind::S_LPROC32)
+ k = SymbolRecordKind::LocalProcRef;
+ ProcRefSym ps(k);
+ ps.Module = modIndex;
// For some reason, MSVC seems to add one to this value.
- ++PS.Module;
- PS.Name = getSymbolName(Sym);
- PS.SumName = 0;
- PS.SymOffset = SymOffset;
- Builder.addGlobalSymbol(PS);
+ ++ps.Module;
+ ps.Name = getSymbolName(sym);
+ ps.SumName = 0;
+ ps.SymOffset = symOffset;
+ builder.addGlobalSymbol(ps);
break;
}
default:
@@ -1005,229 +828,304 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, uint16_t ModIndex,
}
}
-void PDBLinker::mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap,
- std::vector<ulittle32_t *> &StringTableRefs,
- BinaryStreamRef SymData) {
- ArrayRef<uint8_t> SymsBuffer;
- cantFail(SymData.readBytes(0, SymData.getLength(), SymsBuffer));
- SmallVector<SymbolScope, 4> Scopes;
+void PDBLinker::mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
+ std::vector<ulittle32_t *> &stringTableRefs,
+ BinaryStreamRef symData) {
+ ArrayRef<uint8_t> symsBuffer;
+ cantFail(symData.readBytes(0, symData.getLength(), symsBuffer));
+ SmallVector<SymbolScope, 4> scopes;
// Iterate every symbol to check if any need to be realigned, and if so, how
// much space we need to allocate for them.
- bool NeedsRealignment = false;
- unsigned TotalRealignedSize = 0;
- auto EC = forEachCodeViewRecord<CVSymbol>(
- SymsBuffer, [&](CVSymbol Sym) -> llvm::Error {
- unsigned RealignedSize =
- alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb));
- NeedsRealignment |= RealignedSize != Sym.length();
- TotalRealignedSize += RealignedSize;
+ bool needsRealignment = false;
+ unsigned totalRealignedSize = 0;
+ auto ec = forEachCodeViewRecord<CVSymbol>(
+ symsBuffer, [&](CVSymbol sym) -> llvm::Error {
+ unsigned realignedSize =
+ alignTo(sym.length(), alignOf(CodeViewContainer::Pdb));
+ needsRealignment |= realignedSize != sym.length();
+ totalRealignedSize += realignedSize;
return Error::success();
});
// If any of the symbol record lengths was corrupt, ignore them all, warn
// about it, and move on.
- if (EC) {
- warn("corrupt symbol records in " + File->getName());
- consumeError(std::move(EC));
+ if (ec) {
+ warn("corrupt symbol records in " + file->getName());
+ consumeError(std::move(ec));
return;
}
// If any symbol needed realignment, allocate enough contiguous memory for
// them all. Typically symbol subsections are small enough that this will not
// cause fragmentation.
- MutableArrayRef<uint8_t> AlignedSymbolMem;
- if (NeedsRealignment) {
- void *AlignedData =
- Alloc.Allocate(TotalRealignedSize, alignOf(CodeViewContainer::Pdb));
- AlignedSymbolMem = makeMutableArrayRef(
- reinterpret_cast<uint8_t *>(AlignedData), TotalRealignedSize);
+ MutableArrayRef<uint8_t> alignedSymbolMem;
+ if (needsRealignment) {
+ void *alignedData =
+ alloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb));
+ alignedSymbolMem = makeMutableArrayRef(
+ reinterpret_cast<uint8_t *>(alignedData), totalRealignedSize);
}
// Iterate again, this time doing the real work.
- unsigned CurSymOffset = File->ModuleDBI->getNextSymbolOffset();
- ArrayRef<uint8_t> BulkSymbols;
+ unsigned curSymOffset = file->moduleDBI->getNextSymbolOffset();
+ ArrayRef<uint8_t> bulkSymbols;
cantFail(forEachCodeViewRecord<CVSymbol>(
- SymsBuffer, [&](CVSymbol Sym) -> llvm::Error {
+ symsBuffer, [&](CVSymbol sym) -> llvm::Error {
// Align the record if required.
- MutableArrayRef<uint8_t> RecordBytes;
- if (NeedsRealignment) {
- RecordBytes = copyAndAlignSymbol(Sym, AlignedSymbolMem);
- Sym = CVSymbol(Sym.kind(), RecordBytes);
+ MutableArrayRef<uint8_t> recordBytes;
+ if (needsRealignment) {
+ recordBytes = copyAndAlignSymbol(sym, alignedSymbolMem);
+ sym = CVSymbol(recordBytes);
} else {
// Otherwise, we can actually mutate the symbol directly, since we
// copied it to apply relocations.
- RecordBytes = makeMutableArrayRef(
- const_cast<uint8_t *>(Sym.data().data()), Sym.length());
+ recordBytes = makeMutableArrayRef(
+ const_cast<uint8_t *>(sym.data().data()), sym.length());
}
// Discover type index references in the record. Skip it if we don't
// know where they are.
- SmallVector<TiReference, 32> TypeRefs;
- if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) {
+ SmallVector<TiReference, 32> typeRefs;
+ if (!discoverTypeIndicesInSymbol(sym, typeRefs)) {
log("ignoring unknown symbol record with kind 0x" +
- utohexstr(Sym.kind()));
+ utohexstr(sym.kind()));
return Error::success();
}
// Re-map all the type index references.
- remapTypesInSymbolRecord(File, Sym.kind(), RecordBytes, IndexMap,
- TypeRefs);
+ remapTypesInSymbolRecord(file, sym.kind(), recordBytes, indexMap,
+ typeRefs);
// An object file may have S_xxx_ID symbols, but these get converted to
// "real" symbols in a PDB.
- translateIdSymbols(RecordBytes, getIDTable());
- Sym = CVSymbol(symbolKind(RecordBytes), RecordBytes);
+ translateIdSymbols(recordBytes, tMerger.getIDTable());
+ sym = CVSymbol(recordBytes);
// If this record refers to an offset in the object file's string table,
// add that item to the global PDB string table and re-write the index.
- recordStringTableReferences(Sym.kind(), RecordBytes, StringTableRefs);
+ recordStringTableReferences(sym.kind(), recordBytes, stringTableRefs);
// Fill in "Parent" and "End" fields by maintaining a stack of scopes.
- if (symbolOpensScope(Sym.kind()))
- scopeStackOpen(Scopes, CurSymOffset, Sym);
- else if (symbolEndsScope(Sym.kind()))
- scopeStackClose(Scopes, CurSymOffset, File);
+ if (symbolOpensScope(sym.kind()))
+ scopeStackOpen(scopes, curSymOffset, sym);
+ else if (symbolEndsScope(sym.kind()))
+ scopeStackClose(scopes, curSymOffset, file);
// Add the symbol to the globals stream if necessary. Do this before
// adding the symbol to the module since we may need to get the next
// symbol offset, and writing to the module's symbol stream will update
// that offset.
- if (symbolGoesInGlobalsStream(Sym, Scopes.empty()))
- addGlobalSymbol(Builder.getGsiBuilder(),
- File->ModuleDBI->getModuleIndex(), CurSymOffset, Sym);
+ if (symbolGoesInGlobalsStream(sym, scopes.empty())) {
+ addGlobalSymbol(builder.getGsiBuilder(),
+ file->moduleDBI->getModuleIndex(), curSymOffset, sym);
+ ++globalSymbols;
+ }
- if (symbolGoesInModuleStream(Sym, Scopes.empty())) {
+ if (symbolGoesInModuleStream(sym, scopes.empty())) {
// Add symbols to the module in bulk. If this symbol is contiguous
// with the previous run of symbols to add, combine the ranges. If
// not, close the previous range of symbols and start a new one.
- if (Sym.data().data() == BulkSymbols.end()) {
- BulkSymbols = makeArrayRef(BulkSymbols.data(),
- BulkSymbols.size() + Sym.length());
+ if (sym.data().data() == bulkSymbols.end()) {
+ bulkSymbols = makeArrayRef(bulkSymbols.data(),
+ bulkSymbols.size() + sym.length());
} else {
- File->ModuleDBI->addSymbolsInBulk(BulkSymbols);
- BulkSymbols = RecordBytes;
+ file->moduleDBI->addSymbolsInBulk(bulkSymbols);
+ bulkSymbols = recordBytes;
}
- CurSymOffset += Sym.length();
+ curSymOffset += sym.length();
+ ++moduleSymbols;
}
return Error::success();
}));
// Add any remaining symbols we've accumulated.
- File->ModuleDBI->addSymbolsInBulk(BulkSymbols);
+ file->moduleDBI->addSymbolsInBulk(bulkSymbols);
}
// Allocate memory for a .debug$S / .debug$F section and relocate it.
-static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc,
- SectionChunk &DebugChunk) {
- uint8_t *Buffer = Alloc.Allocate<uint8_t>(DebugChunk.getSize());
- assert(DebugChunk.OutputSectionOff == 0 &&
+static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &alloc,
+ SectionChunk &debugChunk) {
+ uint8_t *buffer = alloc.Allocate<uint8_t>(debugChunk.getSize());
+ assert(debugChunk.getOutputSectionIdx() == 0 &&
"debug sections should not be in output sections");
- DebugChunk.readRelocTargets();
- DebugChunk.writeTo(Buffer);
- return makeArrayRef(Buffer, DebugChunk.getSize());
+ debugChunk.writeTo(buffer);
+ return makeArrayRef(buffer, debugChunk.getSize());
}
-static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) {
- OutputSection *OS = C->getOutputSection();
- pdb::SectionContrib SC;
- memset(&SC, 0, sizeof(SC));
- SC.ISect = OS->SectionIndex;
- SC.Off = C->getRVA() - OS->getRVA();
- SC.Size = C->getSize();
- if (auto *SecChunk = dyn_cast<SectionChunk>(C)) {
- SC.Characteristics = SecChunk->Header->Characteristics;
- SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex();
- ArrayRef<uint8_t> Contents = SecChunk->getContents();
- JamCRC CRC(0);
- ArrayRef<char> CharContents = makeArrayRef(
- reinterpret_cast<const char *>(Contents.data()), Contents.size());
- CRC.update(CharContents);
- SC.DataCrc = CRC.getCRC();
+static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) {
+ OutputSection *os = c ? c->getOutputSection() : nullptr;
+ pdb::SectionContrib sc;
+ memset(&sc, 0, sizeof(sc));
+ sc.ISect = os ? os->sectionIndex : llvm::pdb::kInvalidStreamIndex;
+ sc.Off = c && os ? c->getRVA() - os->getRVA() : 0;
+ sc.Size = c ? c->getSize() : -1;
+ if (auto *secChunk = dyn_cast_or_null<SectionChunk>(c)) {
+ sc.Characteristics = secChunk->header->Characteristics;
+ sc.Imod = secChunk->file->moduleDBI->getModuleIndex();
+ ArrayRef<uint8_t> contents = secChunk->getContents();
+ JamCRC crc(0);
+ ArrayRef<char> charContents = makeArrayRef(
+ reinterpret_cast<const char *>(contents.data()), contents.size());
+ crc.update(charContents);
+ sc.DataCrc = crc.getCRC();
} else {
- SC.Characteristics = OS->Header.Characteristics;
- // FIXME: When we start creating DBI for import libraries, use those here.
- SC.Imod = Modi;
+ sc.Characteristics = os ? os->header.Characteristics : 0;
+ sc.Imod = modi;
}
- SC.RelocCrc = 0; // FIXME
+ sc.RelocCrc = 0; // FIXME
- return SC;
+ return sc;
}
static uint32_t
-translateStringTableIndex(uint32_t ObjIndex,
- const DebugStringTableSubsectionRef &ObjStrTable,
- DebugStringTableSubsection &PdbStrTable) {
- auto ExpectedString = ObjStrTable.getString(ObjIndex);
- if (!ExpectedString) {
+translateStringTableIndex(uint32_t objIndex,
+ const DebugStringTableSubsectionRef &objStrTable,
+ DebugStringTableSubsection &pdbStrTable) {
+ auto expectedString = objStrTable.getString(objIndex);
+ if (!expectedString) {
warn("Invalid string table reference");
- consumeError(ExpectedString.takeError());
+ consumeError(expectedString.takeError());
return 0;
}
- return PdbStrTable.insert(*ExpectedString);
+ return pdbStrTable.insert(*expectedString);
}
-void DebugSHandler::handleDebugS(lld::coff::SectionChunk &DebugS) {
- DebugSubsectionArray Subsections;
+void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) {
+ DebugSubsectionArray subsections;
+
+ ArrayRef<uint8_t> relocatedDebugContents = SectionChunk::consumeDebugMagic(
+ relocateDebugChunk(linker.alloc, debugS), debugS.getSectionName());
- ArrayRef<uint8_t> RelocatedDebugContents = consumeDebugMagic(
- relocateDebugChunk(Linker.Alloc, DebugS), DebugS.getSectionName());
+ BinaryStreamReader reader(relocatedDebugContents, support::little);
+ exitOnErr(reader.readArray(subsections, relocatedDebugContents.size()));
- BinaryStreamReader Reader(RelocatedDebugContents, support::little);
- ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size()));
+ for (const DebugSubsectionRecord &ss : subsections) {
+ // Ignore subsections with the 'ignore' bit. Some versions of the Visual C++
+ // runtime have subsections with this bit set.
+ if (uint32_t(ss.kind()) & codeview::SubsectionIgnoreFlag)
+ continue;
- for (const DebugSubsectionRecord &SS : Subsections) {
- switch (SS.kind()) {
+ switch (ss.kind()) {
case DebugSubsectionKind::StringTable: {
- assert(!CVStrTab.valid() &&
+ assert(!cVStrTab.valid() &&
"Encountered multiple string table subsections!");
- ExitOnErr(CVStrTab.initialize(SS.getRecordData()));
+ exitOnErr(cVStrTab.initialize(ss.getRecordData()));
break;
}
case DebugSubsectionKind::FileChecksums:
- assert(!Checksums.valid() &&
+ assert(!checksums.valid() &&
"Encountered multiple checksum subsections!");
- ExitOnErr(Checksums.initialize(SS.getRecordData()));
+ exitOnErr(checksums.initialize(ss.getRecordData()));
break;
case DebugSubsectionKind::Lines:
// We can add the relocated line table directly to the PDB without
// modification because the file checksum offsets will stay the same.
- File.ModuleDBI->addDebugSubsection(SS);
+ file.moduleDBI->addDebugSubsection(ss);
+ break;
+ case DebugSubsectionKind::InlineeLines:
+ assert(!inlineeLines.valid() &&
+ "Encountered multiple inlinee lines subsections!");
+ exitOnErr(inlineeLines.initialize(ss.getRecordData()));
break;
case DebugSubsectionKind::FrameData: {
// We need to re-write string table indices here, so save off all
// frame data subsections until we've processed the entire list of
// subsections so that we can be sure we have the string table.
- DebugFrameDataSubsectionRef FDS;
- ExitOnErr(FDS.initialize(SS.getRecordData()));
- NewFpoFrames.push_back(std::move(FDS));
+ DebugFrameDataSubsectionRef fds;
+ exitOnErr(fds.initialize(ss.getRecordData()));
+ newFpoFrames.push_back(std::move(fds));
break;
}
case DebugSubsectionKind::Symbols: {
- Linker.mergeSymbolRecords(&File, IndexMap, StringTableReferences,
- SS.getRecordData());
+ linker.mergeSymbolRecords(&file, indexMap, stringTableReferences,
+ ss.getRecordData());
break;
}
+
+ case DebugSubsectionKind::CrossScopeImports:
+ case DebugSubsectionKind::CrossScopeExports:
+ // These appear to relate to cross-module optimization, so we might use
+ // these for ThinLTO.
+ break;
+
+ case DebugSubsectionKind::ILLines:
+ case DebugSubsectionKind::FuncMDTokenMap:
+ case DebugSubsectionKind::TypeMDTokenMap:
+ case DebugSubsectionKind::MergedAssemblyInput:
+ // These appear to relate to .Net assembly info.
+ break;
+
+ case DebugSubsectionKind::CoffSymbolRVA:
+ // Unclear what this is for.
+ break;
+
default:
- // FIXME: Process the rest of the subsections.
+ warn("ignoring unknown debug$S subsection kind 0x" +
+ utohexstr(uint32_t(ss.kind())) + " in file " + toString(&file));
break;
}
}
}
+static Expected<StringRef>
+getFileName(const DebugStringTableSubsectionRef &strings,
+ const DebugChecksumsSubsectionRef &checksums, uint32_t fileID) {
+ auto iter = checksums.getArray().at(fileID);
+ if (iter == checksums.getArray().end())
+ return make_error<CodeViewError>(cv_error_code::no_records);
+ uint32_t offset = iter->FileNameOffset;
+ return strings.getString(offset);
+}
+
+std::shared_ptr<DebugInlineeLinesSubsection>
+DebugSHandler::mergeInlineeLines(DebugChecksumsSubsection *newChecksums) {
+ auto newInlineeLines = std::make_shared<DebugInlineeLinesSubsection>(
+ *newChecksums, inlineeLines.hasExtraFiles());
+
+ for (const InlineeSourceLine &line : inlineeLines) {
+ TypeIndex inlinee = line.Header->Inlinee;
+ uint32_t fileID = line.Header->FileID;
+ uint32_t sourceLine = line.Header->SourceLineNum;
+
+ ArrayRef<TypeIndex> typeOrItemMap =
+ indexMap.isTypeServerMap ? indexMap.ipiMap : indexMap.tpiMap;
+ if (!remapTypeIndex(inlinee, typeOrItemMap)) {
+ log("ignoring inlinee line record in " + file.getName() +
+ " with bad inlinee index 0x" + utohexstr(inlinee.getIndex()));
+ continue;
+ }
+
+ SmallString<128> filename =
+ exitOnErr(getFileName(cVStrTab, checksums, fileID));
+ pdbMakeAbsolute(filename);
+ newInlineeLines->addInlineSite(inlinee, filename, sourceLine);
+
+ if (inlineeLines.hasExtraFiles()) {
+ for (uint32_t extraFileId : line.ExtraFiles) {
+ filename = exitOnErr(getFileName(cVStrTab, checksums, extraFileId));
+ pdbMakeAbsolute(filename);
+ newInlineeLines->addExtraFile(filename);
+ }
+ }
+ }
+
+ return newInlineeLines;
+}
+
void DebugSHandler::finish() {
- pdb::DbiStreamBuilder &DbiBuilder = Linker.Builder.getDbiBuilder();
+ pdb::DbiStreamBuilder &dbiBuilder = linker.builder.getDbiBuilder();
// We should have seen all debug subsections across the entire object file now
// which means that if a StringTable subsection and Checksums subsection were
// present, now is the time to handle them.
- if (!CVStrTab.valid()) {
- if (Checksums.valid())
+ if (!cVStrTab.valid()) {
+ if (checksums.valid())
fatal(".debug$S sections with a checksums subsection must also contain a "
"string table subsection");
- if (!StringTableReferences.empty())
+ if (!stringTableReferences.empty())
warn("No StringTable subsection was encountered, but there are string "
"table references");
return;
@@ -1235,186 +1133,231 @@ void DebugSHandler::finish() {
// Rewrite string table indices in the Fpo Data and symbol records to refer to
// the global PDB string table instead of the object file string table.
- for (DebugFrameDataSubsectionRef &FDS : NewFpoFrames) {
- const ulittle32_t *Reloc = FDS.getRelocPtr();
- for (codeview::FrameData FD : FDS) {
- FD.RvaStart += *Reloc;
- FD.FrameFunc =
- translateStringTableIndex(FD.FrameFunc, CVStrTab, Linker.PDBStrTab);
- DbiBuilder.addNewFpoData(FD);
+ for (DebugFrameDataSubsectionRef &fds : newFpoFrames) {
+ const ulittle32_t *reloc = fds.getRelocPtr();
+ for (codeview::FrameData fd : fds) {
+ fd.RvaStart += *reloc;
+ fd.FrameFunc =
+ translateStringTableIndex(fd.FrameFunc, cVStrTab, linker.pdbStrTab);
+ dbiBuilder.addNewFpoData(fd);
}
}
- for (ulittle32_t *Ref : StringTableReferences)
- *Ref = translateStringTableIndex(*Ref, CVStrTab, Linker.PDBStrTab);
+ for (ulittle32_t *ref : stringTableReferences)
+ *ref = translateStringTableIndex(*ref, cVStrTab, linker.pdbStrTab);
// Make a new file checksum table that refers to offsets in the PDB-wide
// string table. Generally the string table subsection appears after the
// checksum table, so we have to do this after looping over all the
// subsections.
- auto NewChecksums = make_unique<DebugChecksumsSubsection>(Linker.PDBStrTab);
- for (FileChecksumEntry &FC : Checksums) {
- SmallString<128> FileName =
- ExitOnErr(CVStrTab.getString(FC.FileNameOffset));
- pdbMakeAbsolute(FileName);
- ExitOnErr(Linker.Builder.getDbiBuilder().addModuleSourceFile(
- *File.ModuleDBI, FileName));
- NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum);
+ auto newChecksums = make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
+ for (FileChecksumEntry &fc : checksums) {
+ SmallString<128> filename =
+ exitOnErr(cVStrTab.getString(fc.FileNameOffset));
+ pdbMakeAbsolute(filename);
+ exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename));
+ newChecksums->addChecksum(filename, fc.Kind, fc.Checksum);
}
- File.ModuleDBI->addDebugSubsection(std::move(NewChecksums));
+
+ // Rewrite inlinee item indices if present.
+ if (inlineeLines.valid())
+ file.moduleDBI->addDebugSubsection(mergeInlineeLines(newChecksums.get()));
+
+ file.moduleDBI->addDebugSubsection(std::move(newChecksums));
}
-void PDBLinker::addObjFile(ObjFile *File, CVIndexMap *ExternIndexMap) {
- if (File->wasProcessedForPDB())
+void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) {
+ if (file->mergedIntoPDB)
return;
- // Add a module descriptor for every object file. We need to put an absolute
- // path to the object into the PDB. If this is a plain object, we make its
- // path absolute. If it's an object in an archive, we make the archive path
- // absolute.
- bool InArchive = !File->ParentName.empty();
- SmallString<128> Path = InArchive ? File->ParentName : File->getName();
- pdbMakeAbsolute(Path);
- StringRef Name = InArchive ? File->getName() : StringRef(Path);
-
- pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
- File->ModuleDBI = &ExitOnErr(DbiBuilder.addModuleInfo(Name));
- File->ModuleDBI->setObjFileName(Path);
-
- auto Chunks = File->getChunks();
- uint32_t Modi = File->ModuleDBI->getModuleIndex();
- for (Chunk *C : Chunks) {
- auto *SecChunk = dyn_cast<SectionChunk>(C);
- if (!SecChunk || !SecChunk->Live)
- continue;
- pdb::SectionContrib SC = createSectionContrib(SecChunk, Modi);
- File->ModuleDBI->setFirstSectionContrib(SC);
- break;
- }
+ file->mergedIntoPDB = true;
// Before we can process symbol substreams from .debug$S, we need to process
// type information, file checksums, and the string table. Add type info to
// the PDB first, so that we can get the map from object file type and item
// indices to PDB type and item indices.
- CVIndexMap ObjectIndexMap;
- auto IndexMapResult =
- mergeDebugT(File, ExternIndexMap ? ExternIndexMap : &ObjectIndexMap);
+ CVIndexMap objectIndexMap;
+ auto indexMapResult =
+ mergeDebugT(file, externIndexMap ? externIndexMap : &objectIndexMap);
// If the .debug$T sections fail to merge, assume there is no debug info.
- if (!IndexMapResult) {
- if (!Config->WarnDebugInfoUnusable) {
- consumeError(IndexMapResult.takeError());
+ if (!indexMapResult) {
+ if (!config->warnDebugInfoUnusable) {
+ consumeError(indexMapResult.takeError());
return;
}
- StringRef FileName = sys::path::filename(Path);
- warn("Cannot use debug info for '" + FileName + "' [LNK4099]\n" +
+ warn("Cannot use debug info for '" + toString(file) + "' [LNK4099]\n" +
">>> failed to load reference " +
- StringRef(toString(IndexMapResult.takeError())));
+ StringRef(toString(indexMapResult.takeError())));
return;
}
- ScopedTimer T(SymbolMergingTimer);
+ ScopedTimer t(symbolMergingTimer);
- DebugSHandler DSH(*this, *File, *IndexMapResult);
+ pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
+ DebugSHandler dsh(*this, *file, *indexMapResult);
// Now do all live .debug$S and .debug$F sections.
- for (SectionChunk *DebugChunk : File->getDebugChunks()) {
- if (!DebugChunk->Live || DebugChunk->getSize() == 0)
+ for (SectionChunk *debugChunk : file->getDebugChunks()) {
+ if (!debugChunk->live || debugChunk->getSize() == 0)
continue;
- if (DebugChunk->getSectionName() == ".debug$S") {
- DSH.handleDebugS(*DebugChunk);
+ if (debugChunk->getSectionName() == ".debug$S") {
+ dsh.handleDebugS(*debugChunk);
continue;
}
- if (DebugChunk->getSectionName() == ".debug$F") {
- ArrayRef<uint8_t> RelocatedDebugContents =
- relocateDebugChunk(Alloc, *DebugChunk);
+ if (debugChunk->getSectionName() == ".debug$F") {
+ ArrayRef<uint8_t> relocatedDebugContents =
+ relocateDebugChunk(alloc, *debugChunk);
- FixedStreamArray<object::FpoData> FpoRecords;
- BinaryStreamReader Reader(RelocatedDebugContents, support::little);
- uint32_t Count = RelocatedDebugContents.size() / sizeof(object::FpoData);
- ExitOnErr(Reader.readArray(FpoRecords, Count));
+ FixedStreamArray<object::FpoData> fpoRecords;
+ BinaryStreamReader reader(relocatedDebugContents, support::little);
+ uint32_t count = relocatedDebugContents.size() / sizeof(object::FpoData);
+ exitOnErr(reader.readArray(fpoRecords, count));
// These are already relocated and don't refer to the string table, so we
// can just copy it.
- for (const object::FpoData &FD : FpoRecords)
- DbiBuilder.addOldFpoData(FD);
+ for (const object::FpoData &fd : fpoRecords)
+ dbiBuilder.addOldFpoData(fd);
continue;
}
}
// Do any post-processing now that all .debug$S sections have been processed.
- DSH.finish();
+ dsh.finish();
}
-static PublicSym32 createPublic(Defined *Def) {
- PublicSym32 Pub(SymbolKind::S_PUB32);
- Pub.Name = Def->getName();
- if (auto *D = dyn_cast<DefinedCOFF>(Def)) {
- if (D->getCOFFSymbol().isFunctionDefinition())
- Pub.Flags = PublicSymFlags::Function;
- } else if (isa<DefinedImportThunk>(Def)) {
- Pub.Flags = PublicSymFlags::Function;
+// Add a module descriptor for every object file. We need to put an absolute
+// path to the object into the PDB. If this is a plain object, we make its
+// path absolute. If it's an object in an archive, we make the archive path
+// absolute.
+static void createModuleDBI(pdb::PDBFileBuilder &builder) {
+ pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
+ SmallString<128> objName;
+
+ for (ObjFile *file : ObjFile::instances) {
+
+ bool inArchive = !file->parentName.empty();
+ objName = inArchive ? file->parentName : file->getName();
+ pdbMakeAbsolute(objName);
+ StringRef modName = inArchive ? file->getName() : StringRef(objName);
+
+ file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName));
+ file->moduleDBI->setObjFileName(objName);
+
+ ArrayRef<Chunk *> chunks = file->getChunks();
+ uint32_t modi = file->moduleDBI->getModuleIndex();
+
+ for (Chunk *c : chunks) {
+ auto *secChunk = dyn_cast<SectionChunk>(c);
+ if (!secChunk || !secChunk->live)
+ continue;
+ pdb::SectionContrib sc = createSectionContrib(secChunk, modi);
+ file->moduleDBI->setFirstSectionContrib(sc);
+ break;
+ }
}
+}
- OutputSection *OS = Def->getChunk()->getOutputSection();
- assert(OS && "all publics should be in final image");
- Pub.Offset = Def->getRVA() - OS->getRVA();
- Pub.Segment = OS->SectionIndex;
- return Pub;
+static PublicSym32 createPublic(Defined *def) {
+ PublicSym32 pub(SymbolKind::S_PUB32);
+ pub.Name = def->getName();
+ if (auto *d = dyn_cast<DefinedCOFF>(def)) {
+ if (d->getCOFFSymbol().isFunctionDefinition())
+ pub.Flags = PublicSymFlags::Function;
+ } else if (isa<DefinedImportThunk>(def)) {
+ pub.Flags = PublicSymFlags::Function;
+ }
+
+ OutputSection *os = def->getChunk()->getOutputSection();
+ assert(os && "all publics should be in final image");
+ pub.Offset = def->getRVA() - os->getRVA();
+ pub.Segment = os->sectionIndex;
+ return pub;
}
// Add all object files to the PDB. Merge .debug$T sections into IpiData and
// TpiData.
void PDBLinker::addObjectsToPDB() {
- ScopedTimer T1(AddObjectsTimer);
- for (ObjFile *File : ObjFile::Instances)
- addObjFile(File);
+ ScopedTimer t1(addObjectsTimer);
+
+ createModuleDBI(builder);
- Builder.getStringTableBuilder().setStrings(PDBStrTab);
- T1.stop();
+ for (ObjFile *file : ObjFile::instances)
+ addObjFile(file);
+
+ builder.getStringTableBuilder().setStrings(pdbStrTab);
+ t1.stop();
// Construct TPI and IPI stream contents.
- ScopedTimer T2(TpiStreamLayoutTimer);
- addTypeInfo(Builder.getTpiBuilder(), getTypeTable());
- addTypeInfo(Builder.getIpiBuilder(), getIDTable());
- T2.stop();
+ ScopedTimer t2(tpiStreamLayoutTimer);
+ addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable());
+ addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable());
+ t2.stop();
- ScopedTimer T3(GlobalsLayoutTimer);
+ ScopedTimer t3(globalsLayoutTimer);
// Compute the public and global symbols.
- auto &GsiBuilder = Builder.getGsiBuilder();
- std::vector<PublicSym32> Publics;
- Symtab->forEachSymbol([&Publics](Symbol *S) {
+ auto &gsiBuilder = builder.getGsiBuilder();
+ std::vector<PublicSym32> publics;
+ symtab->forEachSymbol([&publics](Symbol *s) {
// Only emit defined, live symbols that have a chunk.
- auto *Def = dyn_cast<Defined>(S);
- if (Def && Def->isLive() && Def->getChunk())
- Publics.push_back(createPublic(Def));
+ auto *def = dyn_cast<Defined>(s);
+ if (def && def->isLive() && def->getChunk())
+ publics.push_back(createPublic(def));
});
- if (!Publics.empty()) {
+ if (!publics.empty()) {
+ publicSymbols = publics.size();
// Sort the public symbols and add them to the stream.
- sort(parallel::par, Publics.begin(), Publics.end(),
- [](const PublicSym32 &L, const PublicSym32 &R) {
- return L.Name < R.Name;
- });
- for (const PublicSym32 &Pub : Publics)
- GsiBuilder.addPublicSymbol(Pub);
+ parallelSort(publics, [](const PublicSym32 &l, const PublicSym32 &r) {
+ return l.Name < r.Name;
+ });
+ for (const PublicSym32 &pub : publics)
+ gsiBuilder.addPublicSymbol(pub);
}
}
+void PDBLinker::printStats() {
+ if (!config->showSummary)
+ return;
+
+ SmallString<256> buffer;
+ raw_svector_ostream stream(buffer);
+
+ stream << center_justify("Summary", 80) << '\n'
+ << std::string(80, '-') << '\n';
+
+ auto print = [&](uint64_t v, StringRef s) {
+ stream << format_decimal(v, 15) << " " << s << '\n';
+ };
+
+ print(ObjFile::instances.size(),
+ "Input OBJ files (expanded from all cmd-line inputs)");
+ print(typeServerIndexMappings.size(), "PDB type server dependencies");
+ print(precompTypeIndexMappings.size(), "Precomp OBJ dependencies");
+ print(tMerger.getTypeTable().size() + tMerger.getIDTable().size(),
+ "Merged TPI records");
+ print(pdbStrTab.size(), "Output PDB strings");
+ print(globalSymbols, "Global symbol records");
+ print(moduleSymbols, "Module symbol records");
+ print(publicSymbols, "Public symbol records");
+
+ message(buffer);
+}
+
void PDBLinker::addNatvisFiles() {
- for (StringRef File : Config->NatvisFiles) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> DataOrErr =
- MemoryBuffer::getFile(File);
- if (!DataOrErr) {
- warn("Cannot open input file: " + File);
+ for (StringRef file : config->natvisFiles) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr =
+ MemoryBuffer::getFile(file);
+ if (!dataOrErr) {
+ warn("Cannot open input file: " + file);
continue;
}
- Builder.addInjectedSource(File, std::move(*DataOrErr));
+ builder.addInjectedSource(file, std::move(*dataOrErr));
}
}
-static codeview::CPUType toCodeViewMachine(COFF::MachineTypes Machine) {
- switch (Machine) {
+static codeview::CPUType toCodeViewMachine(COFF::MachineTypes machine) {
+ switch (machine) {
case COFF::IMAGE_FILE_MACHINE_AMD64:
return codeview::CPUType::X64;
case COFF::IMAGE_FILE_MACHINE_ARM:
@@ -1433,201 +1376,329 @@ static codeview::CPUType toCodeViewMachine(COFF::MachineTypes Machine) {
// Mimic MSVC which surrounds arguments containing whitespace with quotes.
// Double double-quotes are handled, so that the resulting string can be
// executed again on the cmd-line.
-static std::string quote(ArrayRef<StringRef> Args) {
- std::string R;
- R.reserve(256);
- for (StringRef A : Args) {
- if (!R.empty())
- R.push_back(' ');
- bool HasWS = A.find(' ') != StringRef::npos;
- bool HasQ = A.find('"') != StringRef::npos;
- if (HasWS || HasQ)
- R.push_back('"');
- if (HasQ) {
- SmallVector<StringRef, 4> S;
- A.split(S, '"');
- R.append(join(S, "\"\""));
+static std::string quote(ArrayRef<StringRef> args) {
+ std::string r;
+ r.reserve(256);
+ for (StringRef a : args) {
+ if (!r.empty())
+ r.push_back(' ');
+ bool hasWS = a.find(' ') != StringRef::npos;
+ bool hasQ = a.find('"') != StringRef::npos;
+ if (hasWS || hasQ)
+ r.push_back('"');
+ if (hasQ) {
+ SmallVector<StringRef, 4> s;
+ a.split(s, '"');
+ r.append(join(s, "\"\""));
} else {
- R.append(A);
+ r.append(a);
}
- if (HasWS || HasQ)
- R.push_back('"');
+ if (hasWS || hasQ)
+ r.push_back('"');
}
- return R;
+ return r;
}
-static void addCommonLinkerModuleSymbols(StringRef Path,
- pdb::DbiModuleDescriptorBuilder &Mod,
- BumpPtrAllocator &Allocator) {
- ObjNameSym ONS(SymbolRecordKind::ObjNameSym);
- Compile3Sym CS(SymbolRecordKind::Compile3Sym);
- EnvBlockSym EBS(SymbolRecordKind::EnvBlockSym);
-
- ONS.Name = "* Linker *";
- ONS.Signature = 0;
-
- CS.Machine = toCodeViewMachine(Config->Machine);
+static void fillLinkerVerRecord(Compile3Sym &cs) {
+ cs.Machine = toCodeViewMachine(config->machine);
// Interestingly, if we set the string to 0.0.0.0, then when trying to view
// local variables WinDbg emits an error that private symbols are not present.
// By setting this to a valid MSVC linker version string, local variables are
// displayed properly. As such, even though it is not representative of
// LLVM's version information, we need this for compatibility.
- CS.Flags = CompileSym3Flags::None;
- CS.VersionBackendBuild = 25019;
- CS.VersionBackendMajor = 14;
- CS.VersionBackendMinor = 10;
- CS.VersionBackendQFE = 0;
+ cs.Flags = CompileSym3Flags::None;
+ cs.VersionBackendBuild = 25019;
+ cs.VersionBackendMajor = 14;
+ cs.VersionBackendMinor = 10;
+ cs.VersionBackendQFE = 0;
// MSVC also sets the frontend to 0.0.0.0 since this is specifically for the
// linker module (which is by definition a backend), so we don't need to do
// anything here. Also, it seems we can use "LLVM Linker" for the linker name
// without any problems. Only the backend version has to be hardcoded to a
// magic number.
- CS.VersionFrontendBuild = 0;
- CS.VersionFrontendMajor = 0;
- CS.VersionFrontendMinor = 0;
- CS.VersionFrontendQFE = 0;
- CS.Version = "LLVM Linker";
- CS.setLanguage(SourceLanguage::Link);
-
- ArrayRef<StringRef> Args = makeArrayRef(Config->Argv).drop_front();
- std::string ArgStr = quote(Args);
- EBS.Fields.push_back("cwd");
+ cs.VersionFrontendBuild = 0;
+ cs.VersionFrontendMajor = 0;
+ cs.VersionFrontendMinor = 0;
+ cs.VersionFrontendQFE = 0;
+ cs.Version = "LLVM Linker";
+ cs.setLanguage(SourceLanguage::Link);
+}
+
+static void addCommonLinkerModuleSymbols(StringRef path,
+ pdb::DbiModuleDescriptorBuilder &mod,
+ BumpPtrAllocator &allocator) {
+ ObjNameSym ons(SymbolRecordKind::ObjNameSym);
+ EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym);
+ Compile3Sym cs(SymbolRecordKind::Compile3Sym);
+ fillLinkerVerRecord(cs);
+
+ ons.Name = "* Linker *";
+ ons.Signature = 0;
+
+ ArrayRef<StringRef> args = makeArrayRef(config->argv).drop_front();
+ std::string argStr = quote(args);
+ ebs.Fields.push_back("cwd");
SmallString<64> cwd;
- if (Config->PDBSourcePath.empty())
+ if (config->pdbSourcePath.empty())
sys::fs::current_path(cwd);
else
- cwd = Config->PDBSourcePath;
- EBS.Fields.push_back(cwd);
- EBS.Fields.push_back("exe");
- SmallString<64> exe = Config->Argv[0];
+ cwd = config->pdbSourcePath;
+ ebs.Fields.push_back(cwd);
+ ebs.Fields.push_back("exe");
+ SmallString<64> exe = config->argv[0];
pdbMakeAbsolute(exe);
- EBS.Fields.push_back(exe);
- EBS.Fields.push_back("pdb");
- EBS.Fields.push_back(Path);
- EBS.Fields.push_back("cmd");
- EBS.Fields.push_back(ArgStr);
- Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- ONS, Allocator, CodeViewContainer::Pdb));
- Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- CS, Allocator, CodeViewContainer::Pdb));
- Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- EBS, Allocator, CodeViewContainer::Pdb));
+ ebs.Fields.push_back(exe);
+ ebs.Fields.push_back("pdb");
+ ebs.Fields.push_back(path);
+ ebs.Fields.push_back("cmd");
+ ebs.Fields.push_back(argStr);
+ mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ ons, allocator, CodeViewContainer::Pdb));
+ mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ cs, allocator, CodeViewContainer::Pdb));
+ mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ ebs, allocator, CodeViewContainer::Pdb));
+}
+
+static void addLinkerModuleCoffGroup(PartialSection *sec,
+ pdb::DbiModuleDescriptorBuilder &mod,
+ OutputSection &os,
+ BumpPtrAllocator &allocator) {
+ // If there's a section, there's at least one chunk
+ assert(!sec->chunks.empty());
+ const Chunk *firstChunk = *sec->chunks.begin();
+ const Chunk *lastChunk = *sec->chunks.rbegin();
+
+ // Emit COFF group
+ CoffGroupSym cgs(SymbolRecordKind::CoffGroupSym);
+ cgs.Name = sec->name;
+ cgs.Segment = os.sectionIndex;
+ cgs.Offset = firstChunk->getRVA() - os.getRVA();
+ cgs.Size = lastChunk->getRVA() + lastChunk->getSize() - firstChunk->getRVA();
+ cgs.Characteristics = sec->characteristics;
+
+ // Somehow .idata sections & sections groups in the debug symbol stream have
+ // the "write" flag set. However the section header for the corresponding
+ // .idata section doesn't have it.
+ if (cgs.Name.startswith(".idata"))
+ cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE;
+
+ mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ cgs, allocator, CodeViewContainer::Pdb));
+}
+
+static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
+ OutputSection &os,
+ BumpPtrAllocator &allocator) {
+ SectionSym sym(SymbolRecordKind::SectionSym);
+ sym.Alignment = 12; // 2^12 = 4KB
+ sym.Characteristics = os.header.Characteristics;
+ sym.Length = os.getVirtualSize();
+ sym.Name = os.name;
+ sym.Rva = os.getRVA();
+ sym.SectionNumber = os.sectionIndex;
+ mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ sym, allocator, CodeViewContainer::Pdb));
+
+ // Skip COFF groups in MinGW because it adds a significant footprint to the
+ // PDB, due to each function being in its own section
+ if (config->mingw)
+ return;
+
+ // Output COFF groups for individual chunks of this section.
+ for (PartialSection *sec : os.contribSections) {
+ addLinkerModuleCoffGroup(sec, mod, os, allocator);
+ }
}
-static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &Mod,
- OutputSection &OS,
- BumpPtrAllocator &Allocator) {
- SectionSym Sym(SymbolRecordKind::SectionSym);
- Sym.Alignment = 12; // 2^12 = 4KB
- Sym.Characteristics = OS.Header.Characteristics;
- Sym.Length = OS.getVirtualSize();
- Sym.Name = OS.Name;
- Sym.Rva = OS.getRVA();
- Sym.SectionNumber = OS.SectionIndex;
- Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- Sym, Allocator, CodeViewContainer::Pdb));
+// Add all import files as modules to the PDB.
+void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
+ if (ImportFile::instances.empty())
+ return;
+
+ std::map<std::string, llvm::pdb::DbiModuleDescriptorBuilder *> dllToModuleDbi;
+
+ for (ImportFile *file : ImportFile::instances) {
+ if (!file->live)
+ continue;
+
+ if (!file->thunkSym)
+ continue;
+
+ if (!file->thunkLive)
+ continue;
+
+ std::string dll = StringRef(file->dllName).lower();
+ llvm::pdb::DbiModuleDescriptorBuilder *&mod = dllToModuleDbi[dll];
+ if (!mod) {
+ pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
+ SmallString<128> libPath = file->parentName;
+ pdbMakeAbsolute(libPath);
+ sys::path::native(libPath);
+
+ // Name modules similar to MSVC's link.exe.
+ // The first module is the simple dll filename
+ llvm::pdb::DbiModuleDescriptorBuilder &firstMod =
+ exitOnErr(dbiBuilder.addModuleInfo(file->dllName));
+ firstMod.setObjFileName(libPath);
+ pdb::SectionContrib sc =
+ createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex);
+ firstMod.setFirstSectionContrib(sc);
+
+ // The second module is where the import stream goes.
+ mod = &exitOnErr(dbiBuilder.addModuleInfo("Import:" + file->dllName));
+ mod->setObjFileName(libPath);
+ }
+
+ DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym);
+ Chunk *thunkChunk = thunk->getChunk();
+ OutputSection *thunkOS = thunkChunk->getOutputSection();
+
+ ObjNameSym ons(SymbolRecordKind::ObjNameSym);
+ Compile3Sym cs(SymbolRecordKind::Compile3Sym);
+ Thunk32Sym ts(SymbolRecordKind::Thunk32Sym);
+ ScopeEndSym es(SymbolRecordKind::ScopeEndSym);
+
+ ons.Name = file->dllName;
+ ons.Signature = 0;
+
+ fillLinkerVerRecord(cs);
+
+ ts.Name = thunk->getName();
+ ts.Parent = 0;
+ ts.End = 0;
+ ts.Next = 0;
+ ts.Thunk = ThunkOrdinal::Standard;
+ ts.Length = thunkChunk->getSize();
+ ts.Segment = thunkOS->sectionIndex;
+ ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA();
+
+ mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ ons, alloc, CodeViewContainer::Pdb));
+ mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
+ cs, alloc, CodeViewContainer::Pdb));
+
+ SmallVector<SymbolScope, 4> scopes;
+ CVSymbol newSym = codeview::SymbolSerializer::writeOneSymbol(
+ ts, alloc, CodeViewContainer::Pdb);
+ scopeStackOpen(scopes, mod->getNextSymbolOffset(), newSym);
+
+ mod->addSymbol(newSym);
+
+ newSym = codeview::SymbolSerializer::writeOneSymbol(es, alloc,
+ CodeViewContainer::Pdb);
+ scopeStackClose(scopes, mod->getNextSymbolOffset(), file);
+
+ mod->addSymbol(newSym);
+
+ pdb::SectionContrib sc =
+ createSectionContrib(thunk->getChunk(), mod->getModuleIndex());
+ mod->setFirstSectionContrib(sc);
+ }
}
// Creates a PDB file.
-void coff::createPDB(SymbolTable *Symtab,
- ArrayRef<OutputSection *> OutputSections,
- ArrayRef<uint8_t> SectionTable,
- llvm::codeview::DebugInfo *BuildId) {
- ScopedTimer T1(TotalPdbLinkTimer);
- PDBLinker PDB(Symtab);
-
- PDB.initialize(BuildId);
- PDB.addObjectsToPDB();
- PDB.addSections(OutputSections, SectionTable);
- PDB.addNatvisFiles();
-
- ScopedTimer T2(DiskCommitTimer);
- codeview::GUID Guid;
- PDB.commit(&Guid);
- memcpy(&BuildId->PDB70.Signature, &Guid, 16);
+void coff::createPDB(SymbolTable *symtab,
+ ArrayRef<OutputSection *> outputSections,
+ ArrayRef<uint8_t> sectionTable,
+ llvm::codeview::DebugInfo *buildId) {
+ ScopedTimer t1(totalPdbLinkTimer);
+ PDBLinker pdb(symtab);
+
+ pdb.initialize(buildId);
+ pdb.addObjectsToPDB();
+ pdb.addImportFilesToPDB(outputSections);
+ pdb.addSections(outputSections, sectionTable);
+ pdb.addNatvisFiles();
+
+ ScopedTimer t2(diskCommitTimer);
+ codeview::GUID guid;
+ pdb.commit(&guid);
+ memcpy(&buildId->PDB70.Signature, &guid, 16);
+
+ t2.stop();
+ t1.stop();
+ pdb.printStats();
}
-void PDBLinker::initialize(llvm::codeview::DebugInfo *BuildId) {
- ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize
+void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
+ exitOnErr(builder.initialize(4096)); // 4096 is blocksize
- BuildId->Signature.CVSignature = OMF::Signature::PDB70;
+ buildId->Signature.CVSignature = OMF::Signature::PDB70;
// Signature is set to a hash of the PDB contents when the PDB is done.
- memset(BuildId->PDB70.Signature, 0, 16);
- BuildId->PDB70.Age = 1;
+ memset(buildId->PDB70.Signature, 0, 16);
+ buildId->PDB70.Age = 1;
// Create streams in MSF for predefined streams, namely
// PDB, TPI, DBI and IPI.
- for (int I = 0; I < (int)pdb::kSpecialStreamCount; ++I)
- ExitOnErr(Builder.getMsfBuilder().addStream(0));
+ for (int i = 0; i < (int)pdb::kSpecialStreamCount; ++i)
+ exitOnErr(builder.getMsfBuilder().addStream(0));
// Add an Info stream.
- auto &InfoBuilder = Builder.getInfoBuilder();
- InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
- InfoBuilder.setHashPDBContentsToGUID(true);
+ auto &infoBuilder = builder.getInfoBuilder();
+ infoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
+ infoBuilder.setHashPDBContentsToGUID(true);
// Add an empty DBI stream.
- pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
- DbiBuilder.setAge(BuildId->PDB70.Age);
- DbiBuilder.setVersionHeader(pdb::PdbDbiV70);
- DbiBuilder.setMachineType(Config->Machine);
+ pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
+ dbiBuilder.setAge(buildId->PDB70.Age);
+ dbiBuilder.setVersionHeader(pdb::PdbDbiV70);
+ dbiBuilder.setMachineType(config->machine);
// Technically we are not link.exe 14.11, but there are known cases where
// debugging tools on Windows expect Microsoft-specific version numbers or
// they fail to work at all. Since we know we produce PDBs that are
// compatible with LINK 14.11, we set that version number here.
- DbiBuilder.setBuildNumber(14, 11);
+ dbiBuilder.setBuildNumber(14, 11);
}
-void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections,
- ArrayRef<uint8_t> SectionTable) {
+void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
+ ArrayRef<uint8_t> sectionTable) {
// It's not entirely clear what this is, but the * Linker * module uses it.
- pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
- NativePath = Config->PDBPath;
- pdbMakeAbsolute(NativePath);
- uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath);
- auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *"));
- LinkerModule.setPdbFilePathNI(PdbFilePathNI);
- addCommonLinkerModuleSymbols(NativePath, LinkerModule, Alloc);
+ pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
+ nativePath = config->pdbPath;
+ pdbMakeAbsolute(nativePath);
+ uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath);
+ auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *"));
+ linkerModule.setPdbFilePathNI(pdbFilePathNI);
+ addCommonLinkerModuleSymbols(nativePath, linkerModule, alloc);
// Add section contributions. They must be ordered by ascending RVA.
- for (OutputSection *OS : OutputSections) {
- addLinkerModuleSectionSymbol(LinkerModule, *OS, Alloc);
- for (Chunk *C : OS->Chunks) {
- pdb::SectionContrib SC =
- createSectionContrib(C, LinkerModule.getModuleIndex());
- Builder.getDbiBuilder().addSectionContrib(SC);
+ for (OutputSection *os : outputSections) {
+ addLinkerModuleSectionSymbol(linkerModule, *os, alloc);
+ for (Chunk *c : os->chunks) {
+ pdb::SectionContrib sc =
+ createSectionContrib(c, linkerModule.getModuleIndex());
+ builder.getDbiBuilder().addSectionContrib(sc);
}
}
+ // The * Linker * first section contrib is only used along with /INCREMENTAL,
+ // to provide trampolines thunks for incremental function patching. Set this
+ // as "unused" because LLD doesn't support /INCREMENTAL link.
+ pdb::SectionContrib sc =
+ createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex);
+ linkerModule.setFirstSectionContrib(sc);
+
// Add Section Map stream.
- ArrayRef<object::coff_section> Sections = {
- (const object::coff_section *)SectionTable.data(),
- SectionTable.size() / sizeof(object::coff_section)};
- SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections);
- DbiBuilder.setSectionMap(SectionMap);
+ ArrayRef<object::coff_section> sections = {
+ (const object::coff_section *)sectionTable.data(),
+ sectionTable.size() / sizeof(object::coff_section)};
+ sectionMap = pdb::DbiStreamBuilder::createSectionMap(sections);
+ dbiBuilder.setSectionMap(sectionMap);
// Add COFF section header stream.
- ExitOnErr(
- DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable));
+ exitOnErr(
+ dbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, sectionTable));
}
-void PDBLinker::commit(codeview::GUID *Guid) {
+void PDBLinker::commit(codeview::GUID *guid) {
// Write to a file.
- ExitOnErr(Builder.commit(Config->PDBPath, Guid));
-}
-
-static Expected<StringRef>
-getFileName(const DebugStringTableSubsectionRef &Strings,
- const DebugChecksumsSubsectionRef &Checksums, uint32_t FileID) {
- auto Iter = Checksums.getArray().at(FileID);
- if (Iter == Checksums.getArray().end())
- return make_error<CodeViewError>(cv_error_code::no_records);
- uint32_t Offset = Iter->FileNameOffset;
- return Strings.getString(Offset);
+ exitOnErr(builder.commit(config->pdbPath, guid));
}
static uint32_t getSecrelReloc() {
- switch (Config->Machine) {
+ switch (config->machine) {
case AMD64:
return COFF::IMAGE_REL_AMD64_SECREL;
case I386:
@@ -1646,78 +1717,78 @@ static uint32_t getSecrelReloc() {
// that are used to interpret the line table, and the offset of Addr in the line
// table are stored in the output arguments. Returns whether a line table was
// found.
-static bool findLineTable(const SectionChunk *C, uint32_t Addr,
- DebugStringTableSubsectionRef &CVStrTab,
- DebugChecksumsSubsectionRef &Checksums,
- DebugLinesSubsectionRef &Lines,
- uint32_t &OffsetInLinetable) {
- ExitOnError ExitOnErr;
- uint32_t SecrelReloc = getSecrelReloc();
-
- for (SectionChunk *DbgC : C->File->getDebugChunks()) {
- if (DbgC->getSectionName() != ".debug$S")
+static bool findLineTable(const SectionChunk *c, uint32_t addr,
+ DebugStringTableSubsectionRef &cVStrTab,
+ DebugChecksumsSubsectionRef &checksums,
+ DebugLinesSubsectionRef &lines,
+ uint32_t &offsetInLinetable) {
+ ExitOnError exitOnErr;
+ uint32_t secrelReloc = getSecrelReloc();
+
+ for (SectionChunk *dbgC : c->file->getDebugChunks()) {
+ if (dbgC->getSectionName() != ".debug$S")
continue;
- // Build a mapping of SECREL relocations in DbgC that refer to C.
- DenseMap<uint32_t, uint32_t> Secrels;
- for (const coff_relocation &R : DbgC->Relocs) {
- if (R.Type != SecrelReloc)
+ // Build a mapping of SECREL relocations in dbgC that refer to `c`.
+ DenseMap<uint32_t, uint32_t> secrels;
+ for (const coff_relocation &r : dbgC->getRelocs()) {
+ if (r.Type != secrelReloc)
continue;
- if (auto *S = dyn_cast_or_null<DefinedRegular>(
- C->File->getSymbols()[R.SymbolTableIndex]))
- if (S->getChunk() == C)
- Secrels[R.VirtualAddress] = S->getValue();
+ if (auto *s = dyn_cast_or_null<DefinedRegular>(
+ c->file->getSymbols()[r.SymbolTableIndex]))
+ if (s->getChunk() == c)
+ secrels[r.VirtualAddress] = s->getValue();
}
- ArrayRef<uint8_t> Contents =
- consumeDebugMagic(DbgC->getContents(), ".debug$S");
- DebugSubsectionArray Subsections;
- BinaryStreamReader Reader(Contents, support::little);
- ExitOnErr(Reader.readArray(Subsections, Contents.size()));
+ ArrayRef<uint8_t> contents =
+ SectionChunk::consumeDebugMagic(dbgC->getContents(), ".debug$S");
+ DebugSubsectionArray subsections;
+ BinaryStreamReader reader(contents, support::little);
+ exitOnErr(reader.readArray(subsections, contents.size()));
- for (const DebugSubsectionRecord &SS : Subsections) {
- switch (SS.kind()) {
+ for (const DebugSubsectionRecord &ss : subsections) {
+ switch (ss.kind()) {
case DebugSubsectionKind::StringTable: {
- assert(!CVStrTab.valid() &&
+ assert(!cVStrTab.valid() &&
"Encountered multiple string table subsections!");
- ExitOnErr(CVStrTab.initialize(SS.getRecordData()));
+ exitOnErr(cVStrTab.initialize(ss.getRecordData()));
break;
}
case DebugSubsectionKind::FileChecksums:
- assert(!Checksums.valid() &&
+ assert(!checksums.valid() &&
"Encountered multiple checksum subsections!");
- ExitOnErr(Checksums.initialize(SS.getRecordData()));
+ exitOnErr(checksums.initialize(ss.getRecordData()));
break;
case DebugSubsectionKind::Lines: {
- ArrayRef<uint8_t> Bytes;
- auto Ref = SS.getRecordData();
- ExitOnErr(Ref.readLongestContiguousChunk(0, Bytes));
- size_t OffsetInDbgC = Bytes.data() - DbgC->getContents().data();
+ ArrayRef<uint8_t> bytes;
+ auto ref = ss.getRecordData();
+ exitOnErr(ref.readLongestContiguousChunk(0, bytes));
+ size_t offsetInDbgC = bytes.data() - dbgC->getContents().data();
// Check whether this line table refers to C.
- auto I = Secrels.find(OffsetInDbgC);
- if (I == Secrels.end())
+ auto i = secrels.find(offsetInDbgC);
+ if (i == secrels.end())
break;
// Check whether this line table covers Addr in C.
- DebugLinesSubsectionRef LinesTmp;
- ExitOnErr(LinesTmp.initialize(BinaryStreamReader(Ref)));
- uint32_t OffsetInC = I->second + LinesTmp.header()->RelocOffset;
- if (Addr < OffsetInC || Addr >= OffsetInC + LinesTmp.header()->CodeSize)
+ DebugLinesSubsectionRef linesTmp;
+ exitOnErr(linesTmp.initialize(BinaryStreamReader(ref)));
+ uint32_t offsetInC = i->second + linesTmp.header()->RelocOffset;
+ if (addr < offsetInC || addr >= offsetInC + linesTmp.header()->CodeSize)
break;
- assert(!Lines.header() &&
+ assert(!lines.header() &&
"Encountered multiple line tables for function!");
- ExitOnErr(Lines.initialize(BinaryStreamReader(Ref)));
- OffsetInLinetable = Addr - OffsetInC;
+ exitOnErr(lines.initialize(BinaryStreamReader(ref)));
+ offsetInLinetable = addr - offsetInC;
break;
}
default:
break;
}
- if (CVStrTab.valid() && Checksums.valid() && Lines.header())
+ if (cVStrTab.valid() && checksums.valid() && lines.header())
return true;
}
}
@@ -1728,38 +1799,38 @@ static bool findLineTable(const SectionChunk *C, uint32_t Addr,
// Use CodeView line tables to resolve a file and line number for the given
// offset into the given chunk and return them, or {"", 0} if a line table was
// not found.
-std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *C,
- uint32_t Addr) {
- ExitOnError ExitOnErr;
+std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
+ uint32_t addr) {
+ ExitOnError exitOnErr;
- DebugStringTableSubsectionRef CVStrTab;
- DebugChecksumsSubsectionRef Checksums;
- DebugLinesSubsectionRef Lines;
- uint32_t OffsetInLinetable;
+ DebugStringTableSubsectionRef cVStrTab;
+ DebugChecksumsSubsectionRef checksums;
+ DebugLinesSubsectionRef lines;
+ uint32_t offsetInLinetable;
- if (!findLineTable(C, Addr, CVStrTab, Checksums, Lines, OffsetInLinetable))
+ if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable))
return {"", 0};
- Optional<uint32_t> NameIndex;
- Optional<uint32_t> LineNumber;
- for (LineColumnEntry &Entry : Lines) {
- for (const LineNumberEntry &LN : Entry.LineNumbers) {
- LineInfo LI(LN.Flags);
- if (LN.Offset > OffsetInLinetable) {
- if (!NameIndex) {
- NameIndex = Entry.NameIndex;
- LineNumber = LI.getStartLine();
+ Optional<uint32_t> nameIndex;
+ Optional<uint32_t> lineNumber;
+ for (LineColumnEntry &entry : lines) {
+ for (const LineNumberEntry &ln : entry.LineNumbers) {
+ LineInfo li(ln.Flags);
+ if (ln.Offset > offsetInLinetable) {
+ if (!nameIndex) {
+ nameIndex = entry.NameIndex;
+ lineNumber = li.getStartLine();
}
- StringRef Filename =
- ExitOnErr(getFileName(CVStrTab, Checksums, *NameIndex));
- return {Filename, *LineNumber};
+ StringRef filename =
+ exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
+ return {filename, *lineNumber};
}
- NameIndex = Entry.NameIndex;
- LineNumber = LI.getStartLine();
+ nameIndex = entry.NameIndex;
+ lineNumber = li.getStartLine();
}
}
- if (!NameIndex)
+ if (!nameIndex)
return {"", 0};
- StringRef Filename = ExitOnErr(getFileName(CVStrTab, Checksums, *NameIndex));
- return {Filename, *LineNumber};
+ StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
+ return {filename, *lineNumber};
}
diff --git a/COFF/PDB.h b/COFF/PDB.h
index ea7a9996f415..3ac1adc85c5d 100644
--- a/COFF/PDB.h
+++ b/COFF/PDB.h
@@ -1,9 +1,8 @@
//===- PDB.h ----------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -25,13 +24,13 @@ class OutputSection;
class SectionChunk;
class SymbolTable;
-void createPDB(SymbolTable *Symtab,
- llvm::ArrayRef<OutputSection *> OutputSections,
- llvm::ArrayRef<uint8_t> SectionTable,
- llvm::codeview::DebugInfo *BuildId);
+void createPDB(SymbolTable *symtab,
+ llvm::ArrayRef<OutputSection *> outputSections,
+ llvm::ArrayRef<uint8_t> sectionTable,
+ llvm::codeview::DebugInfo *buildId);
-std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *C,
- uint32_t Addr);
+std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *c,
+ uint32_t addr);
}
}
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp
index 1a9e0455dc1d..0aff164ee567 100644
--- a/COFF/SymbolTable.cpp
+++ b/COFF/SymbolTable.cpp
@@ -1,9 +1,8 @@
//===- SymbolTable.cpp ----------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -17,6 +16,7 @@
#include "lld/Common/Memory.h"
#include "lld/Common/Timer.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/Object/WindowsMachineFlag.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>
@@ -26,521 +26,576 @@ using namespace llvm;
namespace lld {
namespace coff {
-static Timer LTOTimer("LTO", Timer::root());
+static Timer ltoTimer("LTO", Timer::root());
-SymbolTable *Symtab;
+SymbolTable *symtab;
-void SymbolTable::addFile(InputFile *File) {
- log("Reading " + toString(File));
- File->parse();
+void SymbolTable::addFile(InputFile *file) {
+ log("Reading " + toString(file));
+ file->parse();
- MachineTypes MT = File->getMachineType();
- if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
- Config->Machine = MT;
- } else if (MT != IMAGE_FILE_MACHINE_UNKNOWN && Config->Machine != MT) {
- error(toString(File) + ": machine type " + machineToStr(MT) +
- " conflicts with " + machineToStr(Config->Machine));
+ MachineTypes mt = file->getMachineType();
+ if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
+ config->machine = mt;
+ } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) {
+ error(toString(file) + ": machine type " + machineToStr(mt) +
+ " conflicts with " + machineToStr(config->machine));
return;
}
- if (auto *F = dyn_cast<ObjFile>(File)) {
- ObjFile::Instances.push_back(F);
- } else if (auto *F = dyn_cast<BitcodeFile>(File)) {
- BitcodeFile::Instances.push_back(F);
- } else if (auto *F = dyn_cast<ImportFile>(File)) {
- ImportFile::Instances.push_back(F);
+ if (auto *f = dyn_cast<ObjFile>(file)) {
+ ObjFile::instances.push_back(f);
+ } else if (auto *f = dyn_cast<BitcodeFile>(file)) {
+ BitcodeFile::instances.push_back(f);
+ } else if (auto *f = dyn_cast<ImportFile>(file)) {
+ ImportFile::instances.push_back(f);
}
- StringRef S = File->getDirectives();
- if (S.empty())
- return;
-
- log("Directives: " + toString(File) + ": " + S);
- Driver->parseDirectives(S);
+ driver->parseDirectives(file);
}
-static void errorOrWarn(const Twine &S) {
- if (Config->ForceUnresolved)
- warn(S);
+static void errorOrWarn(const Twine &s) {
+ if (config->forceUnresolved)
+ warn(s);
else
- error(S);
+ error(s);
}
// Returns the symbol in SC whose value is <= Addr that is closest to Addr.
// This is generally the global variable or function whose definition contains
// Addr.
-static Symbol *getSymbol(SectionChunk *SC, uint32_t Addr) {
- DefinedRegular *Candidate = nullptr;
+static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) {
+ DefinedRegular *candidate = nullptr;
- for (Symbol *S : SC->File->getSymbols()) {
- auto *D = dyn_cast_or_null<DefinedRegular>(S);
- if (!D || D->getChunk() != SC || D->getValue() > Addr ||
- (Candidate && D->getValue() < Candidate->getValue()))
+ for (Symbol *s : sc->file->getSymbols()) {
+ auto *d = dyn_cast_or_null<DefinedRegular>(s);
+ if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr ||
+ (candidate && d->getValue() < candidate->getValue()))
continue;
- Candidate = D;
+ candidate = d;
}
- return Candidate;
+ return candidate;
}
-std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
+// Given a file and the index of a symbol in that file, returns a description
+// of all references to that symbol from that file. If no debug information is
+// available, returns just the name of the file, else one string per actual
+// reference as described in the debug info.
+std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
struct Location {
- Symbol *Sym;
- std::pair<StringRef, uint32_t> FileLine;
+ Symbol *sym;
+ std::pair<StringRef, uint32_t> fileLine;
};
- std::vector<Location> Locations;
+ std::vector<Location> locations;
- for (Chunk *C : File->getChunks()) {
- auto *SC = dyn_cast<SectionChunk>(C);
- if (!SC)
+ for (Chunk *c : file->getChunks()) {
+ auto *sc = dyn_cast<SectionChunk>(c);
+ if (!sc)
continue;
- for (const coff_relocation &R : SC->Relocs) {
- if (R.SymbolTableIndex != SymIndex)
+ for (const coff_relocation &r : sc->getRelocs()) {
+ if (r.SymbolTableIndex != symIndex)
continue;
- std::pair<StringRef, uint32_t> FileLine =
- getFileLine(SC, R.VirtualAddress);
- Symbol *Sym = getSymbol(SC, R.VirtualAddress);
- if (!FileLine.first.empty() || Sym)
- Locations.push_back({Sym, FileLine});
+ std::pair<StringRef, uint32_t> fileLine =
+ getFileLine(sc, r.VirtualAddress);
+ Symbol *sym = getSymbol(sc, r.VirtualAddress);
+ if (!fileLine.first.empty() || sym)
+ locations.push_back({sym, fileLine});
}
}
- if (Locations.empty())
- return "\n>>> referenced by " + toString(File);
+ if (locations.empty())
+ return std::vector<std::string>({"\n>>> referenced by " + toString(file)});
- std::string Out;
- llvm::raw_string_ostream OS(Out);
- for (Location Loc : Locations) {
- OS << "\n>>> referenced by ";
- if (!Loc.FileLine.first.empty())
- OS << Loc.FileLine.first << ":" << Loc.FileLine.second
+ std::vector<std::string> symbolLocations(locations.size());
+ size_t i = 0;
+ for (Location loc : locations) {
+ llvm::raw_string_ostream os(symbolLocations[i++]);
+ os << "\n>>> referenced by ";
+ if (!loc.fileLine.first.empty())
+ os << loc.fileLine.first << ":" << loc.fileLine.second
<< "\n>>> ";
- OS << toString(File);
- if (Loc.Sym)
- OS << ":(" << toString(*Loc.Sym) << ')';
+ os << toString(file);
+ if (loc.sym)
+ os << ":(" << toString(*loc.sym) << ')';
+ }
+ return symbolLocations;
+}
+
+// For an undefined symbol, stores all files referencing it and the index of
+// the undefined symbol in each file.
+struct UndefinedDiag {
+ Symbol *sym;
+ struct File {
+ ObjFile *oFile;
+ uint64_t symIndex;
+ };
+ std::vector<File> files;
+};
+
+static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
+ std::string out;
+ llvm::raw_string_ostream os(out);
+ os << "undefined symbol: " << toString(*undefDiag.sym);
+
+ const size_t maxUndefReferences = 10;
+ size_t i = 0, numRefs = 0;
+ for (const UndefinedDiag::File &ref : undefDiag.files) {
+ std::vector<std::string> symbolLocations =
+ getSymbolLocations(ref.oFile, ref.symIndex);
+ numRefs += symbolLocations.size();
+ for (const std::string &s : symbolLocations) {
+ if (i >= maxUndefReferences)
+ break;
+ os << s;
+ i++;
+ }
}
- return OS.str();
+ if (i < numRefs)
+ os << "\n>>> referenced " << numRefs - i << " more times";
+ errorOrWarn(os.str());
}
void SymbolTable::loadMinGWAutomaticImports() {
- for (auto &I : SymMap) {
- Symbol *Sym = I.second;
- auto *Undef = dyn_cast<Undefined>(Sym);
- if (!Undef)
+ for (auto &i : symMap) {
+ Symbol *sym = i.second;
+ auto *undef = dyn_cast<Undefined>(sym);
+ if (!undef)
continue;
- if (!Sym->IsUsedInRegularObj)
+ if (!sym->isUsedInRegularObj)
continue;
- StringRef Name = Undef->getName();
+ StringRef name = undef->getName();
- if (Name.startswith("__imp_"))
+ if (name.startswith("__imp_"))
continue;
// If we have an undefined symbol, but we have a Lazy representing a
// symbol we could load from file, make sure to load that.
- Lazy *L = dyn_cast_or_null<Lazy>(find(("__imp_" + Name).str()));
- if (!L || L->PendingArchiveLoad)
+ Lazy *l = dyn_cast_or_null<Lazy>(find(("__imp_" + name).str()));
+ if (!l || l->pendingArchiveLoad)
continue;
- log("Loading lazy " + L->getName() + " from " + L->File->getName() +
+ log("Loading lazy " + l->getName() + " from " + l->file->getName() +
" for automatic import");
- L->PendingArchiveLoad = true;
- L->File->addMember(&L->Sym);
+ l->pendingArchiveLoad = true;
+ l->file->addMember(&l->sym);
}
}
-bool SymbolTable::handleMinGWAutomaticImport(Symbol *Sym, StringRef Name) {
- if (Name.startswith("__imp_"))
+bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
+ if (name.startswith("__imp_"))
return false;
- Defined *Imp = dyn_cast_or_null<Defined>(find(("__imp_" + Name).str()));
- if (!Imp)
+ Defined *imp = dyn_cast_or_null<Defined>(find(("__imp_" + name).str()));
+ if (!imp)
return false;
// Replace the reference directly to a variable with a reference
// to the import address table instead. This obviously isn't right,
- // but we mark the symbol as IsRuntimePseudoReloc, and a later pass
+ // but we mark the symbol as isRuntimePseudoReloc, and a later pass
// will add runtime pseudo relocations for every relocation against
// this Symbol. The runtime pseudo relocation framework expects the
// reference itself to point at the IAT entry.
- size_t ImpSize = 0;
- if (isa<DefinedImportData>(Imp)) {
- log("Automatically importing " + Name + " from " +
- cast<DefinedImportData>(Imp)->getDLLName());
- ImpSize = sizeof(DefinedImportData);
- } else if (isa<DefinedRegular>(Imp)) {
- log("Automatically importing " + Name + " from " +
- toString(cast<DefinedRegular>(Imp)->File));
- ImpSize = sizeof(DefinedRegular);
+ size_t impSize = 0;
+ if (isa<DefinedImportData>(imp)) {
+ log("Automatically importing " + name + " from " +
+ cast<DefinedImportData>(imp)->getDLLName());
+ impSize = sizeof(DefinedImportData);
+ } else if (isa<DefinedRegular>(imp)) {
+ log("Automatically importing " + name + " from " +
+ toString(cast<DefinedRegular>(imp)->file));
+ impSize = sizeof(DefinedRegular);
} else {
- warn("unable to automatically import " + Name + " from " + Imp->getName() +
- " from " + toString(cast<DefinedRegular>(Imp)->File) +
+ warn("unable to automatically import " + name + " from " + imp->getName() +
+ " from " + toString(cast<DefinedRegular>(imp)->file) +
"; unexpected symbol type");
return false;
}
- Sym->replaceKeepingName(Imp, ImpSize);
- Sym->IsRuntimePseudoReloc = true;
+ sym->replaceKeepingName(imp, impSize);
+ sym->isRuntimePseudoReloc = true;
// There may exist symbols named .refptr.<name> which only consist
// of a single pointer to <name>. If it turns out <name> is
// automatically imported, we don't need to keep the .refptr.<name>
// pointer at all, but redirect all accesses to it to the IAT entry
// for __imp_<name> instead, and drop the whole .refptr.<name> chunk.
- DefinedRegular *Refptr =
- dyn_cast_or_null<DefinedRegular>(find((".refptr." + Name).str()));
- if (Refptr && Refptr->getChunk()->getSize() == Config->Wordsize) {
- SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Refptr->getChunk());
- if (SC && SC->Relocs.size() == 1 && *SC->symbols().begin() == Sym) {
- log("Replacing .refptr." + Name + " with " + Imp->getName());
- Refptr->getChunk()->Live = false;
- Refptr->replaceKeepingName(Imp, ImpSize);
+ DefinedRegular *refptr =
+ dyn_cast_or_null<DefinedRegular>(find((".refptr." + name).str()));
+ if (refptr && refptr->getChunk()->getSize() == config->wordsize) {
+ SectionChunk *sc = dyn_cast_or_null<SectionChunk>(refptr->getChunk());
+ if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) {
+ log("Replacing .refptr." + name + " with " + imp->getName());
+ refptr->getChunk()->live = false;
+ refptr->replaceKeepingName(imp, impSize);
}
}
return true;
}
void SymbolTable::reportRemainingUndefines() {
- SmallPtrSet<Symbol *, 8> Undefs;
- DenseMap<Symbol *, Symbol *> LocalImports;
+ SmallPtrSet<Symbol *, 8> undefs;
+ DenseMap<Symbol *, Symbol *> localImports;
- for (auto &I : SymMap) {
- Symbol *Sym = I.second;
- auto *Undef = dyn_cast<Undefined>(Sym);
- if (!Undef)
+ for (auto &i : symMap) {
+ Symbol *sym = i.second;
+ auto *undef = dyn_cast<Undefined>(sym);
+ if (!undef)
continue;
- if (!Sym->IsUsedInRegularObj)
+ if (!sym->isUsedInRegularObj)
continue;
- StringRef Name = Undef->getName();
+ StringRef name = undef->getName();
// A weak alias may have been resolved, so check for that.
- if (Defined *D = Undef->getWeakAlias()) {
+ if (Defined *d = undef->getWeakAlias()) {
// We want to replace Sym with D. However, we can't just blindly
// copy sizeof(SymbolUnion) bytes from D to Sym because D may be an
// internal symbol, and internal symbols are stored as "unparented"
// Symbols. For that reason we need to check which type of symbol we
// are dealing with and copy the correct number of bytes.
- if (isa<DefinedRegular>(D))
- memcpy(Sym, D, sizeof(DefinedRegular));
- else if (isa<DefinedAbsolute>(D))
- memcpy(Sym, D, sizeof(DefinedAbsolute));
+ if (isa<DefinedRegular>(d))
+ memcpy(sym, d, sizeof(DefinedRegular));
+ else if (isa<DefinedAbsolute>(d))
+ memcpy(sym, d, sizeof(DefinedAbsolute));
else
- memcpy(Sym, D, sizeof(SymbolUnion));
+ memcpy(sym, d, sizeof(SymbolUnion));
continue;
}
// If we can resolve a symbol by removing __imp_ prefix, do that.
// This odd rule is for compatibility with MSVC linker.
- if (Name.startswith("__imp_")) {
- Symbol *Imp = find(Name.substr(strlen("__imp_")));
- if (Imp && isa<Defined>(Imp)) {
- auto *D = cast<Defined>(Imp);
- replaceSymbol<DefinedLocalImport>(Sym, Name, D);
- LocalImportChunks.push_back(cast<DefinedLocalImport>(Sym)->getChunk());
- LocalImports[Sym] = D;
+ if (name.startswith("__imp_")) {
+ Symbol *imp = find(name.substr(strlen("__imp_")));
+ if (imp && isa<Defined>(imp)) {
+ auto *d = cast<Defined>(imp);
+ replaceSymbol<DefinedLocalImport>(sym, name, d);
+ localImportChunks.push_back(cast<DefinedLocalImport>(sym)->getChunk());
+ localImports[sym] = d;
continue;
}
}
// We don't want to report missing Microsoft precompiled headers symbols.
// A proper message will be emitted instead in PDBLinker::aquirePrecompObj
- if (Name.contains("_PchSym_"))
+ if (name.contains("_PchSym_"))
continue;
- if (Config->MinGW && handleMinGWAutomaticImport(Sym, Name))
+ if (config->mingw && handleMinGWAutomaticImport(sym, name))
continue;
// Remaining undefined symbols are not fatal if /force is specified.
// They are replaced with dummy defined symbols.
- if (Config->ForceUnresolved)
- replaceSymbol<DefinedAbsolute>(Sym, Name, 0);
- Undefs.insert(Sym);
+ if (config->forceUnresolved)
+ replaceSymbol<DefinedAbsolute>(sym, name, 0);
+ undefs.insert(sym);
}
- if (Undefs.empty() && LocalImports.empty())
+ if (undefs.empty() && localImports.empty())
return;
- for (Symbol *B : Config->GCRoot) {
- if (Undefs.count(B))
- errorOrWarn("<root>: undefined symbol: " + toString(*B));
- if (Config->WarnLocallyDefinedImported)
- if (Symbol *Imp = LocalImports.lookup(B))
- warn("<root>: locally defined symbol imported: " + toString(*Imp) +
- " (defined in " + toString(Imp->getFile()) + ") [LNK4217]");
+ for (Symbol *b : config->gcroot) {
+ if (undefs.count(b))
+ errorOrWarn("<root>: undefined symbol: " + toString(*b));
+ if (config->warnLocallyDefinedImported)
+ if (Symbol *imp = localImports.lookup(b))
+ warn("<root>: locally defined symbol imported: " + toString(*imp) +
+ " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
}
- for (ObjFile *File : ObjFile::Instances) {
- size_t SymIndex = (size_t)-1;
- for (Symbol *Sym : File->getSymbols()) {
- ++SymIndex;
- if (!Sym)
+ std::vector<UndefinedDiag> undefDiags;
+ DenseMap<Symbol *, int> firstDiag;
+
+ for (ObjFile *file : ObjFile::instances) {
+ size_t symIndex = (size_t)-1;
+ for (Symbol *sym : file->getSymbols()) {
+ ++symIndex;
+ if (!sym)
continue;
- if (Undefs.count(Sym))
- errorOrWarn("undefined symbol: " + toString(*Sym) +
- getSymbolLocations(File, SymIndex));
- if (Config->WarnLocallyDefinedImported)
- if (Symbol *Imp = LocalImports.lookup(Sym))
- warn(toString(File) +
- ": locally defined symbol imported: " + toString(*Imp) +
- " (defined in " + toString(Imp->getFile()) + ") [LNK4217]");
+ if (undefs.count(sym)) {
+ auto it = firstDiag.find(sym);
+ if (it == firstDiag.end()) {
+ firstDiag[sym] = undefDiags.size();
+ undefDiags.push_back({sym, {{file, symIndex}}});
+ } else {
+ undefDiags[it->second].files.push_back({file, symIndex});
+ }
+ }
+ if (config->warnLocallyDefinedImported)
+ if (Symbol *imp = localImports.lookup(sym))
+ warn(toString(file) +
+ ": locally defined symbol imported: " + toString(*imp) +
+ " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
}
}
+
+ for (const UndefinedDiag& undefDiag : undefDiags)
+ reportUndefinedSymbol(undefDiag);
}
-std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
- bool Inserted = false;
- Symbol *&Sym = SymMap[CachedHashStringRef(Name)];
- if (!Sym) {
- Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
- Sym->IsUsedInRegularObj = false;
- Sym->PendingArchiveLoad = false;
- Inserted = true;
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
+ bool inserted = false;
+ Symbol *&sym = symMap[CachedHashStringRef(name)];
+ if (!sym) {
+ sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
+ sym->isUsedInRegularObj = false;
+ sym->pendingArchiveLoad = false;
+ inserted = true;
}
- return {Sym, Inserted};
+ return {sym, inserted};
}
-std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name, InputFile *File) {
- std::pair<Symbol *, bool> Result = insert(Name);
- if (!File || !isa<BitcodeFile>(File))
- Result.first->IsUsedInRegularObj = true;
- return Result;
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) {
+ std::pair<Symbol *, bool> result = insert(name);
+ if (!file || !isa<BitcodeFile>(file))
+ result.first->isUsedInRegularObj = true;
+ return result;
}
-Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F,
- bool IsWeakAlias) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name, F);
- if (WasInserted || (isa<Lazy>(S) && IsWeakAlias)) {
- replaceSymbol<Undefined>(S, Name);
- return S;
+Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
+ bool isWeakAlias) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name, f);
+ if (wasInserted || (isa<Lazy>(s) && isWeakAlias)) {
+ replaceSymbol<Undefined>(s, name);
+ return s;
}
- if (auto *L = dyn_cast<Lazy>(S)) {
- if (!S->PendingArchiveLoad) {
- S->PendingArchiveLoad = true;
- L->File->addMember(&L->Sym);
+ if (auto *l = dyn_cast<Lazy>(s)) {
+ if (!s->pendingArchiveLoad) {
+ s->pendingArchiveLoad = true;
+ l->file->addMember(&l->sym);
}
}
- return S;
+ return s;
}
-void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
- StringRef Name = Sym.getName();
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name);
- if (WasInserted) {
- replaceSymbol<Lazy>(S, F, Sym);
+void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol sym) {
+ StringRef name = sym.getName();
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name);
+ if (wasInserted) {
+ replaceSymbol<Lazy>(s, f, sym);
return;
}
- auto *U = dyn_cast<Undefined>(S);
- if (!U || U->WeakAlias || S->PendingArchiveLoad)
+ auto *u = dyn_cast<Undefined>(s);
+ if (!u || u->weakAlias || s->pendingArchiveLoad)
return;
- S->PendingArchiveLoad = true;
- F->addMember(&Sym);
+ s->pendingArchiveLoad = true;
+ f->addMember(&sym);
}
-void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
- std::string Msg = "duplicate symbol: " + toString(*Existing) + " in " +
- toString(Existing->getFile()) + " and in " +
- toString(NewFile);
+void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) {
+ std::string msg = "duplicate symbol: " + toString(*existing) + " in " +
+ toString(existing->getFile()) + " and in " +
+ toString(newFile);
- if (Config->ForceMultiple)
- warn(Msg);
+ if (config->forceMultiple)
+ warn(msg);
else
- error(Msg);
+ error(msg);
}
-Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, nullptr);
- S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
- replaceSymbol<DefinedAbsolute>(S, N, Sym);
- else if (!isa<DefinedCOFF>(S))
- reportDuplicate(S, nullptr);
- return S;
+Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, nullptr);
+ s->isUsedInRegularObj = true;
+ if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
+ replaceSymbol<DefinedAbsolute>(s, n, sym);
+ else if (!isa<DefinedCOFF>(s))
+ reportDuplicate(s, nullptr);
+ return s;
}
-Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, nullptr);
- S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
- replaceSymbol<DefinedAbsolute>(S, N, VA);
- else if (!isa<DefinedCOFF>(S))
- reportDuplicate(S, nullptr);
- return S;
+Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, nullptr);
+ s->isUsedInRegularObj = true;
+ if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
+ replaceSymbol<DefinedAbsolute>(s, n, va);
+ else if (!isa<DefinedCOFF>(s))
+ reportDuplicate(s, nullptr);
+ return s;
}
-Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, nullptr);
- S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
- replaceSymbol<DefinedSynthetic>(S, N, C);
- else if (!isa<DefinedCOFF>(S))
- reportDuplicate(S, nullptr);
- return S;
+Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, nullptr);
+ s->isUsedInRegularObj = true;
+ if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
+ replaceSymbol<DefinedSynthetic>(s, n, c);
+ else if (!isa<DefinedCOFF>(s))
+ reportDuplicate(s, nullptr);
+ return s;
}
-Symbol *SymbolTable::addRegular(InputFile *F, StringRef N,
- const coff_symbol_generic *Sym,
- SectionChunk *C) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, F);
- if (WasInserted || !isa<DefinedRegular>(S))
- replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ false,
- /*IsExternal*/ true, Sym, C);
+Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
+ const coff_symbol_generic *sym,
+ SectionChunk *c) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, f);
+ if (wasInserted || !isa<DefinedRegular>(s))
+ replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false,
+ /*IsExternal*/ true, sym, c);
else
- reportDuplicate(S, F);
- return S;
+ reportDuplicate(s, f);
+ return s;
}
-std::pair<Symbol *, bool>
-SymbolTable::addComdat(InputFile *F, StringRef N,
- const coff_symbol_generic *Sym) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, F);
- if (WasInserted || !isa<DefinedRegular>(S)) {
- replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ true,
- /*IsExternal*/ true, Sym, nullptr);
- return {S, true};
+std::pair<DefinedRegular *, bool>
+SymbolTable::addComdat(InputFile *f, StringRef n,
+ const coff_symbol_generic *sym) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, f);
+ if (wasInserted || !isa<DefinedRegular>(s)) {
+ replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ true,
+ /*IsExternal*/ true, sym, nullptr);
+ return {cast<DefinedRegular>(s), true};
}
- if (!cast<DefinedRegular>(S)->isCOMDAT())
- reportDuplicate(S, F);
- return {S, false};
+ auto *existingSymbol = cast<DefinedRegular>(s);
+ if (!existingSymbol->isCOMDAT)
+ reportDuplicate(s, f);
+ return {existingSymbol, false};
}
-Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
- const coff_symbol_generic *Sym, CommonChunk *C) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, F);
- if (WasInserted || !isa<DefinedCOFF>(S))
- replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C);
- else if (auto *DC = dyn_cast<DefinedCommon>(S))
- if (Size > DC->getSize())
- replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C);
- return S;
+Symbol *SymbolTable::addCommon(InputFile *f, StringRef n, uint64_t size,
+ const coff_symbol_generic *sym, CommonChunk *c) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, f);
+ if (wasInserted || !isa<DefinedCOFF>(s))
+ replaceSymbol<DefinedCommon>(s, f, n, size, sym, c);
+ else if (auto *dc = dyn_cast<DefinedCommon>(s))
+ if (size > dc->getSize())
+ replaceSymbol<DefinedCommon>(s, f, n, size, sym, c);
+ return s;
}
-Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(N, nullptr);
- S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
- replaceSymbol<DefinedImportData>(S, N, F);
- return S;
+Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, nullptr);
+ s->isUsedInRegularObj = true;
+ if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
+ replaceSymbol<DefinedImportData>(s, n, f);
+ return s;
}
- reportDuplicate(S, F);
+ reportDuplicate(s, f);
return nullptr;
}
-Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID,
- uint16_t Machine) {
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name, nullptr);
- S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
- replaceSymbol<DefinedImportThunk>(S, Name, ID, Machine);
- return S;
+Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
+ uint16_t machine) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name, nullptr);
+ s->isUsedInRegularObj = true;
+ if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
+ replaceSymbol<DefinedImportThunk>(s, name, id, machine);
+ return s;
}
- reportDuplicate(S, ID->File);
+ reportDuplicate(s, id->file);
return nullptr;
}
std::vector<Chunk *> SymbolTable::getChunks() {
- std::vector<Chunk *> Res;
- for (ObjFile *File : ObjFile::Instances) {
- ArrayRef<Chunk *> V = File->getChunks();
- Res.insert(Res.end(), V.begin(), V.end());
+ std::vector<Chunk *> res;
+ for (ObjFile *file : ObjFile::instances) {
+ ArrayRef<Chunk *> v = file->getChunks();
+ res.insert(res.end(), v.begin(), v.end());
}
- return Res;
+ return res;
}
-Symbol *SymbolTable::find(StringRef Name) {
- return SymMap.lookup(CachedHashStringRef(Name));
+Symbol *SymbolTable::find(StringRef name) {
+ return symMap.lookup(CachedHashStringRef(name));
}
-Symbol *SymbolTable::findUnderscore(StringRef Name) {
- if (Config->Machine == I386)
- return find(("_" + Name).str());
- return find(Name);
+Symbol *SymbolTable::findUnderscore(StringRef name) {
+ if (config->machine == I386)
+ return find(("_" + name).str());
+ return find(name);
}
-StringRef SymbolTable::findByPrefix(StringRef Prefix) {
- for (auto Pair : SymMap) {
- StringRef Name = Pair.first.val();
- if (Name.startswith(Prefix))
- return Name;
+// Return all symbols that start with Prefix, possibly ignoring the first
+// character of Prefix or the first character symbol.
+std::vector<Symbol *> SymbolTable::getSymsWithPrefix(StringRef prefix) {
+ std::vector<Symbol *> syms;
+ for (auto pair : symMap) {
+ StringRef name = pair.first.val();
+ if (name.startswith(prefix) || name.startswith(prefix.drop_front()) ||
+ name.drop_front().startswith(prefix) ||
+ name.drop_front().startswith(prefix.drop_front())) {
+ syms.push_back(pair.second);
+ }
}
- return "";
+ return syms;
}
-StringRef SymbolTable::findMangle(StringRef Name) {
- if (Symbol *Sym = find(Name))
- if (!isa<Undefined>(Sym))
- return Name;
- if (Config->Machine != I386)
- return findByPrefix(("?" + Name + "@@Y").str());
- if (!Name.startswith("_"))
- return "";
+Symbol *SymbolTable::findMangle(StringRef name) {
+ if (Symbol *sym = find(name))
+ if (!isa<Undefined>(sym))
+ return sym;
+
+ // Efficient fuzzy string lookup is impossible with a hash table, so iterate
+ // the symbol table once and collect all possibly matching symbols into this
+ // vector. Then compare each possibly matching symbol with each possible
+ // mangling.
+ std::vector<Symbol *> syms = getSymsWithPrefix(name);
+ auto findByPrefix = [&syms](const Twine &t) -> Symbol * {
+ std::string prefix = t.str();
+ for (auto *s : syms)
+ if (s->getName().startswith(prefix))
+ return s;
+ return nullptr;
+ };
+
+ // For non-x86, just look for C++ functions.
+ if (config->machine != I386)
+ return findByPrefix("?" + name + "@@Y");
+
+ if (!name.startswith("_"))
+ return nullptr;
// Search for x86 stdcall function.
- StringRef S = findByPrefix((Name + "@").str());
- if (!S.empty())
- return S;
+ if (Symbol *s = findByPrefix(name + "@"))
+ return s;
// Search for x86 fastcall function.
- S = findByPrefix(("@" + Name.substr(1) + "@").str());
- if (!S.empty())
- return S;
+ if (Symbol *s = findByPrefix("@" + name.substr(1) + "@"))
+ return s;
// Search for x86 vectorcall function.
- S = findByPrefix((Name.substr(1) + "@@").str());
- if (!S.empty())
- return S;
+ if (Symbol *s = findByPrefix(name.substr(1) + "@@"))
+ return s;
// Search for x86 C++ non-member function.
- return findByPrefix(("?" + Name.substr(1) + "@@Y").str());
-}
-
-void SymbolTable::mangleMaybe(Symbol *B) {
- auto *U = dyn_cast<Undefined>(B);
- if (!U || U->WeakAlias)
- return;
- StringRef Alias = findMangle(U->getName());
- if (!Alias.empty()) {
- log(U->getName() + " aliased to " + Alias);
- U->WeakAlias = addUndefined(Alias);
- }
+ return findByPrefix("?" + name.substr(1) + "@@Y");
}
-Symbol *SymbolTable::addUndefined(StringRef Name) {
- return addUndefined(Name, nullptr, false);
+Symbol *SymbolTable::addUndefined(StringRef name) {
+ return addUndefined(name, nullptr, false);
}
std::vector<StringRef> SymbolTable::compileBitcodeFiles() {
- LTO.reset(new BitcodeCompiler);
- for (BitcodeFile *F : BitcodeFile::Instances)
- LTO->add(*F);
- return LTO->compile();
+ lto.reset(new BitcodeCompiler);
+ for (BitcodeFile *f : BitcodeFile::instances)
+ lto->add(*f);
+ return lto->compile();
}
void SymbolTable::addCombinedLTOObjects() {
- if (BitcodeFile::Instances.empty())
+ if (BitcodeFile::instances.empty())
return;
- ScopedTimer T(LTOTimer);
- for (StringRef Object : compileBitcodeFiles()) {
- auto *Obj = make<ObjFile>(MemoryBufferRef(Object, "lto.tmp"));
- Obj->parse();
- ObjFile::Instances.push_back(Obj);
+ ScopedTimer t(ltoTimer);
+ for (StringRef object : compileBitcodeFiles()) {
+ auto *obj = make<ObjFile>(MemoryBufferRef(object, "lto.tmp"));
+ obj->parse();
+ ObjFile::instances.push_back(obj);
}
}
diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h
index 00e55dbb7a02..88f47cbe9e78 100644
--- a/COFF/SymbolTable.h
+++ b/COFF/SymbolTable.h
@@ -1,9 +1,8 @@
//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -28,6 +27,7 @@ class Chunk;
class CommonChunk;
class Defined;
class DefinedAbsolute;
+class DefinedRegular;
class DefinedRelative;
class Lazy;
class SectionChunk;
@@ -47,7 +47,7 @@ class Symbol;
// There is one add* function per symbol type.
class SymbolTable {
public:
- void addFile(InputFile *File);
+ void addFile(InputFile *file);
// Try to resolve any undefined symbols and update the symbol table
// accordingly, then print an error message for any remaining undefined
@@ -55,21 +55,20 @@ public:
void reportRemainingUndefines();
void loadMinGWAutomaticImports();
- bool handleMinGWAutomaticImport(Symbol *Sym, StringRef Name);
+ bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
// Returns a list of chunks of selected symbols.
std::vector<Chunk *> getChunks();
// Returns a symbol for a given name. Returns a nullptr if not found.
- Symbol *find(StringRef Name);
- Symbol *findUnderscore(StringRef Name);
+ Symbol *find(StringRef name);
+ Symbol *findUnderscore(StringRef name);
// Occasionally we have to resolve an undefined symbol to its
// mangled symbol. This function tries to find a mangled name
// for U from the symbol table, and if found, set the symbol as
// a weak alias for U.
- void mangleMaybe(Symbol *B);
- StringRef findMangle(StringRef Name);
+ Symbol *findMangle(StringRef name);
// Build a set of COFF objects representing the combined contents of
// BitcodeFiles and add them to the symbol table. Called after all files are
@@ -78,52 +77,53 @@ public:
std::vector<StringRef> compileBitcodeFiles();
// Creates an Undefined symbol for a given name.
- Symbol *addUndefined(StringRef Name);
-
- Symbol *addSynthetic(StringRef N, Chunk *C);
- Symbol *addAbsolute(StringRef N, uint64_t VA);
-
- Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias);
- void addLazy(ArchiveFile *F, const Archive::Symbol Sym);
- Symbol *addAbsolute(StringRef N, COFFSymbolRef S);
- Symbol *addRegular(InputFile *F, StringRef N,
- const llvm::object::coff_symbol_generic *S = nullptr,
- SectionChunk *C = nullptr);
- std::pair<Symbol *, bool>
- addComdat(InputFile *F, StringRef N,
- const llvm::object::coff_symbol_generic *S = nullptr);
- Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size,
- const llvm::object::coff_symbol_generic *S = nullptr,
- CommonChunk *C = nullptr);
- Symbol *addImportData(StringRef N, ImportFile *F);
- Symbol *addImportThunk(StringRef Name, DefinedImportData *S,
- uint16_t Machine);
-
- void reportDuplicate(Symbol *Existing, InputFile *NewFile);
+ Symbol *addUndefined(StringRef name);
+
+ Symbol *addSynthetic(StringRef n, Chunk *c);
+ Symbol *addAbsolute(StringRef n, uint64_t va);
+
+ Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias);
+ void addLazy(ArchiveFile *f, const Archive::Symbol sym);
+ Symbol *addAbsolute(StringRef n, COFFSymbolRef s);
+ Symbol *addRegular(InputFile *f, StringRef n,
+ const llvm::object::coff_symbol_generic *s = nullptr,
+ SectionChunk *c = nullptr);
+ std::pair<DefinedRegular *, bool>
+ addComdat(InputFile *f, StringRef n,
+ const llvm::object::coff_symbol_generic *s = nullptr);
+ Symbol *addCommon(InputFile *f, StringRef n, uint64_t size,
+ const llvm::object::coff_symbol_generic *s = nullptr,
+ CommonChunk *c = nullptr);
+ Symbol *addImportData(StringRef n, ImportFile *f);
+ Symbol *addImportThunk(StringRef name, DefinedImportData *s,
+ uint16_t machine);
+
+ void reportDuplicate(Symbol *existing, InputFile *newFile);
// A list of chunks which to be added to .rdata.
- std::vector<Chunk *> LocalImportChunks;
+ std::vector<Chunk *> localImportChunks;
// Iterates symbols in non-determinstic hash table order.
- template <typename T> void forEachSymbol(T Callback) {
- for (auto &Pair : SymMap)
- Callback(Pair.second);
+ template <typename T> void forEachSymbol(T callback) {
+ for (auto &pair : symMap)
+ callback(pair.second);
}
private:
/// Inserts symbol if not already present.
- std::pair<Symbol *, bool> insert(StringRef Name);
- /// Same as insert(Name), but also sets IsUsedInRegularObj.
- std::pair<Symbol *, bool> insert(StringRef Name, InputFile *F);
- StringRef findByPrefix(StringRef Prefix);
+ std::pair<Symbol *, bool> insert(StringRef name);
+ /// Same as insert(Name), but also sets isUsedInRegularObj.
+ std::pair<Symbol *, bool> insert(StringRef name, InputFile *f);
+
+ std::vector<Symbol *> getSymsWithPrefix(StringRef prefix);
- llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap;
- std::unique_ptr<BitcodeCompiler> LTO;
+ llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> symMap;
+ std::unique_ptr<BitcodeCompiler> lto;
};
-extern SymbolTable *Symtab;
+extern SymbolTable *symtab;
-std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex);
+std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex);
} // namespace coff
} // namespace lld
diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp
index ccaf86417f10..3583d4cb28c1 100644
--- a/COFF/Symbols.cpp
+++ b/COFF/Symbols.cpp
@@ -1,9 +1,8 @@
//===- Symbols.cpp --------------------------------------------------------===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -19,11 +18,17 @@
using namespace llvm;
using namespace llvm::object;
+using namespace lld::coff;
+
+static_assert(sizeof(SymbolUnion) <= 48,
+ "symbols should be optimized for memory usage");
+
// Returns a symbol name for an error message.
-std::string lld::toString(coff::Symbol &B) {
- if (Optional<std::string> S = lld::demangleMSVC(B.getName()))
- return ("\"" + *S + "\" (" + B.getName() + ")").str();
- return B.getName();
+std::string lld::toString(coff::Symbol &b) {
+ if (config->demangle)
+ if (Optional<std::string> s = lld::demangleMSVC(b.getName()))
+ return *s;
+ return b.getName();
}
namespace lld {
@@ -37,70 +42,75 @@ StringRef Symbol::getName() {
// name. Object files contain lots of non-external symbols, and creating
// StringRefs for them (which involves lots of strlen() on the string table)
// is a waste of time.
- if (Name.empty()) {
- auto *D = cast<DefinedCOFF>(this);
- cast<ObjFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name);
+ if (nameData == nullptr) {
+ auto *d = cast<DefinedCOFF>(this);
+ StringRef nameStr;
+ cast<ObjFile>(d->file)->getCOFFObj()->getSymbolName(d->sym, nameStr);
+ nameData = nameStr.data();
+ nameSize = nameStr.size();
+ assert(nameSize == nameStr.size() && "name length truncated");
}
- return Name;
+ return StringRef(nameData, nameSize);
}
InputFile *Symbol::getFile() {
- if (auto *Sym = dyn_cast<DefinedCOFF>(this))
- return Sym->File;
- if (auto *Sym = dyn_cast<Lazy>(this))
- return Sym->File;
+ if (auto *sym = dyn_cast<DefinedCOFF>(this))
+ return sym->file;
+ if (auto *sym = dyn_cast<Lazy>(this))
+ return sym->file;
return nullptr;
}
bool Symbol::isLive() const {
- if (auto *R = dyn_cast<DefinedRegular>(this))
- return R->getChunk()->Live;
- if (auto *Imp = dyn_cast<DefinedImportData>(this))
- return Imp->File->Live;
- if (auto *Imp = dyn_cast<DefinedImportThunk>(this))
- return Imp->WrappedSym->File->ThunkLive;
+ if (auto *r = dyn_cast<DefinedRegular>(this))
+ return r->getChunk()->live;
+ if (auto *imp = dyn_cast<DefinedImportData>(this))
+ return imp->file->live;
+ if (auto *imp = dyn_cast<DefinedImportThunk>(this))
+ return imp->wrappedSym->file->thunkLive;
// Assume any other kind of symbol is live.
return true;
}
// MinGW specific.
-void Symbol::replaceKeepingName(Symbol *Other, size_t Size) {
- StringRef OrigName = Name;
- memcpy(this, Other, Size);
- Name = OrigName;
+void Symbol::replaceKeepingName(Symbol *other, size_t size) {
+ StringRef origName = getName();
+ memcpy(this, other, size);
+ nameData = origName.data();
+ nameSize = origName.size();
}
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
- size_t SymSize = cast<ObjFile>(File)->getCOFFObj()->getSymbolTableEntrySize();
- if (SymSize == sizeof(coff_symbol16))
- return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(Sym));
- assert(SymSize == sizeof(coff_symbol32));
- return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(Sym));
+ size_t symSize = cast<ObjFile>(file)->getCOFFObj()->getSymbolTableEntrySize();
+ if (symSize == sizeof(coff_symbol16))
+ return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(sym));
+ assert(symSize == sizeof(coff_symbol32));
+ return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(sym));
}
-uint16_t DefinedAbsolute::NumOutputSections;
+uint16_t DefinedAbsolute::numOutputSections;
-static Chunk *makeImportThunk(DefinedImportData *S, uint16_t Machine) {
- if (Machine == AMD64)
- return make<ImportThunkChunkX64>(S);
- if (Machine == I386)
- return make<ImportThunkChunkX86>(S);
- if (Machine == ARM64)
- return make<ImportThunkChunkARM64>(S);
- assert(Machine == ARMNT);
- return make<ImportThunkChunkARM>(S);
+static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) {
+ if (machine == AMD64)
+ return make<ImportThunkChunkX64>(s);
+ if (machine == I386)
+ return make<ImportThunkChunkX86>(s);
+ if (machine == ARM64)
+ return make<ImportThunkChunkARM64>(s);
+ assert(machine == ARMNT);
+ return make<ImportThunkChunkARM>(s);
}
-DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S,
- uint16_t Machine)
- : Defined(DefinedImportThunkKind, Name), WrappedSym(S),
- Data(makeImportThunk(S, Machine)) {}
+DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s,
+ uint16_t machine)
+ : Defined(DefinedImportThunkKind, name), wrappedSym(s),
+ data(makeImportThunk(s, machine)) {}
Defined *Undefined::getWeakAlias() {
// A weak alias may be a weak alias to another symbol, so check recursively.
- for (Symbol *A = WeakAlias; A; A = cast<Undefined>(A)->WeakAlias)
- if (auto *D = dyn_cast<Defined>(A))
- return D;
+ for (Symbol *a = weakAlias; a; a = cast<Undefined>(a)->weakAlias)
+ if (auto *d = dyn_cast<Defined>(a))
+ return d;
return nullptr;
}
} // namespace coff
diff --git a/COFF/Symbols.h b/COFF/Symbols.h
index 4a8693e22e3c..86cd4f585e50 100644
--- a/COFF/Symbols.h
+++ b/COFF/Symbols.h
@@ -1,9 +1,8 @@
//===- Symbols.h ------------------------------------------------*- C++ -*-===//
//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -58,15 +57,12 @@ public:
LastDefinedKind = DefinedSyntheticKind,
};
- Kind kind() const { return static_cast<Kind>(SymbolKind); }
-
- // Returns true if this is an external symbol.
- bool isExternal() { return IsExternal; }
+ Kind kind() const { return static_cast<Kind>(symbolKind); }
// Returns the symbol name.
StringRef getName();
- void replaceKeepingName(Symbol *Other, size_t Size);
+ void replaceKeepingName(Symbol *other, size_t size);
// Returns the file from which this symbol was created.
InputFile *getFile();
@@ -77,46 +73,50 @@ public:
protected:
friend SymbolTable;
- explicit Symbol(Kind K, StringRef N = "")
- : SymbolKind(K), IsExternal(true), IsCOMDAT(false),
- WrittenToSymtab(false), PendingArchiveLoad(false), IsGCRoot(false),
- IsRuntimePseudoReloc(false), Name(N) {}
+ explicit Symbol(Kind k, StringRef n = "")
+ : symbolKind(k), isExternal(true), isCOMDAT(false),
+ writtenToSymtab(false), pendingArchiveLoad(false), isGCRoot(false),
+ isRuntimePseudoReloc(false), nameSize(n.size()),
+ nameData(n.empty() ? nullptr : n.data()) {}
- const unsigned SymbolKind : 8;
- unsigned IsExternal : 1;
+ const unsigned symbolKind : 8;
+ unsigned isExternal : 1;
+public:
// This bit is used by the \c DefinedRegular subclass.
- unsigned IsCOMDAT : 1;
+ unsigned isCOMDAT : 1;
-public:
// This bit is used by Writer::createSymbolAndStringTable() to prevent
// symbols from bein