Index: COFF/DLL.h =================================================================== --- COFF/DLL.h +++ COFF/DLL.h @@ -76,6 +76,11 @@ public: EdataContents(); std::vector Chunks; + + uint64_t getRVA() { return Chunks[0]->getRVA(); } + uint64_t getSize() { + return Chunks.back()->getRVA() + Chunks.back()->getSize() - getRVA(); + } }; } // namespace coff Index: COFF/DriverUtils.cpp =================================================================== --- COFF/DriverUtils.cpp +++ COFF/DriverUtils.cpp @@ -185,6 +185,10 @@ std::tie(From, To) = S.split('='); if (From.empty() || To.empty()) fatal("/merge: invalid argument: " + S); + if (From == ".rsrc" || To == ".rsrc") + fatal("/merge: cannot merge '.rsrc' with any section"); + if (From == ".reloc" || To == ".reloc") + fatal("/merge: cannot merge '.reloc' with any section"); auto Pair = Config->Merge.insert(std::make_pair(From, To)); bool Inserted = Pair.second; if (!Inserted) { Index: COFF/Writer.cpp =================================================================== --- COFF/Writer.cpp +++ COFF/Writer.cpp @@ -209,12 +209,23 @@ OutputSection *TextSec; OutputSection *RdataSec; 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 @@ -362,17 +373,12 @@ 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)); - - auto It = Config->Merge.find(S); - if (It == Config->Merge.end()) - return S; - return It->second; + return S.substr(0, S.find('.', 1)); } // For /order. @@ -403,10 +409,15 @@ SmallDenseMap Sections; auto CreateSection = [&](StringRef Name, uint32_t Perms) { - auto Sec = make(Name); + auto I = Config->Merge.find(Name); + if (I != Config->Merge.end()) + Name = I->second; + OutputSection *&Sec = Sections[Name]; + if (!Sec) { + Sec = make(Name); + OutputSections.push_back(Sec); + } Sec->addPermissions(Perms); - OutputSections.push_back(Sec); - Sections[Name] = Sec; return Sec; }; @@ -415,7 +426,7 @@ CreateSection(".bss", BSS | R | W); RdataSec = CreateSection(".rdata", DATA | R); DataSec = CreateSection(".data", DATA | R | W); - PdataSec = CreateSection(".pdata", DATA | R); + CreateSection(".pdata", DATA | R); IdataSec = CreateSection(".idata", DATA | R); EdataSec = CreateSection(".edata", DATA | R); DidatSec = CreateSection(".didat", DATA | R); @@ -444,12 +455,13 @@ // discarded when determining output section. So, .text$foo // contributes to .text, for example. See PE/COFF spec 3.2. for (auto Pair : Map) { - StringRef Name = getOutputSection(Pair.first); - OutputSection *&Sec = Sections[Name]; - if (!Sec) { - Sec = make(Name); - OutputSections.push_back(Sec); + StringRef Name = getOutputSectionName(Pair.first); + if (Name == ".pdata") { + if (!FirstPdata) + FirstPdata = Pair.second.front(); + LastPdata = Pair.second.back(); } + OutputSection *Sec = CreateSection(Name, 0); std::vector &Chunks = Pair.second; for (Chunk *C : Chunks) { Sec->addChunk(C); @@ -838,9 +850,9 @@ // Write data directory auto *Dir = reinterpret_cast(Buf); Buf += sizeof(*Dir) * NumberfOfDataDirectory; - if (EdataSec->getVirtualSize()) { - Dir[EXPORT_TABLE].RelativeVirtualAddress = EdataSec->getRVA(); - Dir[EXPORT_TABLE].Size = EdataSec->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(); @@ -852,9 +864,10 @@ Dir[RESOURCE_TABLE].RelativeVirtualAddress = RsrcSec->getRVA(); Dir[RESOURCE_TABLE].Size = RsrcSec->getVirtualSize(); } - if (PdataSec->getVirtualSize()) { - Dir[EXCEPTION_TABLE].RelativeVirtualAddress = PdataSec->getRVA(); - Dir[EXCEPTION_TABLE].Size = PdataSec->getVirtualSize(); + if (FirstPdata) { + Dir[EXCEPTION_TABLE].RelativeVirtualAddress = FirstPdata->getRVA(); + Dir[EXCEPTION_TABLE].Size = + LastPdata->getRVA() + LastPdata->getSize() - FirstPdata->getRVA(); } if (RelocSec->getVirtualSize()) { Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = RelocSec->getRVA(); @@ -1159,11 +1172,15 @@ // Sort .pdata section contents according to PE/COFF spec 5.5. void Writer::sortExceptionTable() { - if (PdataSec->getVirtualSize() == 0) + if (!FirstPdata) return; // We assume .pdata contains function table entries only. - uint8_t *Begin = Buffer->getBufferStart() + PdataSec->getFileOff(); - uint8_t *End = Begin + PdataSec->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, Index: test/COFF/export32.test =================================================================== --- test/COFF/export32.test +++ test/COFF/export32.test @@ -2,6 +2,10 @@ # # RUN: lld-link /out:%t.dll /dll %t.obj /export:exportfn1 /export:exportfn2 # RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK1 %s +# +# RUN: lld-link /out:%t.dll /dll %t.obj /export:exportfn1 /export:exportfn2 /merge:.edata=.rdata +# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK1 %s +# RUN: llvm-readobj -file-headers -sections %t.dll | FileCheck -check-prefix=HEADER-MERGE %s # CHECK1: Export Table: # CHECK1: DLL name: export32.test.tmp.dll @@ -10,6 +14,12 @@ # CHECK1-NEXT: 1 0x1008 exportfn1 # CHECK1-NEXT: 2 0x1010 exportfn2 +# HEADER-MERGE: ExportTableRVA: 0x2000 +# HEADER-MERGE-NEXT: ExportTableSize: 0x7E +# HEADER-MERGE: Name: .rdata +# HEADER-MERGE-NEXT: VirtualSize: 0x7E +# HEADER-MERGE-NEXT: VirtualAddress: 0x2000 + # RUN: lld-link /out:%t.dll /dll %t.obj /export:exportfn1,@5 \ # RUN: /export:exportfn2 /export:mangled # RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK2 %s Index: test/COFF/merge.test =================================================================== --- test/COFF/merge.test +++ test/COFF/merge.test @@ -3,9 +3,21 @@ # RUN: /merge:.foo=.abc /merge:.bar=.def %t.obj /debug # RUN: llvm-readobj -sections %t.exe | FileCheck %s +# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \ +# RUN: /merge:.rsrc=.foo %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-RSRC %s +# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \ +# RUN: /merge:.foo=.rsrc %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-RSRC %s +# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \ +# RUN: /merge:.reloc=.foo %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-RELOC %s +# RUN: not lld-link /out:%t.exe /entry:main /subsystem:console /force \ +# RUN: /merge:.foo=.reloc %t.obj /debug 2>&1 | FileCheck --check-prefix=NO-RELOC %s + # CHECK: Name: .def # CHECK: Name: .abc +# NO-RSRC: /merge: cannot merge '.rsrc' with any section +# NO-RELOC: /merge: cannot merge '.reloc' with any section + --- !COFF header: Machine: IMAGE_FILE_MACHINE_AMD64 Index: test/COFF/unwind.test =================================================================== --- test/COFF/unwind.test +++ test/COFF/unwind.test @@ -4,12 +4,24 @@ # RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=HEADER %s # RUN: llvm-objdump -unwind-info %t.exe | FileCheck -check-prefix=UNWIND %s # -# HEADER: ExceptionTableRVA: 0x2000 +# RUN: lld-link /merge:.pdata=.rdata /out:%t.exe /entry:main %t.obj +# RUN: llvm-readobj -file-headers -sections %t.exe | FileCheck -check-prefix=HEADER-MERGE %s +# +# HEADER: ExceptionTableRVA: 0x3000 +# +# FIXME: llvm-readobj currently does not understand files with .pdata merged +# into .rdata. But we can at least check that the section headers look correct. +# +# HEADER-MERGE: ExceptionTableRVA: 0x2000 +# HEADER-MERGE-NEXT: ExceptionTableSize: 0x30 +# HEADER-MERGE: Name: .rdata +# HEADER-MERGE-NEXT: VirtualSize: 0x34 +# HEADER-MERGE-NEXT: VirtualAddress: 0x2000 # # UNWIND: Function Table: # UNWIND: Start Address: 0x1000 # UNWIND: End Address: 0x101b -# UNWIND: Unwind Info Address: 0x3000 +# UNWIND: Unwind Info Address: 0x4000 # UNWIND: Version: 1 # UNWIND: Flags: 1 UNW_ExceptionHandler # UNWIND: Size of prolog: 18 @@ -26,7 +38,7 @@ # UNWIND: Function Table: # UNWIND: Start Address: 0x1012 # UNWIND: End Address: 0x1012 -# UNWIND: Unwind Info Address: 0x301c +# UNWIND: Unwind Info Address: 0x401c # UNWIND: Version: 1 # UNWIND: Flags: 4 UNW_ChainInfo # UNWIND: Size of prolog: 0 @@ -35,7 +47,7 @@ # UNWIND: Function Table: # UNWIND: Start Address: 0x101b # UNWIND: End Address: 0x101c -# UNWIND: Unwind Info Address: 0x302c +# UNWIND: Unwind Info Address: 0x402c # UNWIND: Version: 1 # UNWIND: Flags: 0 # UNWIND: Size of prolog: 0 @@ -44,7 +56,7 @@ # UNWIND: Function Table: # UNWIND: Start Address: 0x101c # UNWIND: End Address: 0x1039 -# UNWIND: Unwind Info Address: 0x3034 +# UNWIND: Unwind Info Address: 0x4034 # UNWIND: Version: 1 # UNWIND: Flags: 0 # UNWIND: Size of prolog: 14 @@ -122,6 +134,10 @@ - VirtualAddress: 44 SymbolName: .xdata Type: IMAGE_REL_AMD64_ADDR32NB + - Name: .rdata + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 00000000 symbols: - Name: .text Value: 0