diff options
Diffstat (limited to 'COFF')
-rw-r--r-- | COFF/Chunks.cpp | 33 | ||||
-rw-r--r-- | COFF/Chunks.h | 30 | ||||
-rw-r--r-- | COFF/Config.h | 3 | ||||
-rw-r--r-- | COFF/Driver.cpp | 19 | ||||
-rw-r--r-- | COFF/Driver.h | 2 | ||||
-rw-r--r-- | COFF/DriverUtils.cpp | 160 | ||||
-rw-r--r-- | COFF/InputFiles.cpp | 22 | ||||
-rw-r--r-- | COFF/Options.td | 1 | ||||
-rw-r--r-- | COFF/PDB.cpp | 297 | ||||
-rw-r--r-- | COFF/Strings.cpp | 5 | ||||
-rw-r--r-- | COFF/SymbolTable.cpp | 10 | ||||
-rw-r--r-- | COFF/SymbolTable.h | 2 | ||||
-rw-r--r-- | COFF/Symbols.cpp | 2 | ||||
-rw-r--r-- | COFF/Symbols.h | 45 | ||||
-rw-r--r-- | COFF/Writer.cpp | 92 |
15 files changed, 485 insertions, 238 deletions
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp index 359dbe69559f5..56124acaf9a12 100644 --- a/COFF/Chunks.cpp +++ b/COFF/Chunks.cpp @@ -37,8 +37,14 @@ SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H) Align = Header->getAlignment(); - // Only COMDAT sections are subject of dead-stripping. - Live = !isCOMDAT(); + // Chunks may be discarded during comdat merging. + Discarded = false; + + // 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(); } static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); } @@ -46,6 +52,15 @@ 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 applySecRel(const SectionChunk *Sec, uint8_t *Off, Defined *Sym) { + // Don't apply section relative relocations to absolute symbols in codeview + // debug info sections. MSVC does not treat such relocations as fatal errors, + // and they can be found in the standard library for linker-provided symbols + // like __guard_fids_table and __safe_se_handler_table. + if (!(isa<DefinedAbsolute>(Sym) && Sec->isCodeView())) + add32(Off, Sym->getSecrel()); +} + void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym, uint64_t P) const { uint64_t S = Sym->getRVA(); @@ -60,7 +75,7 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym, 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: add16(Off, Sym->getSectionIndex()); break; - case IMAGE_REL_AMD64_SECREL: add32(Off, Sym->getSecrel()); break; + case IMAGE_REL_AMD64_SECREL: applySecRel(this, Off, Sym); break; default: fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); } @@ -75,7 +90,7 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym, 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: add16(Off, Sym->getSectionIndex()); break; - case IMAGE_REL_I386_SECREL: add32(Off, Sym->getSecrel()); break; + case IMAGE_REL_I386_SECREL: applySecRel(this, Off, Sym); break; default: fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); } @@ -135,7 +150,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym, case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, S - P - 4); break; case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, S - P - 4); break; case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, S - P - 4); break; - case IMAGE_REL_ARM_SECREL: add32(Off, Sym->getSecrel()); break; + case IMAGE_REL_ARM_SECREL: applySecRel(this, Off, Sym); break; default: fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); } @@ -226,8 +241,12 @@ bool SectionChunk::isCOMDAT() const { 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) { + if (Discarded) + message("Discarded comdat symbol " + Sym->getName()); + else if (!Live) + message("Discarded " + Sym->getName()); + } } StringRef SectionChunk::getDebugName() { diff --git a/COFF/Chunks.h b/COFF/Chunks.h index f7412517765c6..54fffc5f6d08e 100644 --- a/COFF/Chunks.h +++ b/COFF/Chunks.h @@ -64,7 +64,6 @@ public: uint64_t getRVA() const { return RVA; } uint32_t getAlign() const { return Align; } void setRVA(uint64_t V) { RVA = V; } - void setOutputSectionOff(uint64_t V) { OutputSectionOff = V; } // Returns true if this has non-zero data. BSS chunks return // false. If false is returned, the space occupied by this chunk @@ -97,17 +96,19 @@ protected: Chunk(Kind K = OtherKind) : ChunkKind(K) {} const Kind ChunkKind; + // The alignment of this chunk. The writer uses the value. + uint32_t Align = 1; + // The RVA of this chunk in the output. The writer sets a value. uint64_t RVA = 0; +public: // The offset from beginning of the output section. The writer sets a value. uint64_t OutputSectionOff = 0; +protected: // The output section for this chunk. OutputSection *Out = nullptr; - - // The alignment of this chunk. The writer uses the value. - uint32_t Align = 1; }; // A chunk corresponding a section of an input file. @@ -159,13 +160,29 @@ public: StringRef getDebugName() override; void setSymbol(DefinedRegular *S) { if (!Sym) Sym = S; } + // Returns true if the chunk was not dropped by GC or COMDAT deduplication. + bool isLive() { return Live && !Discarded; } + // Used by the garbage collector. - bool isLive() { return !Config->DoGC || Live; } void markLive() { + assert(Config->DoGC && "should only mark things live from GC"); assert(!isLive() && "Cannot mark an already live section!"); Live = true; } + // Returns true if this chunk was dropped by COMDAT deduplication. + bool isDiscarded() const { return Discarded; } + + // Used by the SymbolTable when discarding unused comdat sections. This is + // redundant when GC is enabled, as all comdat sections will start out dead. + void markDiscarded() { Discarded = true; } + + // 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$"); + } + // 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()), @@ -196,6 +213,9 @@ private: llvm::iterator_range<const coff_relocation *> Relocs; size_t NumRelocs; + // True if this chunk was discarded because it was a duplicate comdat section. + bool Discarded; + // Used by the garbage collector. bool Live; diff --git a/COFF/Config.h b/COFF/Config.h index d859b1fbb7d99..9fcea96d65d37 100644 --- a/COFF/Config.h +++ b/COFF/Config.h @@ -161,9 +161,6 @@ struct Configuration { bool LargeAddressAware = false; bool HighEntropyVA = false; bool AppContainer = false; - - // This is for debugging. - bool DumpPdb = false; }; extern Configuration *Config; diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp index 71a9c679d19fe..22efb312ae49b 100644 --- a/COFF/Driver.cpp +++ b/COFF/Driver.cpp @@ -905,7 +905,6 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { Config->TerminalServerAware = false; if (Args.hasArg(OPT_nosymtab)) Config->WriteSymtab = false; - Config->DumpPdb = Args.hasArg(OPT_dumppdb); Config->MapFile = getMapFile(Args); @@ -936,9 +935,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { Config->Machine = AMD64; } - // Windows specific -- Input files can be Windows resource files (.res files). - // We invoke cvtres.exe to convert resource files to a regular COFF file - // then link the result file normally. + // 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()) addBuffer(convertResToCOFF(Resources)); @@ -1027,17 +1026,21 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) { if (Config->ImageBase == uint64_t(-1)) Config->ImageBase = getDefaultImageBase(); - Symtab.addRelative(mangle("__ImageBase"), 0); + Symtab.addSynthetic(mangle("__ImageBase"), nullptr); if (Config->Machine == I386) { - Config->SEHTable = Symtab.addRelative("___safe_se_handler_table", 0); - Config->SEHCount = Symtab.addAbsolute("___safe_se_handler_count", 0); + Symtab.addAbsolute("___safe_se_handler_table", 0); + Symtab.addAbsolute("___safe_se_handler_count", 0); } // We do not support /guard:cf (control flow protection) yet. // Define CFG symbols anyway so that we can link MSVC 2015 CRT. - Symtab.addAbsolute(mangle("__guard_fids_table"), 0); Symtab.addAbsolute(mangle("__guard_fids_count"), 0); + Symtab.addAbsolute(mangle("__guard_fids_table"), 0); Symtab.addAbsolute(mangle("__guard_flags"), 0x100); + 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); // This code may add new undefined symbols to the link, which may enqueue more // symbol resolution tasks, so we need to continue executing tasks until we diff --git a/COFF/Driver.h b/COFF/Driver.h index 3eb950cca25cf..2b5d1e7ae28b2 100644 --- a/COFF/Driver.h +++ b/COFF/Driver.h @@ -178,7 +178,7 @@ void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects); // Create enum with OPT_xxx values for each option in Options.td enum { OPT_INVALID = 0, -#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11) OPT_##ID, +#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID, #include "Options.inc" #undef OPTION }; diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp index 42619b8fa438b..d0152b0917b61 100644 --- a/COFF/DriverUtils.cpp +++ b/COFF/DriverUtils.cpp @@ -20,12 +20,15 @@ #include "Symbols.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" +#include "llvm/Object/WindowsResource.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileUtilities.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" @@ -41,6 +44,9 @@ namespace lld { namespace coff { namespace { +const uint16_t SUBLANG_ENGLISH_US = 0x0409; +const uint16_t RT_MANIFEST = 24; + class Executor { public: explicit Executor(StringRef S) : Prog(Saver.save(S)) {} @@ -257,26 +263,6 @@ void parseManifestUAC(StringRef Arg) { } } -// Quote each line with "". Existing double-quote is converted -// to two double-quotes. -static void quoteAndPrint(raw_ostream &Out, StringRef S) { - while (!S.empty()) { - StringRef Line; - std::tie(Line, S) = S.split("\n"); - if (Line.empty()) - continue; - Out << '\"'; - for (int I = 0, E = Line.size(); I != E; ++I) { - if (Line[I] == '\"') { - Out << "\"\""; - } else { - Out << Line[I]; - } - } - Out << "\"\n"; - } -} - // An RAII temporary file class that automatically removes a temporary file. namespace { class TemporaryFile { @@ -390,38 +376,64 @@ static std::string createManifestXml() { return readFile(File2.Path); } +static std::unique_ptr<MemoryBuffer> +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, + object::WIN_RES_DATA_ALIGNMENT); + return MemoryBuffer::getNewMemBuffer(ResSize); +} + +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) { + // Write the prefix. + 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); + + // Write the Type/Name IDs. + 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); +} + // Create a resource file containing a manifest XML. std::unique_ptr<MemoryBuffer> createManifestRes() { - // Create a temporary file for the resource script file. - TemporaryFile RCFile("manifest", "rc"); + std::string Manifest = createManifestXml(); - // Open the temporary file for writing. - std::error_code EC; - raw_fd_ostream Out(RCFile.Path, EC, sys::fs::F_Text); - if (EC) - fatal(EC, "failed to open " + RCFile.Path); - - // Write resource script to the RC file. - Out << "#define LANG_ENGLISH 9\n" - << "#define SUBLANG_DEFAULT 1\n" - << "#define APP_MANIFEST " << Config->ManifestID << "\n" - << "#define RT_MANIFEST 24\n" - << "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n" - << "APP_MANIFEST RT_MANIFEST {\n"; - quoteAndPrint(Out, createManifestXml()); - Out << "}\n"; - Out.close(); - - // Create output resource file. - TemporaryFile ResFile("output-resource", "res"); - - Executor E("rc.exe"); - E.add("/fo"); - E.add(ResFile.Path); - E.add("/nologo"); - E.add(RCFile.Path); - E.run(); - return ResFile.getMemoryBuffer(); + std::unique_ptr<MemoryBuffer> Res = + createMemoryBufferForManifestRes(Manifest.size()); + + char *Buf = const_cast<char *>(Res->getBufferStart()); + writeResFileHeader(Buf); + writeResEntryHeader(Buf, Manifest.size()); + + // Copy the manifest data into the .res file. + std::copy(Manifest.begin(), Manifest.end(), Buf); + return Res; } void createSideBySideManifest() { @@ -592,40 +604,22 @@ void checkFailIfMismatch(StringRef Arg) { // using cvtres.exe. std::unique_ptr<MemoryBuffer> convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) { - // Create an output file path. - TemporaryFile File("resource-file", "obj"); + object::WindowsResourceParser Parser; - // Execute cvtres.exe. - Executor E("cvtres.exe"); - E.add("/machine:" + machineToStr(Config->Machine)); - E.add("/readonly"); - E.add("/nologo"); - E.add("/out:" + Twine(File.Path)); - - // We must create new files because the memory buffers we have may have no - // underlying file still existing on the disk. - // It happens if it was created from a TemporaryFile, which usually delete - // the file just after creating the MemoryBuffer. - std::vector<TemporaryFile> ResFiles; - ResFiles.reserve(MBs.size()); for (MemoryBufferRef MB : MBs) { - // We store the temporary file in a vector to avoid deletion - // before running cvtres - ResFiles.emplace_back("resource-file", "res"); - TemporaryFile& ResFile = ResFiles.back(); - // Write the content of the resource in a temporary file - std::error_code EC; - raw_fd_ostream OS(ResFile.Path, EC, sys::fs::F_None); - if (EC) - fatal(EC, "failed to open " + ResFile.Path); - OS << MB.getBuffer(); - OS.close(); - - E.add(ResFile.Path); + 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(EC, "failed to parse .res file"); } - E.run(); - return File.getMemoryBuffer(); + Expected<std::unique_ptr<MemoryBuffer>> E = + llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser); + if (!E) + fatal(errorToErrorCode(E.takeError()), "failed to write .res to COFF"); + return std::move(E.get()); } // Run MSVC link.exe for given in-memory object files. @@ -657,11 +651,9 @@ void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects) { // Create table mapping all options defined in Options.td static const llvm::opt::OptTable::Info infoTable[] = { -#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X6, X7, X8, X9, X10) \ - { \ - X1, X2, X9, X10, OPT_##ID, llvm::opt::Option::KIND##Class, X8, X7, \ - OPT_##GROUP, OPT_##ALIAS, X6 \ - }, +#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}, #include "Options.inc" #undef OPTION }; diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp index 58e8dd329f1f3..c26483e3e368c 100644 --- a/COFF/InputFiles.cpp +++ b/COFF/InputFiles.cpp @@ -132,17 +132,17 @@ void ObjectFile::initializeChunks() { if (!Config->Debug && Name.startswith(".debug")) continue; - // CodeView sections are stored to a different vector because they are - // not linked in the regular manner. - if (Name == ".debug" || Name.startswith(".debug$")) { - DebugChunks.push_back(make<SectionChunk>(this, Sec)); - continue; - } - if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) continue; auto *C = make<SectionChunk>(this, Sec); - Chunks.push_back(C); + + // 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 + Chunks.push_back(C); + SparseChunks[I] = C; } } @@ -249,8 +249,12 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, auto *Aux = reinterpret_cast<const coff_aux_section_definition *>(AuxP); if (Aux->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) if (auto *ParentSC = cast_or_null<SectionChunk>( - SparseChunks[Aux->getNumber(Sym.isBigObj())])) + SparseChunks[Aux->getNumber(Sym.isBigObj())])) { ParentSC->addAssociative(SC); + // If we already discarded the parent, discard the child. + if (ParentSC->isDiscarded()) + SC->markDiscarded(); + } SC->Checksum = Aux->CheckSum; } diff --git a/COFF/Options.td b/COFF/Options.td index 14a1aa04afd64..61523c4f2256a 100644 --- a/COFF/Options.td +++ b/COFF/Options.td @@ -102,7 +102,6 @@ def nosymtab : F<"nosymtab">; def msvclto : F<"msvclto">; // Flags for debugging -def dumppdb : Joined<["/", "-"], "dumppdb">; def lldmap : F<"lldmap">; def lldmap_file : Joined<["/", "-"], "lldmap:">; diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp index 116766740e584..c9842cfd1b9ab 100644 --- a/COFF/PDB.cpp +++ b/COFF/PDB.cpp @@ -15,10 +15,11 @@ #include "Symbols.h" #include "llvm/DebugInfo/CodeView/CVDebugRecord.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h" +#include "llvm/DebugInfo/CodeView/DebugSubsectionVisitor.h" #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" -#include "llvm/DebugInfo/CodeView/SymbolDumper.h" -#include "llvm/DebugInfo/CodeView/TypeDatabase.h" #include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h" #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #include "llvm/DebugInfo/CodeView/TypeTableBuilder.h" #include "llvm/DebugInfo/MSF/MSFBuilder.h" @@ -46,8 +47,6 @@ using namespace lld; using namespace lld::coff; using namespace llvm; using namespace llvm::codeview; -using namespace llvm::support; -using namespace llvm::support::endian; using llvm::object::coff_section; @@ -68,22 +67,24 @@ static SectionChunk *findByName(std::vector<SectionChunk *> &Sections, return nullptr; } -static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) { - SectionChunk *Sec = findByName(File->getDebugChunks(), SecName); - if (!Sec) - return {}; - +static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> Data, + StringRef SecName) { // First 4 bytes are section magic. - ArrayRef<uint8_t> Data = Sec->getContents(); if (Data.size() < 4) fatal(SecName + " too short"); - if (read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC) + 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(ObjectFile *File, StringRef SecName) { + if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName)) + return consumeDebugMagic(Sec->getContents(), SecName); + return {}; +} + static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder, - codeview::TypeTableBuilder &TypeTable) { + TypeTableBuilder &TypeTable) { // Start the TPI or IPI stream header. TpiBuilder.setVersionHeader(pdb::PdbTpiV80); @@ -94,17 +95,148 @@ static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder, }); } +static void mergeDebugT(ObjectFile *File, + TypeTableBuilder &IDTable, + TypeTableBuilder &TypeTable, + SmallVectorImpl<TypeIndex> &TypeIndexMap, + pdb::PDBTypeServerHandler &Handler) { + ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T"); + if (Data.empty()) + return; + + BinaryByteStream Stream(Data, support::little); + CVTypeArray Types; + BinaryStreamReader Reader(Stream); + Handler.addSearchPath(sys::path::parent_path(File->getName())); + if (auto EC = Reader.readArray(Types, Reader.getLength())) + fatal(EC, "Reader::readArray failed"); + if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable, + TypeIndexMap, &Handler, Types)) + fatal(Err, "codeview::mergeTypeStreams failed"); +} + +static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) { + if (TI.isSimple()) + return true; + if (TI.toArrayIndex() >= TypeIndexMap.size()) + return false; + TI = TypeIndexMap[TI.toArrayIndex()]; + return true; +} + +static bool remapTypesInSymbolRecord(ObjectFile *File, + MutableArrayRef<uint8_t> Contents, + ArrayRef<TypeIndex> TypeIndexMap, + ArrayRef<TiReference> TypeRefs) { + for (const TiReference &Ref : TypeRefs) { + unsigned ByteSize = Ref.Count * sizeof(TypeIndex); + if (Contents.size() < Ref.Offset + ByteSize) { + log("ignoring short symbol record"); + return false; + } + MutableArrayRef<TypeIndex> TIs( + reinterpret_cast<TypeIndex *>(Contents.data() + Ref.Offset), Ref.Count); + for (TypeIndex &TI : TIs) + if (!remapTypeIndex(TI, TypeIndexMap)) { + log("ignoring symbol record in " + File->getName() + + " with bad type index 0x" + utohexstr(TI.getIndex())); + return false; + } + } + return true; +} + +/// MSVC translates S_PROC_ID_END to S_END. +uint16_t canonicalizeSymbolKind(SymbolKind Kind) { + if (Kind == SymbolKind::S_PROC_ID_END) + return SymbolKind::S_END; + return 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> copySymbolForPdb(const CVSymbol &Sym, + BumpPtrAllocator &Alloc) { + size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb)); + assert(Size >= 4 && "record too short"); + assert(Size <= MaxRecordLength && "record too long"); + void *Mem = Alloc.Allocate(Size, 4); + + // Copy the symbol record and zero out any padding bytes. + MutableArrayRef<uint8_t> NewData(reinterpret_cast<uint8_t *>(Mem), 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. MSVC does some canonicalization of the record kind, so we do + // that as well. + auto *Prefix = reinterpret_cast<RecordPrefix *>(Mem); + Prefix->RecordKind = canonicalizeSymbolKind(Sym.kind()); + Prefix->RecordLen = Size - 2; + return NewData; +} + +static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File, + ArrayRef<TypeIndex> TypeIndexMap, + BinaryStreamRef SymData) { + // FIXME: Improve error recovery by warning and skipping records when + // possible. + CVSymbolArray Syms; + BinaryStreamReader Reader(SymData); + ExitOnErr(Reader.readArray(Syms, Reader.getLength())); + for (const CVSymbol &Sym : Syms) { + // Discover type index references in the record. Skip it if we don't know + // where they are. + SmallVector<TiReference, 32> TypeRefs; + if (!discoverTypeIndices(Sym, TypeRefs)) { + log("ignoring unknown symbol record with kind 0x" + utohexstr(Sym.kind())); + continue; + } + + // Copy the symbol record so we can mutate it. + MutableArrayRef<uint8_t> NewData = copySymbolForPdb(Sym, Alloc); + + // Re-map all the type index references. + MutableArrayRef<uint8_t> Contents = + NewData.drop_front(sizeof(RecordPrefix)); + if (!remapTypesInSymbolRecord(File, Contents, TypeIndexMap, TypeRefs)) + continue; + + // FIXME: Fill in "Parent" and "End" fields by maintaining a stack of + // scopes. + + // Add the symbol to the module. + File->ModuleDBI->addSymbol(CVSymbol(Sym.kind(), NewData)); + } +} + +// Allocate memory for a .debug$S 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 && + "debug sections should not be in output sections"); + DebugChunk->writeTo(Buffer); + return consumeDebugMagic(makeArrayRef(Buffer, DebugChunk->getSize()), + ".debug$S"); +} + // Add all object files to the PDB. Merge .debug$T sections into IpiData and // TpiData. -static void addObjectsToPDB(SymbolTable *Symtab, pdb::PDBFileBuilder &Builder, - codeview::TypeTableBuilder &TypeTable, - codeview::TypeTableBuilder &IDTable) { +static void addObjectsToPDB(BumpPtrAllocator &Alloc, SymbolTable *Symtab, + pdb::PDBFileBuilder &Builder, + TypeTableBuilder &TypeTable, + TypeTableBuilder &IDTable) { // Follow type servers. If the same type server is encountered more than // once for this instance of `PDBTypeServerHandler` (for example if many // object files reference the same TypeServer), the types from the // TypeServer will only be visited once. pdb::PDBTypeServerHandler Handler; + // PDBs use a single global string table for filenames in the file checksum + // table. + auto PDBStrTab = std::make_shared<DebugStringTableSubsection>(); + // Visit all .debug$T sections to add them to Builder. for (ObjectFile *File : Symtab->ObjectFiles) { // Add a module descriptor for every object file. We need to put an absolute @@ -118,25 +250,74 @@ static void addObjectsToPDB(SymbolTable *Symtab, pdb::PDBFileBuilder &Builder, File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name)); File->ModuleDBI->setObjFileName(Path); - // FIXME: Walk the .debug$S sections and add them. Do things like recording - // source files. - - ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T"); - if (Data.empty()) - continue; - - BinaryByteStream Stream(Data, support::little); - codeview::CVTypeArray Types; - BinaryStreamReader Reader(Stream); - SmallVector<TypeIndex, 128> SourceToDest; - Handler.addSearchPath(llvm::sys::path::parent_path(File->getName())); - if (auto EC = Reader.readArray(Types, Reader.getLength())) - fatal(EC, "Reader::readArray failed"); - if (auto Err = codeview::mergeTypeAndIdRecords( - IDTable, TypeTable, SourceToDest, &Handler, Types)) - fatal(Err, "codeview::mergeTypeStreams failed"); + // 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. + SmallVector<TypeIndex, 128> TypeIndexMap; + mergeDebugT(File, IDTable, TypeTable, TypeIndexMap, Handler); + + // Now do all line info. + for (SectionChunk *DebugChunk : File->getDebugChunks()) { + if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S") + continue; + + ArrayRef<uint8_t> RelocatedDebugContents = + relocateDebugChunk(Alloc, DebugChunk); + if (RelocatedDebugContents.empty()) + continue; + + DebugSubsectionArray Subsections; + BinaryStreamReader Reader(RelocatedDebugContents, support::little); + ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size())); + + DebugStringTableSubsectionRef CVStrTab; + DebugChecksumsSubsectionRef Checksums; + for (const DebugSubsectionRecord &SS : Subsections) { + switch (SS.kind()) { + case DebugSubsectionKind::StringTable: + ExitOnErr(CVStrTab.initialize(SS.getRecordData())); + break; + case DebugSubsectionKind::FileChecksums: + 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); + break; + case DebugSubsectionKind::Symbols: + mergeSymbolRecords(Alloc, File, TypeIndexMap, SS.getRecordData()); + break; + default: + // FIXME: Process the rest of the subsections. + break; + } + } + + if (Checksums.valid()) { + // 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. + if (!CVStrTab.valid()) + fatal(".debug$S sections must have both a string table subsection " + "and a checksum subsection table or neither"); + auto NewChecksums = + make_unique<DebugChecksumsSubsection>(*PDBStrTab); + for (FileChecksumEntry &FC : Checksums) { + StringRef FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset)); + ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile( + *File->ModuleDBI, FileName)); + NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum); + } + File->ModuleDBI->addDebugSubsection(std::move(NewChecksums)); + } + } } + Builder.getStringTableBuilder().setStrings(*PDBStrTab); + // Construct TPI stream contents. addTypeInfo(Builder.getTpiBuilder(), TypeTable); @@ -144,56 +325,10 @@ static void addObjectsToPDB(SymbolTable *Symtab, pdb::PDBFileBuilder &Builder, addTypeInfo(Builder.getIpiBuilder(), IDTable); } -static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) { - ListScope LS(W, "DebugT"); - ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T"); - if (Data.empty()) - return; - - LazyRandomTypeCollection Types(Data, 100); - TypeDumpVisitor TDV(Types, &W, false); - // Use a default implementation that does not follow type servers and instead - // just dumps the contents of the TypeServer2 record. - if (auto EC = codeview::visitTypeStream(Types, TDV)) - fatal(EC, "CVTypeDumper::dump failed"); -} - -static void dumpDebugS(ScopedPrinter &W, ObjectFile *File) { - ListScope LS(W, "DebugS"); - ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$S"); - if (Data.empty()) - return; - - BinaryByteStream Stream(Data, llvm::support::little); - CVSymbolArray Symbols; - BinaryStreamReader Reader(Stream); - if (auto EC = Reader.readArray(Symbols, Reader.getLength())) - fatal(EC, "StreamReader.readArray<CVSymbolArray> failed"); - - TypeDatabase TDB(0); - CVSymbolDumper SymbolDumper(W, TDB, CodeViewContainer::ObjectFile, nullptr, - false); - if (auto EC = SymbolDumper.dump(Symbols)) - fatal(EC, "CVSymbolDumper::dump failed"); -} - -// Dump CodeView debug info. This is for debugging. -static void dumpCodeView(SymbolTable *Symtab) { - ScopedPrinter W(outs()); - - for (ObjectFile *File : Symtab->ObjectFiles) { - dumpDebugT(W, File); - dumpDebugS(W, File); - } -} - // Creates a PDB file. void coff::createPDB(StringRef Path, SymbolTable *Symtab, ArrayRef<uint8_t> SectionTable, const llvm::codeview::DebugInfo *DI) { - if (Config->DumpPdb) - dumpCodeView(Symtab); - BumpPtrAllocator Alloc; pdb::PDBFileBuilder Builder(Alloc); ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize @@ -219,9 +354,9 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab, pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); DbiBuilder.setVersionHeader(pdb::PdbDbiV110); - codeview::TypeTableBuilder TypeTable(BAlloc); - codeview::TypeTableBuilder IDTable(BAlloc); - addObjectsToPDB(Symtab, Builder, TypeTable, IDTable); + TypeTableBuilder TypeTable(BAlloc); + TypeTableBuilder IDTable(BAlloc); + addObjectsToPDB(Alloc, Symtab, Builder, TypeTable, IDTable); // Add Section Contributions. addSectionContribs(Symtab, DbiBuilder); diff --git a/COFF/Strings.cpp b/COFF/Strings.cpp index d0558413f6733..84f9b9a55a32a 100644 --- a/COFF/Strings.cpp +++ b/COFF/Strings.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "Strings.h" +#include <mutex> #if defined(_MSC_VER) #include <Windows.h> @@ -21,6 +22,10 @@ using namespace llvm; Optional<std::string> coff::demangle(StringRef S) { #if defined(_MSC_VER) + // UnDecorateSymbolName is not thread-safe, so we need a mutex. + static std::mutex Mu; + std::lock_guard<std::mutex> Lock(Mu); + char Buf[4096]; if (S.startswith("?")) if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0)) diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp index 310eab2745265..c06e42bb114bb 100644 --- a/COFF/SymbolTable.cpp +++ b/COFF/SymbolTable.cpp @@ -219,13 +219,13 @@ Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) { return S; } -Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) { +Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) { Symbol *S; bool WasInserted; std::tie(S, WasInserted) = insert(N); S->IsUsedInRegularObj = true; if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) - replaceBody<DefinedRelative>(S, N, VA); + replaceBody<DefinedSynthetic>(S, N, C); else if (!isa<DefinedCOFF>(S->body())) reportDuplicate(S, nullptr); return S; @@ -244,6 +244,12 @@ Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT, reportDuplicate(S, F); } else if (SP == SP_NEW) { replaceBody<DefinedRegular>(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C); + } else if (SP == SP_EXISTING && IsCOMDAT && C) { + C->markDiscarded(); + // Discard associative chunks that we've parsed so far. No need to recurse + // because an associative section cannot have children. + for (SectionChunk *Child : C->children()) + Child->markDiscarded(); } return S; } diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h index 0aa8a4593b5c4..ea74678c28d80 100644 --- a/COFF/SymbolTable.h +++ b/COFF/SymbolTable.h @@ -85,7 +85,7 @@ public: // Creates an Undefined symbol for a given name. SymbolBody *addUndefined(StringRef Name); - Symbol *addRelative(StringRef N, uint64_t VA); + Symbol *addSynthetic(StringRef N, Chunk *C); Symbol *addAbsolute(StringRef N, uint64_t VA); Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias); diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp index 5c185a511dd76..1cf2934a355b5 100644 --- a/COFF/Symbols.cpp +++ b/COFF/Symbols.cpp @@ -61,6 +61,8 @@ COFFSymbolRef DefinedCOFF::getCOFFSymbol() { return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(Sym)); } +uint16_t DefinedAbsolute::OutputSectionIndex = 0; + static Chunk *makeImportThunk(DefinedImportData *S, uint16_t Machine) { if (Machine == AMD64) return make<ImportThunkChunkX64>(S); diff --git a/COFF/Symbols.h b/COFF/Symbols.h index 801fc87f91d95..8c1390c45876f 100644 --- a/COFF/Symbols.h +++ b/COFF/Symbols.h @@ -50,13 +50,13 @@ public: DefinedImportThunkKind, DefinedImportDataKind, DefinedAbsoluteKind, - DefinedRelativeKind, + DefinedSyntheticKind, UndefinedKind, LazyKind, LastDefinedCOFFKind = DefinedCommonKind, - LastDefinedKind = DefinedRelativeKind, + LastDefinedKind = DefinedSyntheticKind, }; Kind kind() const { return static_cast<Kind>(SymbolKind); } @@ -112,11 +112,11 @@ public: // Returns the RVA relative to the beginning of the output section. // Used to implement SECREL relocation type. - uint64_t getSecrel(); + uint32_t getSecrel(); // Returns the output section index. // Used to implement SECTION relocation type. - uint64_t getSectionIndex(); + uint16_t getSectionIndex(); // Returns true if this symbol points to an executable (e.g. .text) section. // Used to implement ARM relocations. @@ -167,6 +167,7 @@ public: bool isCOMDAT() { return IsCOMDAT; } SectionChunk *getChunk() { return *Data; } uint32_t getValue() { return Sym->Value; } + uint32_t getSecrel(); private: SectionChunk **Data; @@ -186,6 +187,8 @@ public: } uint64_t getRVA() { return Data->getRVA(); } + uint32_t getSecrel() { return Data->OutputSectionOff; } + uint16_t getSectionIndex(); private: friend SymbolTable; @@ -212,28 +215,34 @@ public: uint64_t getRVA() { return VA - Config->ImageBase; } void setVA(uint64_t V) { VA = V; } + // The sentinel absolute symbol section index. Section index relocations + // against absolute symbols resolve to this 16 bit number, and it is the + // largest valid section index plus one. This is written by the Writer. + static uint16_t OutputSectionIndex; + private: uint64_t VA; }; -// This is a kind of absolute symbol but relative to the image base. -// Unlike absolute symbols, relocations referring this kind of symbols -// are subject of the base relocation. This type is used rarely -- -// mainly for __ImageBase. -class DefinedRelative : public Defined { +// This symbol is used for linker-synthesized symbols like __ImageBase and +// __safe_se_handler_table. +class DefinedSynthetic : public Defined { public: - explicit DefinedRelative(StringRef Name, uint64_t V = 0) - : Defined(DefinedRelativeKind, Name), RVA(V) {} + explicit DefinedSynthetic(StringRef Name, Chunk *C) + : Defined(DefinedSyntheticKind, Name), C(C) {} static bool classof(const SymbolBody *S) { - return S->kind() == DefinedRelativeKind; + return S->kind() == DefinedSyntheticKind; } - uint64_t getRVA() { return RVA; } - void setRVA(uint64_t V) { RVA = V; } + // A null chunk indicates that this is __ImageBase. Otherwise, this is some + // other synthesized chunk, like SEHTableChunk. + uint32_t getRVA() const { return C ? C->getRVA() : 0; } + uint32_t getSecrel() const { return C ? C->OutputSectionOff : 0; } + Chunk *getChunk() const { return C; } private: - uint64_t RVA; + Chunk *C; }; // This class represents a symbol defined in an archive file. It is @@ -350,8 +359,8 @@ inline uint64_t Defined::getRVA() { switch (kind()) { case DefinedAbsoluteKind: return cast<DefinedAbsolute>(this)->getRVA(); - case DefinedRelativeKind: - return cast<DefinedRelative>(this)->getRVA(); + case DefinedSyntheticKind: + return cast<DefinedSynthetic>(this)->getRVA(); case DefinedImportDataKind: return cast<DefinedImportData>(this)->getRVA(); case DefinedImportThunkKind: @@ -386,7 +395,7 @@ struct Symbol { // AlignedCharArrayUnion gives us a struct with a char array field that is // large and aligned enough to store any derived class of SymbolBody. llvm::AlignedCharArrayUnion< - DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedRelative, Lazy, + DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedSynthetic, Lazy, Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport> Body; diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp index fb1f3cae5bb2f..d32577b361fab 100644 --- a/COFF/Writer.cpp +++ b/COFF/Writer.cpp @@ -181,7 +181,7 @@ void OutputSection::addChunk(Chunk *C) { uint64_t Off = Header.VirtualSize; Off = alignTo(Off, C->getAlign()); C->setRVA(Off); - C->setOutputSectionOff(Off); + C->OutputSectionOff = Off; Off += C->getSize(); Header.VirtualSize = Off; if (C->hasData()) @@ -210,16 +210,46 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) { } } -uint64_t Defined::getSecrel() { - if (auto *D = dyn_cast<DefinedRegular>(this)) - return getRVA() - D->getChunk()->getOutputSection()->getRVA(); - fatal("SECREL relocation points to a non-regular symbol"); +uint32_t Defined::getSecrel() { + assert(this); + switch (kind()) { + case DefinedRegularKind: + return cast<DefinedRegular>(this)->getSecrel(); + case DefinedCommonKind: + return cast<DefinedCommon>(this)->getSecrel(); + case DefinedSyntheticKind: + return cast<DefinedSynthetic>(this)->getSecrel(); + default: + break; + } + fatal("SECREL relocation points to a non-regular symbol: " + toString(*this)); +} + +uint32_t DefinedRegular::getSecrel() { + assert(getChunk()->isLive() && "relocation against discarded section"); + uint64_t Diff = getRVA() - getChunk()->getOutputSection()->getRVA(); + assert(Diff < UINT32_MAX && "section offset too large"); + return (uint32_t)Diff; } -uint64_t Defined::getSectionIndex() { +uint16_t Defined::getSectionIndex() { if (auto *D = dyn_cast<DefinedRegular>(this)) return D->getChunk()->getOutputSection()->SectionIndex; - fatal("SECTION relocation points to a non-regular symbol"); + if (isa<DefinedAbsolute>(this)) + return DefinedAbsolute::OutputSectionIndex; + if (auto *D = dyn_cast<DefinedCommon>(this)) + return D->getSectionIndex(); + if (auto *D = dyn_cast<DefinedSynthetic>(this)) { + if (!D->getChunk()) + return 0; + return D->getChunk()->getOutputSection()->SectionIndex; + } + fatal("SECTION relocation points to a non-regular symbol: " + + toString(*this)); +} + +uint16_t DefinedCommon::getSectionIndex() { + return Data->getOutputSection()->SectionIndex; } bool Defined::isExecutable() { @@ -345,12 +375,19 @@ void Writer::createMiscChunks() { for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) { if (!File->SEHCompat) return; - for (SymbolBody *B : File->SEHandlers) - Handlers.insert(cast<Defined>(B)); + for (SymbolBody *B : File->SEHandlers) { + // Make sure the handler is still live. Assume all handlers are regular + // symbols. + auto *D = dyn_cast<DefinedRegular>(B); + if (D && D->getChunk()->isLive()) + Handlers.insert(D); + } } - SEHTable = make<SEHTableChunk>(Handlers); - RData->addChunk(SEHTable); + if (!Handlers.empty()) { + SEHTable = make<SEHTableChunk>(Handlers); + RData->addChunk(SEHTable); + } } // Create .idata section for the DLL-imported symbol table. @@ -442,12 +479,15 @@ size_t Writer::addEntryToStringTable(StringRef Str) { Optional<coff_symbol16> Writer::createSymbol(Defined *Def) { // Relative symbols are unrepresentable in a COFF symbol table. - if (isa<DefinedRelative>(Def)) + if (isa<DefinedSynthetic>(Def)) return None; - if (auto *D = dyn_cast<DefinedRegular>(Def)) - if (!D->getChunk()->isLive()) + if (auto *D = dyn_cast<DefinedRegular>(Def)) { + // Don't write dead symbols or symbols in codeview sections to the symbol + // table. + if (!D->getChunk()->isLive() || D->getChunk()->isCodeView()) return None; + } if (auto *Sym = dyn_cast<DefinedImportData>(Def)) if (!Sym->File->Live) @@ -599,6 +639,15 @@ template <typename PEHeaderTy> void Writer::writeHeader() { auto *PE = reinterpret_cast<PEHeaderTy *>(Buf); Buf += sizeof(*PE); PE->Magic = Config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32; + + // If {Major,Minor}LinkerVersion is left at 0.0, then for some + // reason signing the resulting PE file with Authenticode produces a + // signature that fails to validate on Windows 7 (but is OK on 10). + // Set it to 14.0, which is what VS2015 outputs, and which avoids + // that problem. + PE->MajorLinkerVersion = 14; + PE->MinorLinkerVersion = 0; + PE->ImageBase = Config->ImageBase; PE->SectionAlignment = PageSize; PE->FileAlignment = SectorSize; @@ -743,10 +792,13 @@ void Writer::openFile(StringRef Path) { void Writer::fixSafeSEHSymbols() { if (!SEHTable) return; - if (auto *T = dyn_cast<DefinedRelative>(Config->SEHTable->body())) - T->setRVA(SEHTable->getRVA()); - if (auto *C = dyn_cast<DefinedAbsolute>(Config->SEHCount->body())) - C->setVA(SEHTable->getSize() / 4); + // Replace the absolute table symbol with a synthetic symbol pointing to the + // SEHTable chunk so that we can emit base relocations for it and resolve + // section relative relocations. + Symbol *T = Symtab->find("___safe_se_handler_table"); + Symbol *C = Symtab->find("___safe_se_handler_count"); + replaceBody<DefinedSynthetic>(T, T->body()->getName(), SEHTable); + cast<DefinedAbsolute>(C->body())->setVA(SEHTable->getSize() / 4); } // Handles /section options to allow users to overwrite @@ -762,6 +814,10 @@ void Writer::setSectionPermissions() { // Write section contents to a mmap'ed file. void Writer::writeSections() { + // Record the section index that should be used when resolving a section + // relocation against an absolute symbol. + DefinedAbsolute::OutputSectionIndex = OutputSections.size() + 1; + uint8_t *Buf = Buffer->getBufferStart(); for (OutputSection *Sec : OutputSections) { uint8_t *SecBuf = Buf + Sec->getFileOff(); |