diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h index a0acf5db46903..e5b02ce5904c4 100644 --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -155,6 +155,9 @@ class SymbolTable { llvm::DenseSet directivesExports; bool hadExplicitExports; + Chunk *edataStart = nullptr; + Chunk *edataEnd = nullptr; + void fixupExports(); void assignExportOrdinals(); diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 507621d1e95f8..bef2ced9f2957 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -288,8 +288,6 @@ class Writer { IdataContents idata; Chunk *importTableStart = nullptr; uint64_t importTableSize = 0; - Chunk *edataStart = nullptr; - Chunk *edataEnd = nullptr; Chunk *iatStart = nullptr; uint64_t iatSize = 0; DelayLoadContents delayIdata; @@ -1331,22 +1329,46 @@ void Writer::createExportTable() { if (!edataSec->chunks.empty()) { // Allow using a custom built export table from input object files, instead // of having the linker synthesize the tables. - if (ctx.symtab.hadExplicitExports) - Warn(ctx) << "literal .edata sections override exports"; - } else if (!ctx.symtab.exports.empty()) { - std::vector edataChunks; - createEdataChunks(ctx.symtab, edataChunks); - for (Chunk *c : edataChunks) - edataSec->addChunk(c); - } - if (!edataSec->chunks.empty()) { - edataStart = edataSec->chunks.front(); - edataEnd = edataSec->chunks.back(); + if (!ctx.hybridSymtab) { + ctx.symtab.edataStart = edataSec->chunks.front(); + ctx.symtab.edataEnd = edataSec->chunks.back(); + } else { + // On hybrid target, split EC and native chunks. + llvm::stable_sort(edataSec->chunks, [=](const Chunk *a, const Chunk *b) { + return (a->getMachine() != ARM64) < (b->getMachine() != ARM64); + }); + + for (auto chunk : edataSec->chunks) { + if (chunk->getMachine() != ARM64) { + ctx.hybridSymtab->edataStart = chunk; + ctx.hybridSymtab->edataEnd = edataSec->chunks.back(); + break; + } + + if (!ctx.symtab.edataStart) + ctx.symtab.edataStart = chunk; + ctx.symtab.edataEnd = chunk; + } + } } - // Warn on exported deleting destructor. - for (auto e : ctx.symtab.exports) - if (e.sym && e.sym->getName().starts_with("??_G")) - Warn(ctx) << "export of deleting dtor: " << e.sym; + ctx.forEachSymtab([&](SymbolTable &symtab) { + if (symtab.edataStart) { + if (symtab.hadExplicitExports) + Warn(ctx) << "literal .edata sections override exports"; + } else if (!symtab.exports.empty()) { + std::vector edataChunks; + createEdataChunks(symtab, edataChunks); + for (Chunk *c : edataChunks) + edataSec->addChunk(c); + symtab.edataStart = edataChunks.front(); + symtab.edataEnd = edataChunks.back(); + } + + // Warn on exported deleting destructor. + for (auto e : symtab.exports) + if (e.sym && e.sym->getName().starts_with("??_G")) + Warn(ctx) << "export of deleting dtor: " << toString(ctx, *e.sym); + }); } void Writer::removeUnusedSections() { @@ -1819,10 +1841,11 @@ template void Writer::writeHeader() { dataDirOffset64 == buf - buffer->getBufferStart()); auto *dir = reinterpret_cast(buf); buf += sizeof(*dir) * numberOfDataDirectory; - if (edataStart) { - dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA(); - dir[EXPORT_TABLE].Size = - edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA(); + if (ctx.symtab.edataStart) { + dir[EXPORT_TABLE].RelativeVirtualAddress = ctx.symtab.edataStart->getRVA(); + dir[EXPORT_TABLE].Size = ctx.symtab.edataEnd->getRVA() + + ctx.symtab.edataEnd->getSize() - + ctx.symtab.edataStart->getRVA(); } if (importTableStart) { dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA(); @@ -2393,6 +2416,12 @@ void Writer::setECSymbols() { ->replaceKeepingName(altEntrySym, sizeof(SymbolUnion)); } + if (symtab->edataStart) + ctx.dynamicRelocs->set( + dataDirOffset64 + EXPORT_TABLE * sizeof(data_directory) + + offsetof(data_directory, Size), + symtab->edataEnd->getRVA() - symtab->edataStart->getRVA() + + symtab->edataEnd->getSize()); if (hybridPdata.first) ctx.dynamicRelocs->set( dataDirOffset64 + EXCEPTION_TABLE * sizeof(data_directory) + @@ -2651,6 +2680,19 @@ void Writer::createDynamicRelocs() { Warn(ctx) << "'__chpe_metadata' is missing for ARM64X target"; } + if (ctx.symtab.edataStart != ctx.hybridSymtab->edataStart) { + ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t), + dataDirOffset64 + + EXPORT_TABLE * sizeof(data_directory) + + offsetof(data_directory, RelativeVirtualAddress), + ctx.hybridSymtab->edataStart); + // The Size value is assigned after addresses are finalized. + ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t), + dataDirOffset64 + + EXPORT_TABLE * sizeof(data_directory) + + offsetof(data_directory, Size)); + } + if (pdata.first != hybridPdata.first) { ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t), dataDirOffset64 + diff --git a/lld/test/COFF/arm64x-export.test b/lld/test/COFF/arm64x-export.test index e5d0307e570ef..526be63397358 100644 --- a/lld/test/COFF/arm64x-export.test +++ b/lld/test/COFF/arm64x-export.test @@ -5,6 +5,8 @@ RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-func.s -o arm64ec-fun RUN: llvm-mc -filetype=obj -triple=aarch64-windows arm64-func.s -o arm64-func.obj RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func-drectve.s -o arm64ec-drectve.obj RUN: llvm-mc -filetype=obj -triple=aarch64-windows func-drectve.s -o arm64-drectve.obj +RUN: llvm-mc -filetype=obj -triple=aarch64-windows edata.s -o arm64-edata.obj +RUN: llvm-mc -filetype=obj -triple=arm64ec-windows edata.s -o arm64ec-edata.obj RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64.s -o loadconfig-arm64.obj @@ -36,6 +38,15 @@ RUN: llvm-readobj --headers --coff-exports out-cmd.dll | FileCheck --check-prefi EXPORTS-EC: ExportTableRVA: 0x0 EXPORTS-EC-NEXT: ExportTableSize: 0x0 EXPORTS-EC-NOT: Name: func +EXPORTS-EC: HybridObject { +EXPORTS-EC: ExportTableRVA: 0x3{{.*}} +EXPORTS-EC-NEXT: ExportTableSize: 0x4{{.*}} +EXPORTS-EC: Export { +EXPORTS-EC-NEXT: Ordinal: 1 +EXPORTS-EC-NEXT: Name: func +EXPORTS-EC-NEXT: RVA: 0x2000 +EXPORTS-EC-NEXT: } +EXPORTS-EC-NEXT: } # Export using the EC .drectve section. @@ -44,6 +55,30 @@ RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj arm64ec-drectve.obj -n RUN: llvm-objdump -d out-drectve-ec.dll | FileCheck --check-prefix=DISASM-EC %s RUN: llvm-readobj --headers --coff-exports out-drectve-ec.dll | FileCheck --check-prefix=EXPORTS-EC %s +# Export using the EC .edata section. + +RUN: lld-link -machine:arm64x -dll -out:out-edata-ec.dll arm64ec-func.obj arm64-func.obj \ +RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj arm64ec-edata.obj -noentry + +RUN: llvm-objdump -d out-edata-ec.dll | FileCheck --check-prefix=DISASM-EDATA-EC %s +DISASM-EDATA-EC: 0000000180001000 <.text>: +DISASM-EDATA-EC-NEXT: 180001000: 52800040 mov w0, #0x2 // =2 +DISASM-EDATA-EC-NEXT: 180001004: d65f03c0 ret + +RUN: llvm-readobj --headers --coff-exports out-edata-ec.dll | FileCheck --check-prefix=EXPORTS-EDATA-EC %s +EXPORTS-EDATA-EC: ExportTableRVA: 0x0 +EXPORTS-EDATA-EC-NEXT: ExportTableSize: 0x0 +EXPORTS-EDATA-EC-NOT: Name: func +EXPORTS-EDATA-EC: HybridObject { +EXPORTS-EDATA-EC: ExportTableRVA: 0x2{{.*}} +EXPORTS-EDATA-EC-NEXT: ExportTableSize: 0x4{{.*}} +EXPORTS-EDATA-EC: Export { +EXPORTS-EDATA-EC-NEXT: Ordinal: 1 +EXPORTS-EDATA-EC-NEXT: Name: func +EXPORTS-EDATA-EC-NEXT: RVA: 0x1000 +EXPORTS-EDATA-EC-NEXT: } +EXPORTS-EDATA-EC-NEXT: } + # Export using the native .drectve section. RUN: lld-link -machine:arm64x -dll -out:out-drectve-native.dll arm64ec-func.obj arm64-func.obj \ @@ -64,6 +99,17 @@ EXPORTS-NATIVE-NEXT: Ordinal: 1 EXPORTS-NATIVE-NEXT: Name: func EXPORTS-NATIVE-NEXT: RVA: 0x1000 EXPORTS-NATIVE-NEXT: } +EXPORTS-NATIVE: HybridObject { +EXPORTS-NATIVE: ExportTableRVA: 0x0 +EXPORTS-NATIVE-NEXT: ExportTableSize: 0x0 +EXPORTS-NATIVE-NOT: Name: func + +# Export using the native .edata section. + +RUN: lld-link -machine:arm64x -dll -out:out-edata.dll arm64ec-func.obj arm64-func.obj \ +RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj arm64-edata.obj -noentry +RUN: llvm-objdump -d out-edata.dll | FileCheck --check-prefix=DISASM-NATIVE %s +RUN: llvm-readobj --headers --coff-exports out-edata.dll | FileCheck --check-prefix=EXPORTS-NATIVE %s # Export using both the native and EC .drectve sections. @@ -99,6 +145,37 @@ EXPORTS-BOTH-NEXT: Ordinal: 1 EXPORTS-BOTH-NEXT: Name: func EXPORTS-BOTH-NEXT: RVA: 0x1000 EXPORTS-BOTH-NEXT: } +EXPORTS-BOTH: HybridObject { +EXPORTS-BOTH: ExportTableRVA: 0x4{{.*}} +EXPORTS-BOTH-NEXT: ExportTableSize: 0x4{{.*}} +EXPORTS-BOTH: Export { +EXPORTS-BOTH-NEXT: Ordinal: 1 +EXPORTS-BOTH-NEXT: Name: func +EXPORTS-BOTH-NEXT: RVA: 0x3000 +EXPORTS-BOTH-NEXT: } +EXPORTS-BOTH-NEXT: } + +# Export using both the native and EC .edata sections. + +RUN: lld-link -machine:arm64x -dll -out:out-edata-both.dll arm64ec-func.obj arm64-func.obj \ +RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj arm64-edata.obj arm64ec-edata.obj -noentry +RUN: llvm-readobj --headers --coff-exports out-edata-both.dll | FileCheck --check-prefix=EXPORTS-EDATA-BOTH %s +EXPORTS-EDATA-BOTH: ExportTableRVA: 0x3{{.*}} +EXPORTS-EDATA-BOTH-NEXT: ExportTableSize: 0x4{{.*}} +EXPORTS-EDATA-BOTH: Export { +EXPORTS-EDATA-BOTH-NEXT: Ordinal: 1 +EXPORTS-EDATA-BOTH-NEXT: Name: func +EXPORTS-EDATA-BOTH-NEXT: RVA: 0x1000 +EXPORTS-EDATA-BOTH-NEXT: } +EXPORTS-EDATA-BOTH: HybridObject { +EXPORTS-EDATA-BOTH: ExportTableRVA: 0x3{{.*}} +EXPORTS-EDATA-BOTH-NEXT: ExportTableSize: 0x4{{.*}} +EXPORTS-EDATA-BOTH: Export { +EXPORTS-EDATA-BOTH-NEXT: Ordinal: 1 +EXPORTS-EDATA-BOTH-NEXT: Name: func +EXPORTS-EDATA-BOTH-NEXT: RVA: 0x2000 +EXPORTS-EDATA-BOTH-NEXT: } +EXPORTS-EDATA-BOTH-NEXT: } #--- arm64-func.s .section .text,"xr",discard,func @@ -119,3 +196,34 @@ func: #--- func-drectve.s .section .drectve .ascii "-export:func" + +#--- edata.s + .section .edata, "dr" + .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_func + +nameordinals: + .short 0 + +functions: + .rva func + .long 0 + +funcname_func: + .asciz "func" + +name: + .asciz "out-edata.dll"