Skip to content

[LLD][COFF] Add support for hybrid exports on ARM64X #123724

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lld/COFF/SymbolTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ class SymbolTable {
llvm::DenseSet<StringRef> directivesExports;
bool hadExplicitExports;

Chunk *edataStart = nullptr;
Chunk *edataEnd = nullptr;

void fixupExports();
void assignExportOrdinals();

Expand Down
84 changes: 63 additions & 21 deletions lld/COFF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Chunk *> 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<Chunk *> 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() {
Expand Down Expand Up @@ -1819,10 +1841,11 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
dataDirOffset64 == buf - buffer->getBufferStart());
auto *dir = reinterpret_cast<data_directory *>(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();
Expand Down Expand Up @@ -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) +
Expand Down Expand Up @@ -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 +
Expand Down
108 changes: 108 additions & 0 deletions lld/test/COFF/arm64x-export.test
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.

Expand All @@ -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 \
Expand All @@ -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.

Expand Down Expand Up @@ -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
Expand All @@ -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"
Loading