diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h index 4b62cd05f4fc..7aaa0f5ae4c1 100644 --- a/lld/COFF/Config.h +++ b/lld/COFF/Config.h @@ -122,6 +122,7 @@ struct Configuration { bool dll = false; StringRef implib; std::vector exports; + bool hadExplicitExports; std::set delayLoads; std::map dllOrder; Symbol *delayLoadHelper = nullptr; diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index cc98f396c9ea..b21846879a46 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -1817,6 +1817,7 @@ void LinkerDriver::link(ArrayRef argsArr) { if (errorCount()) return; + config->hadExplicitExports = !config->exports.empty(); if (config->mingw) { // In MinGW, all symbols are automatically exported if no symbols // are chosen to be exported. diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 9bc2f092c94c..032439ea32f8 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -240,6 +240,8 @@ private: IdataContents idata; Chunk *importTableStart = nullptr; uint64_t importTableSize = 0; + Chunk *edataStart = nullptr; + Chunk *edataEnd = nullptr; Chunk *iatStart = nullptr; uint64_t iatSize = 0; DelayLoadContents delayIdata; @@ -837,6 +839,7 @@ void Writer::createSections() { } fixPartialSectionChars(".rsrc", data | r); + fixPartialSectionChars(".edata", data | r); // Even in non MinGW cases, we might need to link against GNU import // libraries. bool hasIdata = fixGnuImportChunks(); @@ -1011,10 +1014,19 @@ void Writer::appendImportThunks() { } void Writer::createExportTable() { - if (config->exports.empty()) - return; - for (Chunk *c : edata.chunks) - edataSec->addChunk(c); + if (!edataSec->chunks.empty()) { + // Allow using a custom built export table from input object files, instead + // of having the linker synthesize the tables. + if (config->hadExplicitExports) + warn("literal .edata sections override exports"); + } else if (!config->exports.empty()) { + for (Chunk *c : edata.chunks) + edataSec->addChunk(c); + } + if (!edataSec->chunks.empty()) { + edataStart = edataSec->chunks.front(); + edataEnd = edataSec->chunks.back(); + } } void Writer::removeUnusedSections() { @@ -1363,9 +1375,10 @@ template void Writer::writeHeader() { // Write data directory auto *dir = reinterpret_cast(buf); buf += sizeof(*dir) * numberOfDataDirectory; - if (!config->exports.empty()) { - dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA(); - dir[EXPORT_TABLE].Size = edata.getSize(); + if (edataStart) { + dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA(); + dir[EXPORT_TABLE].Size = + edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA(); } if (importTableStart) { dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA(); diff --git a/lld/test/COFF/edata.s b/lld/test/COFF/edata.s new file mode 100644 index 000000000000..4e3b9daf4363 --- /dev/null +++ b/lld/test/COFF/edata.s @@ -0,0 +1,61 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-mingw32 -o %t.o %s +# RUN: lld-link -lldmingw -dll -out:%t.dll %t.o -entry:__ImageBase 2>&1 | FileCheck %s --allow-empty --check-prefix=NOWARNING +# RUN: llvm-readobj --coff-exports %t.dll | FileCheck %s +# RUN: lld-link -lldmingw -dll -out:%t.dll %t.o -entry:__ImageBase -export:otherfunc 2>&1 | FileCheck %s --check-prefix=WARNING +# RUN: llvm-readobj --coff-exports %t.dll | FileCheck %s + +# Check that the export table contains the manually crafted content +# instead of the linker generated exports. + +# CHECK: Export { +# CHECK-NEXT: Ordinal: 1 +# CHECK-NEXT: Name: myfunc +# CHECK-NEXT: RVA: +# CHECK-NEXT: } +# CHECK-EMPTY: + +# NOWARNING-NOT: warning + +# WARNING: warning: literal .edata sections override exports + + .text + .globl myfunc +myfunc: + ret + .globl otherfunc +otherfunc: + ret + +// The object contains a manually crafted .edata section, which exports +// myfunc, not otherfunc. + .section .edata, "drw" + .align 4 +exports: + .long 0 // ExportFlags + .long 0 // TimeDateStamp + .long 0 // MajorVersion + MinorVersion + .rva name // NameRVA + .long 1 // OrdinalBase + .long 1 // AddressTableEntries + .long 1 // NumberOfNamePointers + .rva functions // ExportAddressTableRVA + .rva names // NamePointerRVA + .rva nameordinals // OrdinalTableRVA + +names: + .rva funcname_myfunc + +nameordinals: + .short 0 + +functions: + .rva myfunc + .long 0 + +funcname_myfunc: + .asciz "myfunc" + +name: + .asciz "mydll.dll"