diff options
Diffstat (limited to 'COFF/Writer.cpp')
| -rw-r--r-- | COFF/Writer.cpp | 825 |
1 files changed, 573 insertions, 252 deletions
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp index 584f0621bea36..d17405ec26ab0 100644 --- a/COFF/Writer.cpp +++ b/COFF/Writer.cpp @@ -17,6 +17,7 @@ #include "Symbols.h" #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "lld/Common/Timer.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" @@ -25,7 +26,9 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/Parallel.h" +#include "llvm/Support/Path.h" #include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Support/xxhash.h" #include <algorithm> #include <cstdio> #include <map> @@ -40,8 +43,40 @@ using namespace llvm::support::endian; using namespace lld; using namespace lld::coff; +/* To re-generate DOSProgram: +$ cat > /tmp/DOSProgram.asm +org 0 + ; Copy cs to ds. + push cs + pop ds + ; Point ds:dx at the $-terminated string. + mov dx, str + ; Int 21/AH=09h: Write string to standard output. + mov ah, 0x9 + int 0x21 + ; Int 21/AH=4Ch: Exit with return code (in AL). + mov ax, 0x4C01 + int 0x21 +str: + db 'This program cannot be run in DOS mode.$' +align 8, db 0 +$ nasm -fbin /tmp/DOSProgram.asm -o /tmp/DOSProgram.bin +$ xxd -i /tmp/DOSProgram.bin +*/ +static unsigned char DOSProgram[] = { + 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, + 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, + 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, + 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x24, 0x00, 0x00 +}; +static_assert(sizeof(DOSProgram) % 8 == 0, + "DOSProgram size must be multiple of 8"); + static const int SectorSize = 512; -static const int DOSStubSize = 64; +static const int DOSStubSize = sizeof(dos_header) + sizeof(DOSProgram); +static_assert(DOSStubSize % 8 == 0, "DOSStub size must be multiple of 8"); + static const int NumberfOfDataDirectory = 16; namespace { @@ -69,24 +104,25 @@ public: uint64_t Offs = OS->getFileOff() + (Record->getRVA() - OS->getRVA()); D->PointerToRawData = Offs; + TimeDateStamps.push_back(&D->TimeDateStamp); ++D; } } + void setTimeDateStamp(uint32_t TimeDateStamp) { + for (support::ulittle32_t *TDS : TimeDateStamps) + *TDS = TimeDateStamp; + } + private: + mutable std::vector<support::ulittle32_t *> TimeDateStamps; const std::vector<Chunk *> &Records; }; class CVDebugRecordChunk : public Chunk { public: - CVDebugRecordChunk() { - PDBAbsPath = Config->PDBPath; - if (!PDBAbsPath.empty()) - llvm::sys::fs::make_absolute(PDBAbsPath); - } - size_t getSize() const override { - return sizeof(codeview::DebugInfo) + PDBAbsPath.size() + 1; + return sizeof(codeview::DebugInfo) + Config->PDBAltPath.size() + 1; } void writeTo(uint8_t *B) const override { @@ -96,12 +132,11 @@ public: // variable sized field (PDB Path) char *P = reinterpret_cast<char *>(B + OutputSectionOff + sizeof(*BuildId)); - if (!PDBAbsPath.empty()) - memcpy(P, PDBAbsPath.data(), PDBAbsPath.size()); - P[PDBAbsPath.size()] = '\0'; + if (!Config->PDBAltPath.empty()) + memcpy(P, Config->PDBAltPath.data(), Config->PDBAltPath.size()); + P[Config->PDBAltPath.size()] = '\0'; } - SmallString<128> PDBAbsPath; mutable codeview::DebugInfo *BuildId = nullptr; }; @@ -116,12 +151,19 @@ private: void createMiscChunks(); void createImportTables(); void createExportTable(); + void mergeSections(); void assignAddresses(); void removeEmptySections(); void createSymbolAndStringTable(); void openFile(StringRef OutputPath); template <typename PEHeaderTy> void writeHeader(); - void createSEHTable(OutputSection *RData); + void createSEHTable(); + void createGuardCFTables(); + void markSymbolsForRVATable(ObjFile *File, + ArrayRef<SectionChunk *> SymIdxChunks, + SymbolRVASet &TableSymbols); + void maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, + StringRef CountSym); void setSectionPermissions(); void writeSections(); void writeBuildId(); @@ -131,9 +173,8 @@ private: size_t addEntryToStringTable(StringRef Str); OutputSection *findSection(StringRef Name); - OutputSection *createSection(StringRef Name); - void addBaserels(OutputSection *Dest); - void addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V); + void addBaserels(); + void addBaserelBlocks(std::vector<Baserel> &V); uint32_t getSizeOfInitializedData(); std::map<StringRef, std::vector<DefinedImportData *>> binImports(); @@ -145,9 +186,9 @@ private: IdataContents Idata; DelayLoadContents DelayIdata; EdataContents Edata; - SEHTableChunk *SEHTable = nullptr; + bool SetNoSEHCharacteristic = false; - Chunk *DebugDirectory = nullptr; + DebugDirectoryChunk *DebugDirectory = nullptr; std::vector<Chunk *> DebugRecords; CVDebugRecordChunk *BuildId = nullptr; Optional<codeview::DebugInfo> PreviousBuildId; @@ -157,50 +198,55 @@ private: uint32_t PointerToSymbolTable = 0; uint64_t SizeOfImage; uint64_t SizeOfHeaders; + + OutputSection *TextSec; + OutputSection *RdataSec; + OutputSection *BuildidSec; + OutputSection *DataSec; + OutputSection *PdataSec; + OutputSection *IdataSec; + OutputSection *EdataSec; + OutputSection *DidatSec; + OutputSection *RsrcSec; + OutputSection *RelocSec; + + // The first and last .pdata sections in the output file. + // + // We need to keep track of the location of .pdata in whichever section it + // gets merged into so that we can sort its contents and emit a correct data + // directory entry for the exception table. This is also the case for some + // other sections (such as .edata) but because the contents of those sections + // are entirely linker-generated we can keep track of their locations using + // the chunks that the linker creates. All .pdata chunks come from input + // files, so we need to keep track of them separately. + Chunk *FirstPdata = nullptr; + Chunk *LastPdata; }; } // anonymous namespace namespace lld { namespace coff { -void writeResult() { Writer().run(); } - -void OutputSection::setRVA(uint64_t RVA) { - Header.VirtualAddress = RVA; - for (Chunk *C : Chunks) - C->setRVA(C->getRVA() + RVA); -} +static Timer CodeLayoutTimer("Code Layout", Timer::root()); +static Timer DiskCommitTimer("Commit Output File", Timer::root()); -void OutputSection::setFileOffset(uint64_t Off) { - // If a section has no actual data (i.e. BSS section), we want to - // set 0 to its PointerToRawData. Otherwise the output is rejected - // by the loader. - if (Header.SizeOfRawData == 0) - return; - Header.PointerToRawData = Off; -} +void writeResult() { Writer().run(); } void OutputSection::addChunk(Chunk *C) { Chunks.push_back(C); C->setOutputSection(this); - uint64_t Off = Header.VirtualSize; - Off = alignTo(Off, C->Alignment); - C->setRVA(Off); - C->OutputSectionOff = Off; - Off += C->getSize(); - if (Off > UINT32_MAX) - error("section larger than 4 GiB: " + Name); - Header.VirtualSize = Off; - if (C->hasData()) - Header.SizeOfRawData = alignTo(Off, SectorSize); } -void OutputSection::addPermissions(uint32_t C) { - Header.Characteristics |= C & PermMask; +void OutputSection::setPermissions(uint32_t C) { + Header.Characteristics &= ~PermMask; + Header.Characteristics |= C; } -void OutputSection::setPermissions(uint32_t C) { - Header.Characteristics = C & PermMask; +void OutputSection::merge(OutputSection *Other) { + for (Chunk *C : Other->Chunks) + C->setOutputSection(this); + Chunks.insert(Chunks.end(), Other->Chunks.begin(), Other->Chunks.end()); + Other->Chunks.clear(); } // Write the section header to a given buffer. @@ -284,17 +330,22 @@ static Optional<codeview::DebugInfo> loadExistingBuildId(StringRef Path) { // The main function of the writer. void Writer::run() { + ScopedTimer T1(CodeLayoutTimer); + createSections(); createMiscChunks(); createImportTables(); createExportTable(); - if (Config->Relocatable) - createSection(".reloc"); + mergeSections(); assignAddresses(); removeEmptySections(); setSectionPermissions(); createSymbolAndStringTable(); + if (FileSize > UINT32_MAX) + fatal("image size (" + Twine(FileSize) + ") " + + "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); + // We must do this before opening the output file, as it depends on being able // to read the contents of the existing output file. PreviousBuildId = loadExistingBuildId(Config->OutputFile); @@ -308,35 +359,79 @@ void Writer::run() { sortExceptionTable(); writeBuildId(); - if (!Config->PDBPath.empty() && Config->Debug) { + T1.stop(); + if (!Config->PDBPath.empty() && Config->Debug) { assert(BuildId); createPDB(Symtab, OutputSections, SectionTable, *BuildId->BuildId); } writeMapFile(OutputSections); + ScopedTimer T2(DiskCommitTimer); if (auto E = Buffer->commit()) fatal("failed to write the output file: " + toString(std::move(E))); } -static StringRef getOutputSection(StringRef Name) { +static StringRef getOutputSectionName(StringRef Name) { StringRef S = Name.split('$').first; // Treat a later period as a separator for MinGW, for sections like // ".ctors.01234". - S = S.substr(0, S.find('.', 1)); + return S.substr(0, S.find('.', 1)); +} - auto It = Config->Merge.find(S); - if (It == Config->Merge.end()) - return S; - return It->second; +// For /order. +static void sortBySectionOrder(std::vector<Chunk *> &Chunks) { + auto GetPriority = [](const Chunk *C) { + if (auto *Sec = dyn_cast<SectionChunk>(C)) + if (Sec->Sym) + return Config->Order.lookup(Sec->Sym->getName()); + return 0; + }; + + std::stable_sort(Chunks.begin(), Chunks.end(), + [=](const Chunk *A, const Chunk *B) { + return GetPriority(A) < GetPriority(B); + }); } // Create output section objects and add them to OutputSections. void Writer::createSections() { - // First, bin chunks by name. - std::map<StringRef, std::vector<Chunk *>> Map; + // First, create the builtin sections. + const uint32_t DATA = IMAGE_SCN_CNT_INITIALIZED_DATA; + const uint32_t BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA; + const uint32_t CODE = IMAGE_SCN_CNT_CODE; + const uint32_t DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE; + const uint32_t R = IMAGE_SCN_MEM_READ; + const uint32_t W = IMAGE_SCN_MEM_WRITE; + const uint32_t X = IMAGE_SCN_MEM_EXECUTE; + + SmallDenseMap<std::pair<StringRef, uint32_t>, OutputSection *> Sections; + auto CreateSection = [&](StringRef Name, uint32_t OutChars) { + OutputSection *&Sec = Sections[{Name, OutChars}]; + if (!Sec) { + Sec = make<OutputSection>(Name, OutChars); + OutputSections.push_back(Sec); + } + return Sec; + }; + + // Try to match the section order used by link.exe. + TextSec = CreateSection(".text", CODE | R | X); + CreateSection(".bss", BSS | R | W); + RdataSec = CreateSection(".rdata", DATA | R); + BuildidSec = CreateSection(".buildid", DATA | R); + DataSec = CreateSection(".data", DATA | R | W); + PdataSec = CreateSection(".pdata", DATA | R); + IdataSec = CreateSection(".idata", DATA | R); + EdataSec = CreateSection(".edata", DATA | R); + DidatSec = CreateSection(".didat", DATA | R); + RsrcSec = CreateSection(".rsrc", DATA | R); + RelocSec = CreateSection(".reloc", DATA | DISCARDABLE | R); + + // Then bin chunks by name and output characteristics. + std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> Map; for (Chunk *C : Symtab->getChunks()) { auto *SC = dyn_cast<SectionChunk>(C); if (SC && !SC->isLive()) { @@ -344,42 +439,71 @@ void Writer::createSections() { SC->printDiscardedMessage(); continue; } - Map[C->getSectionName()].push_back(C); + Map[{C->getSectionName(), C->getOutputCharacteristics()}].push_back(C); } + // Process an /order option. + if (!Config->Order.empty()) + for (auto &Pair : Map) + sortBySectionOrder(Pair.second); + // Then create an OutputSection for each section. // '$' and all following characters in input section names are // discarded when determining output section. So, .text$foo // contributes to .text, for example. See PE/COFF spec 3.2. - SmallDenseMap<StringRef, OutputSection *> Sections; for (auto Pair : Map) { - StringRef Name = getOutputSection(Pair.first); - OutputSection *&Sec = Sections[Name]; - if (!Sec) { - Sec = make<OutputSection>(Name); - OutputSections.push_back(Sec); - } + StringRef Name = getOutputSectionName(Pair.first.first); + uint32_t OutChars = Pair.first.second; + + // In link.exe, there is a special case for the I386 target where .CRT + // sections are treated as if they have output characteristics DATA | R if + // their characteristics are DATA | R | W. This implements the same special + // case for all architectures. + if (Name == ".CRT") + OutChars = DATA | R; + + OutputSection *Sec = CreateSection(Name, OutChars); std::vector<Chunk *> &Chunks = Pair.second; - for (Chunk *C : Chunks) { + for (Chunk *C : Chunks) Sec->addChunk(C); - Sec->addPermissions(C->getPermissions()); - } } + + // Finally, move some output sections to the end. + auto SectionOrder = [&](OutputSection *S) { + // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because + // the loader cannot handle holes. Stripping can remove other discardable ones + // than .reloc, which is first of them (created early). + if (S->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) + return 2; + // .rsrc should come at the end of the non-discardable sections because its + // size may change by the Win32 UpdateResources() function, causing + // subsequent sections to move (see https://crbug.com/827082). + if (S == RsrcSec) + return 1; + return 0; + }; + std::stable_sort(OutputSections.begin(), OutputSections.end(), + [&](OutputSection *S, OutputSection *T) { + return SectionOrder(S) < SectionOrder(T); + }); } void Writer::createMiscChunks() { - OutputSection *RData = createSection(".rdata"); + for (auto &P : MergeChunk::Instances) + RdataSec->addChunk(P.second); // Create thunks for locally-dllimported symbols. if (!Symtab->LocalImportChunks.empty()) { for (Chunk *C : Symtab->LocalImportChunks) - RData->addChunk(C); + RdataSec->addChunk(C); } // Create Debug Information Chunks if (Config->Debug) { DebugDirectory = make<DebugDirectoryChunk>(DebugRecords); + OutputSection *DebugInfoSec = Config->MinGW ? BuildidSec : RdataSec; + // Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We // output a PDB no matter what, and this chunk provides the only means of // allowing a debugger to match a PDB and an executable. So we need it even @@ -388,12 +512,18 @@ void Writer::createMiscChunks() { BuildId = CVChunk; DebugRecords.push_back(CVChunk); - RData->addChunk(DebugDirectory); + DebugInfoSec->addChunk(DebugDirectory); for (Chunk *C : DebugRecords) - RData->addChunk(C); + DebugInfoSec->addChunk(C); } - createSEHTable(RData); + // Create SEH table. x86-only. + if (Config->Machine == I386) + createSEHTable(); + + // Create /guard:cf tables if requested. + if (Config->GuardCF != GuardCFLevel::Off) + createGuardCFTables(); } // Create .idata section for the DLL-imported symbol table. @@ -414,53 +544,49 @@ void Writer::createImportTables() { std::string DLL = StringRef(File->DLLName).lower(); if (Config->DLLOrder.count(DLL) == 0) Config->DLLOrder[DLL] = Config->DLLOrder.size(); - } - OutputSection *Text = createSection(".text"); - for (ImportFile *File : ImportFile::Instances) { - if (!File->Live) - continue; - - if (DefinedImportThunk *Thunk = File->ThunkSym) - Text->addChunk(Thunk->getChunk()); + if (File->ThunkSym) { + if (!isa<DefinedImportThunk>(File->ThunkSym)) + fatal(toString(*File->ThunkSym) + " was replaced"); + DefinedImportThunk *Thunk = cast<DefinedImportThunk>(File->ThunkSym); + if (File->ThunkLive) + TextSec->addChunk(Thunk->getChunk()); + } + if (File->ImpSym && !isa<DefinedImportData>(File->ImpSym)) + fatal(toString(*File->ImpSym) + " was replaced"); + DefinedImportData *ImpSym = cast_or_null<DefinedImportData>(File->ImpSym); if (Config->DelayLoads.count(StringRef(File->DLLName).lower())) { if (!File->ThunkSym) fatal("cannot delay-load " + toString(File) + - " due to import of data: " + toString(*File->ImpSym)); - DelayIdata.add(File->ImpSym); + " due to import of data: " + toString(*ImpSym)); + DelayIdata.add(ImpSym); } else { - Idata.add(File->ImpSym); + Idata.add(ImpSym); } } - if (!Idata.empty()) { - OutputSection *Sec = createSection(".idata"); + if (!Idata.empty()) for (Chunk *C : Idata.getChunks()) - Sec->addChunk(C); - } + IdataSec->addChunk(C); if (!DelayIdata.empty()) { Defined *Helper = cast<Defined>(Config->DelayLoadHelper); DelayIdata.create(Helper); - OutputSection *Sec = createSection(".didat"); for (Chunk *C : DelayIdata.getChunks()) - Sec->addChunk(C); - Sec = createSection(".data"); + DidatSec->addChunk(C); for (Chunk *C : DelayIdata.getDataChunks()) - Sec->addChunk(C); - Sec = createSection(".text"); + DataSec->addChunk(C); for (Chunk *C : DelayIdata.getCodeChunks()) - Sec->addChunk(C); + TextSec->addChunk(C); } } void Writer::createExportTable() { if (Config->Exports.empty()) return; - OutputSection *Sec = createSection(".edata"); for (Chunk *C : Edata.Chunks) - Sec->addChunk(C); + EdataSec->addChunk(C); } // The Windows loader doesn't seem to like empty sections, @@ -484,19 +610,31 @@ size_t Writer::addEntryToStringTable(StringRef Str) { } Optional<coff_symbol16> Writer::createSymbol(Defined *Def) { - // Relative symbols are unrepresentable in a COFF symbol table. - if (isa<DefinedSynthetic>(Def)) - return None; - - // Don't write dead symbols or symbols in codeview sections to the symbol - // table. - if (!Def->isLive()) + coff_symbol16 Sym; + switch (Def->kind()) { + case Symbol::DefinedAbsoluteKind: + Sym.Value = Def->getRVA(); + Sym.SectionNumber = IMAGE_SYM_ABSOLUTE; + break; + case Symbol::DefinedSyntheticKind: + // Relative symbols are unrepresentable in a COFF symbol table. return None; - if (auto *D = dyn_cast<DefinedRegular>(Def)) - if (D->getChunk()->isCodeView()) + default: { + // Don't write symbols that won't be written to the output to the symbol + // table. + Chunk *C = Def->getChunk(); + if (!C) + return None; + OutputSection *OS = C->getOutputSection(); + if (!OS) return None; - coff_symbol16 Sym; + Sym.Value = Def->getRVA() - OS->getRVA(); + Sym.SectionNumber = OS->SectionIndex; + break; + } + } + StringRef Name = Def->getName(); if (Name.size() > COFF::NameSize) { Sym.Name.Offset.Zeroes = 0; @@ -515,46 +653,27 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) { Sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL; } Sym.NumberOfAuxSymbols = 0; - - switch (Def->kind()) { - case Symbol::DefinedAbsoluteKind: - Sym.Value = Def->getRVA(); - Sym.SectionNumber = IMAGE_SYM_ABSOLUTE; - break; - default: { - uint64_t RVA = Def->getRVA(); - OutputSection *Sec = nullptr; - for (OutputSection *S : OutputSections) { - if (S->getRVA() > RVA) - break; - Sec = S; - } - Sym.Value = RVA - Sec->getRVA(); - Sym.SectionNumber = Sec->SectionIndex; - break; - } - } return Sym; } void Writer::createSymbolAndStringTable() { - // Name field in the section table is 8 byte long. Longer names need - // to be written to the string table. First, construct string table. + // PE/COFF images are limited to 8 byte section names. Longer names can be + // supported by writing a non-standard string table, but this string table is + // not mapped at runtime and the long names will therefore be inaccessible. + // link.exe always truncates section names to 8 bytes, whereas binutils always + // preserves long section names via the string table. LLD adopts a hybrid + // solution where discardable sections have long names preserved and + // non-discardable sections have their names truncated, to ensure that any + // section which is mapped at runtime also has its name mapped at runtime. for (OutputSection *Sec : OutputSections) { - StringRef Name = Sec->getName(); - if (Name.size() <= COFF::NameSize) + if (Sec->Name.size() <= COFF::NameSize) continue; - // If a section isn't discardable (i.e. will be mapped at runtime), - // prefer a truncated section name over a long section name in - // the string table that is unavailable at runtime. This is different from - // what link.exe does, but finding ".eh_fram" instead of "/4" is useful - // to libunwind. - if ((Sec->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0) + if ((Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) continue; - Sec->setStringTableOff(addEntryToStringTable(Name)); + Sec->setStringTableOff(addEntryToStringTable(Sec->Name)); } - if (Config->DebugDwarf) { + if (Config->DebugDwarf || Config->DebugSymtab) { for (ObjFile *File : ObjFile::Instances) { for (Symbol *B : File->getSymbols()) { auto *D = dyn_cast_or_null<Defined>(B); @@ -571,16 +690,45 @@ void Writer::createSymbolAndStringTable() { if (OutputSymtab.empty() && Strtab.empty()) return; - OutputSection *LastSection = OutputSections.back(); // We position the symbol table to be adjacent to the end of the last section. - uint64_t FileOff = LastSection->getFileOff() + - alignTo(LastSection->getRawSize(), SectorSize); + uint64_t FileOff = FileSize; PointerToSymbolTable = FileOff; FileOff += OutputSymtab.size() * sizeof(coff_symbol16); FileOff += 4 + Strtab.size(); FileSize = alignTo(FileOff, SectorSize); } +void Writer::mergeSections() { + if (!PdataSec->getChunks().empty()) { + FirstPdata = PdataSec->getChunks().front(); + LastPdata = PdataSec->getChunks().back(); + } + + for (auto &P : Config->Merge) { + StringRef ToName = P.second; + if (P.first == ToName) + continue; + StringSet<> Names; + while (1) { + if (!Names.insert(ToName).second) + fatal("/merge: cycle found for section '" + P.first + "'"); + auto I = Config->Merge.find(ToName); + if (I == Config->Merge.end()) + break; + ToName = I->second; + } + OutputSection *From = findSection(P.first); + OutputSection *To = findSection(ToName); + if (!From) + continue; + if (!To) { + From->Name = ToName; + continue; + } + To->merge(From); + } +} + // Visits all sections to assign incremental, non-overlapping RVAs and // file offsets. void Writer::assignAddresses() { @@ -590,35 +738,57 @@ void Writer::assignAddresses() { SizeOfHeaders += Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header); SizeOfHeaders = alignTo(SizeOfHeaders, SectorSize); - uint64_t RVA = 0x1000; // The first page is kept unmapped. + uint64_t RVA = PageSize; // The first page is kept unmapped. FileSize = SizeOfHeaders; - // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because - // the loader cannot handle holes. - std::stable_partition( - OutputSections.begin(), OutputSections.end(), [](OutputSection *S) { - return (S->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0; - }); + for (OutputSection *Sec : OutputSections) { - if (Sec->getName() == ".reloc") - addBaserels(Sec); - Sec->setRVA(RVA); - Sec->setFileOffset(FileSize); - RVA += alignTo(Sec->getVirtualSize(), PageSize); - FileSize += alignTo(Sec->getRawSize(), SectorSize); + if (Sec == RelocSec) + addBaserels(); + uint64_t RawSize = 0, VirtualSize = 0; + Sec->Header.VirtualAddress = RVA; + for (Chunk *C : Sec->getChunks()) { + VirtualSize = alignTo(VirtualSize, C->Alignment); + C->setRVA(RVA + VirtualSize); + C->OutputSectionOff = VirtualSize; + C->finalizeContents(); + VirtualSize += C->getSize(); + if (C->hasData()) + RawSize = alignTo(VirtualSize, SectorSize); + } + if (VirtualSize > UINT32_MAX) + error("section larger than 4 GiB: " + Sec->Name); + Sec->Header.VirtualSize = VirtualSize; + Sec->Header.SizeOfRawData = RawSize; + if (RawSize != 0) + Sec->Header.PointerToRawData = FileSize; + RVA += alignTo(VirtualSize, PageSize); + FileSize += alignTo(RawSize, SectorSize); } SizeOfImage = alignTo(RVA, PageSize); } template <typename PEHeaderTy> void Writer::writeHeader() { - // Write DOS stub + // Write DOS header. For backwards compatibility, the first part of a PE/COFF + // executable consists of an MS-DOS MZ executable. If the executable is run + // under DOS, that program gets run (usually to just print an error message). + // When run under Windows, the loader looks at AddressOfNewExeHeader and uses + // the PE header instead. uint8_t *Buf = Buffer->getBufferStart(); auto *DOS = reinterpret_cast<dos_header *>(Buf); - Buf += DOSStubSize; + Buf += sizeof(dos_header); DOS->Magic[0] = 'M'; DOS->Magic[1] = 'Z'; + DOS->UsedBytesInTheLastPage = DOSStubSize % 512; + DOS->FileSizeInPages = divideCeil(DOSStubSize, 512); + DOS->HeaderSizeInParagraphs = sizeof(dos_header) / 16; + DOS->AddressOfRelocationTable = sizeof(dos_header); DOS->AddressOfNewExeHeader = DOSStubSize; + // Write DOS program. + memcpy(Buf, DOSProgram, sizeof(DOSProgram)); + Buf += sizeof(DOSProgram); + // Write PE magic memcpy(Buf, PEMagic, sizeof(PEMagic)); Buf += sizeof(PEMagic); @@ -688,24 +858,27 @@ template <typename PEHeaderTy> void Writer::writeHeader() { PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT; if (!Config->AllowIsolation) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION; - if (Config->Machine == I386 && !SEHTable && - !Symtab->findUnderscore("_load_config_used")) + if (Config->GuardCF != GuardCFLevel::Off) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF; + if (Config->IntegrityCheck) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY; + if (SetNoSEHCharacteristic) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH; if (Config->TerminalServerAware) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE; PE->NumberOfRvaAndSize = NumberfOfDataDirectory; - if (OutputSection *Text = findSection(".text")) { - PE->BaseOfCode = Text->getRVA(); - PE->SizeOfCode = Text->getRawSize(); + if (TextSec->getVirtualSize()) { + PE->BaseOfCode = TextSec->getRVA(); + PE->SizeOfCode = TextSec->getRawSize(); } PE->SizeOfInitializedData = getSizeOfInitializedData(); // Write data directory auto *Dir = reinterpret_cast<data_directory *>(Buf); Buf += sizeof(*Dir) * NumberfOfDataDirectory; - if (OutputSection *Sec = findSection(".edata")) { - Dir[EXPORT_TABLE].RelativeVirtualAddress = Sec->getRVA(); - Dir[EXPORT_TABLE].Size = Sec->getVirtualSize(); + if (!Config->Exports.empty()) { + Dir[EXPORT_TABLE].RelativeVirtualAddress = Edata.getRVA(); + Dir[EXPORT_TABLE].Size = Edata.getSize(); } if (!Idata.empty()) { Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata.getDirRVA(); @@ -713,17 +886,18 @@ template <typename PEHeaderTy> void Writer::writeHeader() { Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA(); Dir[IAT].Size = Idata.getIATSize(); } - if (OutputSection *Sec = findSection(".rsrc")) { - Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA(); - Dir[RESOURCE_TABLE].Size = Sec->getVirtualSize(); + if (RsrcSec->getVirtualSize()) { + Dir[RESOURCE_TABLE].RelativeVirtualAddress = RsrcSec->getRVA(); + Dir[RESOURCE_TABLE].Size = RsrcSec->getVirtualSize(); } - if (OutputSection *Sec = findSection(".pdata")) { - Dir[EXCEPTION_TABLE].RelativeVirtualAddress = Sec->getRVA(); - Dir[EXCEPTION_TABLE].Size = Sec->getVirtualSize(); + if (FirstPdata) { + Dir[EXCEPTION_TABLE].RelativeVirtualAddress = FirstPdata->getRVA(); + Dir[EXCEPTION_TABLE].Size = + LastPdata->getRVA() + LastPdata->getSize() - FirstPdata->getRVA(); } - if (OutputSection *Sec = findSection(".reloc")) { - Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA(); - Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize(); + if (RelocSec->getVirtualSize()) { + Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = RelocSec->getRVA(); + Dir[BASE_RELOCATION_TABLE].Size = RelocSec->getVirtualSize(); } if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) { if (Defined *B = dyn_cast<Defined>(Sym)) { @@ -792,35 +966,172 @@ void Writer::openFile(StringRef Path) { "failed to open " + Path); } -void Writer::createSEHTable(OutputSection *RData) { - // Create SEH table. x86-only. - if (Config->Machine != I386) - return; - - std::set<Defined *> Handlers; +void Writer::createSEHTable() { + // Set the no SEH characteristic on x86 binaries unless we find exception + // handlers. + SetNoSEHCharacteristic = true; + SymbolRVASet Handlers; for (ObjFile *File : ObjFile::Instances) { - if (!File->SEHCompat) + // FIXME: We should error here instead of earlier unless /safeseh:no was + // passed. + if (!File->hasSafeSEH()) return; - for (uint32_t I : File->SXData) - if (Symbol *B = File->getSymbol(I)) - if (B->isLive()) - Handlers.insert(cast<Defined>(B)); + + markSymbolsForRVATable(File, File->getSXDataChunks(), Handlers); + } + + // Remove the "no SEH" characteristic if all object files were built with + // safeseh, we found some exception handlers, and there is a load config in + // the object. + SetNoSEHCharacteristic = + Handlers.empty() || !Symtab->findUnderscore("_load_config_used"); + + maybeAddRVATable(std::move(Handlers), "__safe_se_handler_table", + "__safe_se_handler_count"); +} + +// Add a symbol to an RVA set. Two symbols may have the same RVA, but an RVA set +// cannot contain duplicates. Therefore, the set is uniqued by Chunk and the +// symbol's offset into that Chunk. +static void addSymbolToRVASet(SymbolRVASet &RVASet, Defined *S) { + Chunk *C = S->getChunk(); + if (auto *SC = dyn_cast<SectionChunk>(C)) + C = SC->Repl; // Look through ICF replacement. + uint32_t Off = S->getRVA() - (C ? C->getRVA() : 0); + RVASet.insert({C, Off}); +} + +// Visit all relocations from all section contributions of this object file and +// mark the relocation target as address-taken. +static void markSymbolsWithRelocations(ObjFile *File, + SymbolRVASet &UsedSymbols) { + for (Chunk *C : File->getChunks()) { + // We only care about live section chunks. Common chunks and other chunks + // don't generally contain relocations. + SectionChunk *SC = dyn_cast<SectionChunk>(C); + if (!SC || !SC->isLive()) + continue; + + // Look for relocations in this section against symbols in executable output + // sections. + for (Symbol *Ref : SC->symbols()) { + // FIXME: Do further testing to see if the relocation type matters, + // especially for 32-bit where taking the address of something usually + // uses an absolute relocation instead of a relative one. + if (auto *D = dyn_cast_or_null<Defined>(Ref)) { + Chunk *RefChunk = D->getChunk(); + OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr; + if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE) + addSymbolToRVASet(UsedSymbols, D); + } + } + } +} + +// Create the guard function id table. This is a table of RVAs of all +// address-taken functions. It is sorted and uniqued, just like the safe SEH +// table. +void Writer::createGuardCFTables() { + SymbolRVASet AddressTakenSyms; + SymbolRVASet LongJmpTargets; + for (ObjFile *File : ObjFile::Instances) { + // If the object was compiled with /guard:cf, the address taken symbols + // are in .gfids$y sections, and the longjmp targets are in .gljmp$y + // sections. If the object was not compiled with /guard:cf, we assume there + // were no setjmp targets, and that all code symbols with relocations are + // possibly address-taken. + if (File->hasGuardCF()) { + markSymbolsForRVATable(File, File->getGuardFidChunks(), AddressTakenSyms); + markSymbolsForRVATable(File, File->getGuardLJmpChunks(), LongJmpTargets); + } else { + markSymbolsWithRelocations(File, AddressTakenSyms); + } + } + + // Mark the image entry as address-taken. + if (Config->Entry) + addSymbolToRVASet(AddressTakenSyms, cast<Defined>(Config->Entry)); + + // Ensure sections referenced in the gfid table are 16-byte aligned. + for (const ChunkAndOffset &C : AddressTakenSyms) + if (C.InputChunk->Alignment < 16) + C.InputChunk->Alignment = 16; + + maybeAddRVATable(std::move(AddressTakenSyms), "__guard_fids_table", + "__guard_fids_count"); + + // Add the longjmp target table unless the user told us not to. + if (Config->GuardCF == GuardCFLevel::Full) + maybeAddRVATable(std::move(LongJmpTargets), "__guard_longjmp_table", + "__guard_longjmp_count"); + + // Set __guard_flags, which will be used in the load config to indicate that + // /guard:cf was enabled. + uint32_t GuardFlags = uint32_t(coff_guard_flags::CFInstrumented) | + uint32_t(coff_guard_flags::HasFidTable); + if (Config->GuardCF == GuardCFLevel::Full) + GuardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable); + Symbol *FlagSym = Symtab->findUnderscore("__guard_flags"); + cast<DefinedAbsolute>(FlagSym)->setVA(GuardFlags); +} + +// Take a list of input sections containing symbol table indices and add those +// symbols to an RVA table. The challenge is that symbol RVAs are not known and +// depend on the table size, so we can't directly build a set of integers. +void Writer::markSymbolsForRVATable(ObjFile *File, + ArrayRef<SectionChunk *> SymIdxChunks, + SymbolRVASet &TableSymbols) { + for (SectionChunk *C : SymIdxChunks) { + // Skip sections discarded by linker GC. This comes up when a .gfids section + // is associated with something like a vtable and the vtable is discarded. + // In this case, the associated gfids section is discarded, and we don't + // mark the virtual member functions as address-taken by the vtable. + if (!C->isLive()) + continue; + + // Validate that the contents look like symbol table indices. + ArrayRef<uint8_t> Data = C->getContents(); + if (Data.size() % 4 != 0) { + warn("ignoring " + C->getSectionName() + + " symbol table index section in object " + toString(File)); + continue; + } + + // Read each symbol table index and check if that symbol was included in the + // final link. If so, add it to the table symbol set. + ArrayRef<ulittle32_t> SymIndices( + reinterpret_cast<const ulittle32_t *>(Data.data()), Data.size() / 4); + ArrayRef<Symbol *> ObjSymbols = File->getSymbols(); + for (uint32_t SymIndex : SymIndices) { + if (SymIndex >= ObjSymbols.size()) { + warn("ignoring invalid symbol table index in section " + + C->getSectionName() + " in object " + toString(File)); + continue; + } + if (Symbol *S = ObjSymbols[SymIndex]) { + if (S->isLive()) + addSymbolToRVASet(TableSymbols, cast<Defined>(S)); + } + } } +} - if (Handlers.empty()) +// Replace the absolute table symbol with a synthetic symbol pointing to +// TableChunk so that we can emit base relocations for it and resolve section +// relative relocations. +void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, + StringRef CountSym) { + if (TableSymbols.empty()) return; - SEHTable = make<SEHTableChunk>(Handlers); - RData->addChunk(SEHTable); + RVATableChunk *TableChunk = make<RVATableChunk>(std::move(TableSymbols)); + RdataSec->addChunk(TableChunk); - // 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"); - replaceSymbol<DefinedSynthetic>(T, T->getName(), SEHTable); - cast<DefinedAbsolute>(C)->setVA(SEHTable->getSize() / 4); + Symbol *T = Symtab->findUnderscore(TableSym); + Symbol *C = Symtab->findUnderscore(CountSym); + replaceSymbol<DefinedSynthetic>(T, T->getName(), TableChunk); + cast<DefinedAbsolute>(C)->setVA(TableChunk->getSize() / 4); } // Handles /section options to allow users to overwrite @@ -829,16 +1140,17 @@ void Writer::setSectionPermissions() { for (auto &P : Config->Section) { StringRef Name = P.first; uint32_t Perm = P.second; - if (auto *Sec = findSection(Name)) - Sec->setPermissions(Perm); + for (OutputSection *Sec : OutputSections) + if (Sec->Name == Name) + Sec->setPermissions(Perm); } } // 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; + // Record the number of sections to apply section index relocations + // against absolute symbols. See applySecIdx in Chunks.cpp.. + DefinedAbsolute::NumOutputSections = OutputSections.size(); uint8_t *Buf = Buffer->getBufferStart(); for (OutputSection *Sec : OutputSections) { @@ -846,7 +1158,7 @@ void Writer::writeSections() { // Fill gaps between functions in .text with INT3 instructions // instead of leaving as NUL bytes (which can be interpreted as // ADD instructions). - if (Sec->getPermissions() & IMAGE_SCN_CNT_CODE) + if (Sec->Header.Characteristics & IMAGE_SCN_CNT_CODE) memset(SecBuf, 0xCC, Sec->getRawSize()); for_each(parallel::par, Sec->getChunks().begin(), Sec->getChunks().end(), [&](Chunk *C) { C->writeTo(SecBuf); }); @@ -854,32 +1166,65 @@ void Writer::writeSections() { } void Writer::writeBuildId() { - // If we're not writing a build id (e.g. because /debug is not specified), - // then just return; - if (!Config->Debug) - return; + // There are two important parts to the build ID. + // 1) If building with debug info, the COFF debug directory contains a + // timestamp as well as a Guid and Age of the PDB. + // 2) In all cases, the PE COFF file header also contains a timestamp. + // For reproducibility, instead of a timestamp we want to use a hash of the + // binary, however when building with debug info the hash needs to take into + // account the debug info, since it's possible to add blank lines to a file + // which causes the debug info to change but not the generated code. + // + // To handle this, we first set the Guid and Age in the debug directory (but + // only if we're doing a debug build). Then, we hash the binary (thus causing + // the hash to change if only the debug info changes, since the Age will be + // different). Finally, we write that hash into the debug directory (if + // present) as well as the COFF file header (always). + if (Config->Debug) { + assert(BuildId && "BuildId is not set!"); + if (PreviousBuildId.hasValue()) { + *BuildId->BuildId = *PreviousBuildId; + BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1; + } else { + BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70; + BuildId->BuildId->PDB70.Age = 1; + llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16); + } + } - assert(BuildId && "BuildId is not set!"); + // At this point the only fields in the COFF file which remain unset are the + // "timestamp" in the COFF file header, and the ones in the coff debug + // directory. Now we can hash the file and write that hash to the various + // timestamp fields in the file. + StringRef OutputFileData( + reinterpret_cast<const char *>(Buffer->getBufferStart()), + Buffer->getBufferSize()); - if (PreviousBuildId.hasValue()) { - *BuildId->BuildId = *PreviousBuildId; - BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1; - return; - } + uint32_t Timestamp = Config->Timestamp; + if (Config->Repro) + Timestamp = static_cast<uint32_t>(xxHash64(OutputFileData)); + + if (DebugDirectory) + DebugDirectory->setTimeDateStamp(Timestamp); - BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70; - BuildId->BuildId->PDB70.Age = 1; - llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16); + uint8_t *Buf = Buffer->getBufferStart(); + Buf += DOSStubSize + sizeof(PEMagic); + object::coff_file_header *CoffHeader = + reinterpret_cast<coff_file_header *>(Buf); + CoffHeader->TimeDateStamp = Timestamp; } // Sort .pdata section contents according to PE/COFF spec 5.5. void Writer::sortExceptionTable() { - OutputSection *Sec = findSection(".pdata"); - if (!Sec) + if (!FirstPdata) return; // We assume .pdata contains function table entries only. - uint8_t *Begin = Buffer->getBufferStart() + Sec->getFileOff(); - uint8_t *End = Begin + Sec->getVirtualSize(); + auto BufAddr = [&](Chunk *C) { + return Buffer->getBufferStart() + C->getOutputSection()->getFileOff() + + C->getRVA() - C->getOutputSection()->getRVA(); + }; + uint8_t *Begin = BufAddr(FirstPdata); + uint8_t *End = BufAddr(LastPdata) + LastPdata->getSize(); if (Config->Machine == AMD64) { struct Entry { ulittle32_t Begin, End, Unwind; }; sort(parallel::par, (Entry *)Begin, (Entry *)End, @@ -897,7 +1242,7 @@ void Writer::sortExceptionTable() { OutputSection *Writer::findSection(StringRef Name) { for (OutputSection *Sec : OutputSections) - if (Sec->getName() == Name) + if (Sec->Name == Name) return Sec; return nullptr; } @@ -905,55 +1250,31 @@ OutputSection *Writer::findSection(StringRef Name) { uint32_t Writer::getSizeOfInitializedData() { uint32_t Res = 0; for (OutputSection *S : OutputSections) - if (S->getPermissions() & IMAGE_SCN_CNT_INITIALIZED_DATA) + if (S->Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) Res += S->getRawSize(); return Res; } -// Returns an existing section or create a new one if not found. -OutputSection *Writer::createSection(StringRef Name) { - if (auto *Sec = findSection(Name)) - return Sec; - const auto DATA = IMAGE_SCN_CNT_INITIALIZED_DATA; - const auto BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA; - const auto CODE = IMAGE_SCN_CNT_CODE; - const auto DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE; - const auto R = IMAGE_SCN_MEM_READ; - const auto W = IMAGE_SCN_MEM_WRITE; - const auto X = IMAGE_SCN_MEM_EXECUTE; - uint32_t Perms = StringSwitch<uint32_t>(Name) - .Case(".bss", BSS | R | W) - .Case(".data", DATA | R | W) - .Cases(".didat", ".edata", ".idata", ".rdata", DATA | R) - .Case(".reloc", DATA | DISCARDABLE | R) - .Case(".text", CODE | R | X) - .Default(0); - if (!Perms) - llvm_unreachable("unknown section name"); - auto Sec = make<OutputSection>(Name); - Sec->addPermissions(Perms); - OutputSections.push_back(Sec); - return Sec; -} - -// Dest is .reloc section. Add contents to that section. -void Writer::addBaserels(OutputSection *Dest) { +// Add base relocations to .reloc section. +void Writer::addBaserels() { + if (!Config->Relocatable) + return; std::vector<Baserel> V; for (OutputSection *Sec : OutputSections) { - if (Sec == Dest) + if (Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) continue; // Collect all locations for base relocations. for (Chunk *C : Sec->getChunks()) C->getBaserels(&V); // Add the addresses to .reloc section. if (!V.empty()) - addBaserelBlocks(Dest, V); + addBaserelBlocks(V); V.clear(); } } // Add addresses to .reloc section. Note that addresses are grouped by page. -void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) { +void Writer::addBaserelBlocks(std::vector<Baserel> &V) { const uint32_t Mask = ~uint32_t(PageSize - 1); uint32_t Page = V[0].RVA & Mask; size_t I = 0, J = 1; @@ -961,11 +1282,11 @@ void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) { uint32_t P = V[J].RVA & Mask; if (P == Page) continue; - Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J)); + RelocSec->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J)); I = J; Page = P; } if (I == J) return; - Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J)); + RelocSec->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J)); } |
