diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 20366015b418..ac4f0de86800 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -163,14 +163,13 @@ private: void createSymbolAndStringTable(); void openFile(StringRef OutputPath); template void writeHeader(); - void createSEHTable(OutputSection *RData); - void createGuardCFTables(OutputSection *RData); - void createGLJmpTable(OutputSection *RData); + void createSEHTable(); + void createGuardCFTables(); void markSymbolsForRVATable(ObjFile *File, ArrayRef SymIdxChunks, SymbolRVASet &TableSymbols); - void maybeAddRVATable(OutputSection *RData, SymbolRVASet TableSymbols, - StringRef TableSym, StringRef CountSym); + void maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, + StringRef CountSym); void setSectionPermissions(); void writeSections(); void writeBuildId(); @@ -180,9 +179,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 &V); + void addBaserels(); + void addBaserelBlocks(std::vector &V); uint32_t getSizeOfInitializedData(); std::map> binImports(); @@ -207,6 +205,16 @@ private: uint32_t PointerToSymbolTable = 0; uint64_t SizeOfImage; uint64_t SizeOfHeaders; + + OutputSection *TextSec; + OutputSection *RdataSec; + OutputSection *DataSec; + OutputSection *PdataSec; + OutputSection *IdataSec; + OutputSection *EdataSec; + OutputSection *DidatSec; + OutputSection *RsrcSec; + OutputSection *RelocSec; }; } // anonymous namespace @@ -318,8 +326,6 @@ void Writer::run() { createMiscChunks(); createImportTables(); createExportTable(); - if (Config->Relocatable) - createSection(".reloc"); assignAddresses(); removeEmptySections(); setSectionPermissions(); @@ -386,7 +392,37 @@ static void sortBySectionOrder(std::vector &Chunks) { // Create output section objects and add them to OutputSections. void Writer::createSections() { - // First, bin chunks by name. + // 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 Sections; + auto CreateSection = [&](StringRef Name, uint32_t Perms) { + auto Sec = make(Name); + Sec->addPermissions(Perms); + OutputSections.push_back(Sec); + Sections[Name] = 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); + 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. std::map> Map; for (Chunk *C : Symtab->getChunks()) { auto *SC = dyn_cast(C); @@ -407,7 +443,6 @@ void Writer::createSections() { // '$' 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 Sections; for (auto Pair : Map) { StringRef Name = getOutputSection(Pair.first); OutputSection *&Sec = Sections[Name]; @@ -421,18 +456,38 @@ void Writer::createSections() { Sec->addPermissions(C->getPermissions()); } } + + // Finally, move some output sections to the end. + auto SectionOrder = [&](OutputSection *S) { + // .reloc should come last of all since it refers to RVAs of data in the + // previous sections. + if (S == RelocSec) + return 3; + // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because + // the loader cannot handle holes. + if (S->getPermissions() & 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) - RData->addChunk(P.second); + 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 @@ -447,18 +502,18 @@ void Writer::createMiscChunks() { BuildId = CVChunk; DebugRecords.push_back(CVChunk); - RData->addChunk(DebugDirectory); + RdataSec->addChunk(DebugDirectory); for (Chunk *C : DebugRecords) - RData->addChunk(C); + RdataSec->addChunk(C); } // Create SEH table. x86-only. if (Config->Machine == I386) - createSEHTable(RData); + createSEHTable(); // Create /guard:cf tables if requested. if (Config->GuardCF != GuardCFLevel::Off) - createGuardCFTables(RData); + createGuardCFTables(); } // Create .idata section for the DLL-imported symbol table. @@ -481,13 +536,12 @@ void Writer::createImportTables() { 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()); + TextSec->addChunk(Thunk->getChunk()); if (Config->DelayLoads.count(StringRef(File->DLLName).lower())) { if (!File->ThunkSym) @@ -499,33 +553,27 @@ void Writer::createImportTables() { } } - 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(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, @@ -644,29 +692,6 @@ void Writer::createSymbolAndStringTable() { FileSize = alignTo(FileOff, SectorSize); } -static int sectionIndex(OutputSection *S) { - // Move DISCARDABLE (or non-memory-mapped) sections to the end of file because - // the loader cannot handle holes. - if (S->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) - return 101; - - // Try to match the section order used by link.exe. In particular, it's - // important that .reloc comes last since it refers to RVA's of data in - // the previous sections. .rsrc should come late because its size may - // change by the Win32 UpdateResources() function, causing subsequent - // sections to move (see https://crbug.com/827082). - return StringSwitch(S->Name) - .Case(".text", 1) - .Case(".bss", 2) - .Case(".rdata", 3) - .Case(".data", 4) - .Case(".pdata", 5) - .Case(".idata", 6) - .Case(".rsrc", 99) - .Case(".reloc", 100) - .Default(50); // Default to somewhere in the middle. -} - // Visits all sections to assign incremental, non-overlapping RVAs and // file offsets. void Writer::assignAddresses() { @@ -679,15 +704,9 @@ void Writer::assignAddresses() { uint64_t RVA = PageSize; // The first page is kept unmapped. FileSize = SizeOfHeaders; - // Reorder the sections. - std::stable_sort(OutputSections.begin(), OutputSections.end(), - [](OutputSection *S, OutputSection *T) { - return sectionIndex(S) < sectionIndex(T); - }); - for (OutputSection *Sec : OutputSections) { - if (Sec->Name == ".reloc") - addBaserels(Sec); + if (Sec == RelocSec) + addBaserels(); uint64_t RawSize = 0, VirtualSize = 0; Sec->Header.VirtualAddress = RVA; for (Chunk *C : Sec->getChunks()) { @@ -810,18 +829,18 @@ template void Writer::writeHeader() { 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(Buf); Buf += sizeof(*Dir) * NumberfOfDataDirectory; - if (OutputSection *Sec = findSection(".edata")) { - Dir[EXPORT_TABLE].RelativeVirtualAddress = Sec->getRVA(); - Dir[EXPORT_TABLE].Size = Sec->getVirtualSize(); + if (EdataSec->getVirtualSize()) { + Dir[EXPORT_TABLE].RelativeVirtualAddress = EdataSec->getRVA(); + Dir[EXPORT_TABLE].Size = EdataSec->getVirtualSize(); } if (!Idata.empty()) { Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata.getDirRVA(); @@ -829,17 +848,17 @@ template 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 (PdataSec->getVirtualSize()) { + Dir[EXCEPTION_TABLE].RelativeVirtualAddress = PdataSec->getRVA(); + Dir[EXCEPTION_TABLE].Size = PdataSec->getVirtualSize(); } - 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(Sym)) { @@ -908,7 +927,7 @@ void Writer::openFile(StringRef Path) { "failed to open " + Path); } -void Writer::createSEHTable(OutputSection *RData) { +void Writer::createSEHTable() { SymbolRVASet Handlers; for (ObjFile *File : ObjFile::Instances) { // FIXME: We should error here instead of earlier unless /safeseh:no was @@ -919,7 +938,7 @@ void Writer::createSEHTable(OutputSection *RData) { markSymbolsForRVATable(File, File->getSXDataChunks(), Handlers); } - maybeAddRVATable(RData, std::move(Handlers), "__safe_se_handler_table", + maybeAddRVATable(std::move(Handlers), "__safe_se_handler_table", "__safe_se_handler_count"); } @@ -964,7 +983,7 @@ static void markSymbolsWithRelocations(ObjFile *File, // 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(OutputSection *RData) { +void Writer::createGuardCFTables() { SymbolRVASet AddressTakenSyms; SymbolRVASet LongJmpTargets; for (ObjFile *File : ObjFile::Instances) { @@ -985,12 +1004,12 @@ void Writer::createGuardCFTables(OutputSection *RData) { if (Config->Entry) addSymbolToRVASet(AddressTakenSyms, cast(Config->Entry)); - maybeAddRVATable(RData, std::move(AddressTakenSyms), "__guard_fids_table", + 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(RData, std::move(LongJmpTargets), "__guard_longjmp_table", + maybeAddRVATable(std::move(LongJmpTargets), "__guard_longjmp_table", "__guard_longjmp_count"); // Set __guard_flags, which will be used in the load config to indicate that @@ -1047,14 +1066,13 @@ void Writer::markSymbolsForRVATable(ObjFile *File, // 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(OutputSection *RData, - SymbolRVASet TableSymbols, - StringRef TableSym, StringRef CountSym) { +void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym, + StringRef CountSym) { if (TableSymbols.empty()) return; RVATableChunk *TableChunk = make(std::move(TableSymbols)); - RData->addChunk(TableChunk); + RdataSec->addChunk(TableChunk); Symbol *T = Symtab->findUnderscore(TableSym); Symbol *C = Symtab->findUnderscore(CountSym); @@ -1141,12 +1159,11 @@ void Writer::writeBuildId() { // Sort .pdata section contents according to PE/COFF spec 5.5. void Writer::sortExceptionTable() { - OutputSection *Sec = findSection(".pdata"); - if (!Sec) + if (PdataSec->getVirtualSize() == 0) return; // We assume .pdata contains function table entries only. - uint8_t *Begin = Buffer->getBufferStart() + Sec->getFileOff(); - uint8_t *End = Begin + Sec->getVirtualSize(); + uint8_t *Begin = Buffer->getBufferStart() + PdataSec->getFileOff(); + uint8_t *End = Begin + PdataSec->getVirtualSize(); if (Config->Machine == AMD64) { struct Entry { ulittle32_t Begin, End, Unwind; }; sort(parallel::par, (Entry *)Begin, (Entry *)End, @@ -1177,50 +1194,26 @@ uint32_t Writer::getSizeOfInitializedData() { 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(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(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 V; for (OutputSection *Sec : OutputSections) { - if (Sec == Dest) + if (Sec == RelocSec) 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 &V) { +void Writer::addBaserelBlocks(std::vector &V) { const uint32_t Mask = ~uint32_t(PageSize - 1); uint32_t Page = V[0].RVA & Mask; size_t I = 0, J = 1; @@ -1228,11 +1221,11 @@ void Writer::addBaserelBlocks(OutputSection *Dest, std::vector &V) { uint32_t P = V[J].RVA & Mask; if (P == Page) continue; - Dest->addChunk(make(Page, &V[I], &V[0] + J)); + RelocSec->addChunk(make(Page, &V[I], &V[0] + J)); I = J; Page = P; } if (I == J) return; - Dest->addChunk(make(Page, &V[I], &V[0] + J)); + RelocSec->addChunk(make(Page, &V[I], &V[0] + J)); }