diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -216,7 +216,7 @@ void assignOutputSectionIndices(); void createSymbolAndStringTable(); void openFile(StringRef outputPath); - template void writeHeader(); + template data_directory *writeHeader(); void createSEHTable(); void createRuntimePseudoRelocs(); void insertCtorDtorSymbols(); @@ -229,7 +229,7 @@ void setSectionPermissions(); void writeSections(); void writeBuildId(); - void sortExceptionTable(); + void sortExceptionTable(data_directory *dir); void sortCRTSectionChunks(std::vector &chunks); void addSyntheticIdata(); void fixPartialSectionChars(StringRef name, uint32_t chars); @@ -617,13 +617,10 @@ "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")"); openFile(config->outputFile); - if (config->is64()) { - writeHeader(); - } else { - writeHeader(); - } + data_directory *dir = config->is64() ? writeHeader() + : writeHeader(); writeSections(); - sortExceptionTable(); + sortExceptionTable(dir); t1.stop(); @@ -1290,7 +1287,7 @@ mc->assignSubsectionRVAs(); } -template void Writer::writeHeader() { +template data_directory *Writer::writeHeader() { // 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). @@ -1477,7 +1474,7 @@ buf - outputSections.size() * sizeof(coff_section), buf); if (outputSymtab.empty() && strtab.empty()) - return; + return dir; coff->PointerToSymbolTable = pointerToSymbolTable; uint32_t numberOfSymbols = outputSymtab.size(); @@ -1492,6 +1489,7 @@ write32le(buf, strtab.size() + 4); if (!strtab.empty()) memcpy(buf + 4, strtab.data(), strtab.size()); + return dir; } void Writer::openFile(StringRef path) { @@ -1850,8 +1848,36 @@ coffHeader->TimeDateStamp = timestamp; } +// Remove duplicate RUNTIME_FUNCTION entries from the exception table. +template +static void pruneExceptionTable(Entry *begin, Entry *end, data_directory *dir) { + unsigned removed = 0; + for (Entry *e = begin + 1; e < end - removed;) { + Entry *prev = e - 1; + if (e->begin != prev->begin) { + ++e; + continue; + } + // Duplicates seem to be rare, so moving memory around shouldn't occur too + // often. + memmove(prev, e, (uint8_t *)(end - removed) - (uint8_t *)e); + ++removed; + } + if (removed) { + // Zero-out the reminder of the table. It'll consume a bit extra space in + // the PE image, but that should be fine. + memset(end - removed, 0, removed * sizeof(Entry)); + + // Adjust PE's data directory + dir[EXCEPTION_TABLE].Size = (uint8_t *)(end - removed) - (uint8_t *)begin; + + if (config->verbose) + log("Removed " + Twine(removed) + " duplicate .pdata entries."); + } +} + // Sort .pdata section contents according to PE/COFF spec 5.5. -void Writer::sortExceptionTable() { +void Writer::sortExceptionTable(data_directory *dir) { if (!firstPdata) return; // We assume .pdata contains function table entries only. @@ -1867,6 +1893,7 @@ parallelSort( MutableArrayRef((Entry *)begin, (Entry *)end), [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); + pruneExceptionTable((Entry *)begin, (Entry *)end, dir); return; } if (config->machine == ARMNT || config->machine == ARM64) { @@ -1874,6 +1901,7 @@ parallelSort( MutableArrayRef((Entry *)begin, (Entry *)end), [](const Entry &a, const Entry &b) { return a.begin < b.begin; }); + pruneExceptionTable((Entry *)begin, (Entry *)end, dir); return; } lld::errs() << "warning: don't know how to handle .pdata.\n"; diff --git a/lld/test/COFF/unwind.test b/lld/test/COFF/unwind.test --- a/lld/test/COFF/unwind.test +++ b/lld/test/COFF/unwind.test @@ -1,10 +1,11 @@ # RUN: yaml2obj < %s > %t.obj +# RUN: yaml2obj < %s > %t2.obj # # RUN: lld-link /out:%t.exe /entry:main %t.obj # RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=HEADER %s # RUN: llvm-objdump --unwind-info %t.exe | FileCheck --check-prefix=UNWIND %s # -# RUN: lld-link /merge:.pdata=.rdata /out:%t.exe /entry:main %t.obj +# RUN: lld-link /merge:.pdata=.rdata /out:%t.exe /entry:main %t.obj %t2.obj # RUN: llvm-readobj --file-headers --sections %t.exe | FileCheck -check-prefix=HEADER-MERGE %s # # HEADER: ExceptionTableRVA: 0x3000 @@ -12,10 +13,10 @@ # 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: 0x2004 +# HEADER-MERGE: ExceptionTableRVA: 0x2008 # HEADER-MERGE-NEXT: ExceptionTableSize: 0x30 # HEADER-MERGE: Name: .rdata -# HEADER-MERGE-NEXT: VirtualSize: 0x78 +# HEADER-MERGE-NEXT: VirtualSize: 0xF0 # HEADER-MERGE-NEXT: VirtualAddress: 0x2000 # # UNWIND: Function Table: @@ -73,7 +74,7 @@ Characteristics: [ ] sections: - Name: .text - Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] Alignment: 4 SectionData: 4883EC184889742410440F110424534889E3488D235B4883C418C3C34881ECF0FF00004881ECF0FF80004881C4F0FF80004881C4F0FF0000C3 - Name: .xdata @@ -151,6 +152,7 @@ NumberOfLinenumbers: 0 CheckSum: 0 Number: 1 + Selection: IMAGE_COMDAT_SELECT_ANY - Name: .xdata Value: 0 SectionNumber: 2