Skip to content

Commit dc7153d

Browse files
committed
[LLD][COFF] Add support for hybrid exports on ARM64X
1 parent e970063 commit dc7153d

File tree

3 files changed

+174
-21
lines changed

3 files changed

+174
-21
lines changed

lld/COFF/SymbolTable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ class SymbolTable {
155155
llvm::DenseSet<StringRef> directivesExports;
156156
bool hadExplicitExports;
157157

158+
Chunk *edataStart = nullptr;
159+
Chunk *edataEnd = nullptr;
160+
158161
void fixupExports();
159162
void assignExportOrdinals();
160163

lld/COFF/Writer.cpp

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,6 @@ class Writer {
288288
IdataContents idata;
289289
Chunk *importTableStart = nullptr;
290290
uint64_t importTableSize = 0;
291-
Chunk *edataStart = nullptr;
292-
Chunk *edataEnd = nullptr;
293291
Chunk *iatStart = nullptr;
294292
uint64_t iatSize = 0;
295293
DelayLoadContents delayIdata;
@@ -1331,22 +1329,46 @@ void Writer::createExportTable() {
13311329
if (!edataSec->chunks.empty()) {
13321330
// Allow using a custom built export table from input object files, instead
13331331
// of having the linker synthesize the tables.
1334-
if (ctx.symtab.hadExplicitExports)
1335-
Warn(ctx) << "literal .edata sections override exports";
1336-
} else if (!ctx.symtab.exports.empty()) {
1337-
std::vector<Chunk *> edataChunks;
1338-
createEdataChunks(ctx.symtab, edataChunks);
1339-
for (Chunk *c : edataChunks)
1340-
edataSec->addChunk(c);
1341-
}
1342-
if (!edataSec->chunks.empty()) {
1343-
edataStart = edataSec->chunks.front();
1344-
edataEnd = edataSec->chunks.back();
1332+
if (!ctx.hybridSymtab) {
1333+
ctx.symtab.edataStart = edataSec->chunks.front();
1334+
ctx.symtab.edataEnd = edataSec->chunks.back();
1335+
} else {
1336+
// On hybrid target, split EC and native chunks.
1337+
llvm::stable_sort(edataSec->chunks, [=](const Chunk *a, const Chunk *b) {
1338+
return (a->getMachine() != ARM64) < (b->getMachine() != ARM64);
1339+
});
1340+
1341+
for (auto chunk : edataSec->chunks) {
1342+
if (chunk->getMachine() != ARM64) {
1343+
ctx.hybridSymtab->edataStart = chunk;
1344+
ctx.hybridSymtab->edataEnd = edataSec->chunks.back();
1345+
break;
1346+
}
1347+
1348+
if (!ctx.symtab.edataStart)
1349+
ctx.symtab.edataStart = chunk;
1350+
ctx.symtab.edataEnd = chunk;
1351+
}
1352+
}
13451353
}
1346-
// Warn on exported deleting destructor.
1347-
for (auto e : ctx.symtab.exports)
1348-
if (e.sym && e.sym->getName().starts_with("??_G"))
1349-
Warn(ctx) << "export of deleting dtor: " << e.sym;
1354+
ctx.forEachSymtab([&](SymbolTable &symtab) {
1355+
if (symtab.edataStart) {
1356+
if (symtab.hadExplicitExports)
1357+
Warn(ctx) << "literal .edata sections override exports";
1358+
} else if (!symtab.exports.empty()) {
1359+
std::vector<Chunk *> edataChunks;
1360+
createEdataChunks(symtab, edataChunks);
1361+
for (Chunk *c : edataChunks)
1362+
edataSec->addChunk(c);
1363+
symtab.edataStart = edataChunks.front();
1364+
symtab.edataEnd = edataChunks.back();
1365+
}
1366+
1367+
// Warn on exported deleting destructor.
1368+
for (auto e : symtab.exports)
1369+
if (e.sym && e.sym->getName().starts_with("??_G"))
1370+
Warn(ctx) << "export of deleting dtor: " << toString(ctx, *e.sym);
1371+
});
13501372
}
13511373

13521374
void Writer::removeUnusedSections() {
@@ -1819,10 +1841,11 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
18191841
dataDirOffset64 == buf - buffer->getBufferStart());
18201842
auto *dir = reinterpret_cast<data_directory *>(buf);
18211843
buf += sizeof(*dir) * numberOfDataDirectory;
1822-
if (edataStart) {
1823-
dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA();
1824-
dir[EXPORT_TABLE].Size =
1825-
edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA();
1844+
if (ctx.symtab.edataStart) {
1845+
dir[EXPORT_TABLE].RelativeVirtualAddress = ctx.symtab.edataStart->getRVA();
1846+
dir[EXPORT_TABLE].Size = ctx.symtab.edataEnd->getRVA() +
1847+
ctx.symtab.edataEnd->getSize() -
1848+
ctx.symtab.edataStart->getRVA();
18261849
}
18271850
if (importTableStart) {
18281851
dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA();
@@ -2393,6 +2416,12 @@ void Writer::setECSymbols() {
23932416
->replaceKeepingName(altEntrySym, sizeof(SymbolUnion));
23942417
}
23952418

2419+
if (symtab->edataStart)
2420+
ctx.dynamicRelocs->set(
2421+
dataDirOffset64 + EXPORT_TABLE * sizeof(data_directory) +
2422+
offsetof(data_directory, Size),
2423+
symtab->edataEnd->getRVA() - symtab->edataStart->getRVA() +
2424+
symtab->edataEnd->getSize());
23962425
if (hybridPdata.first)
23972426
ctx.dynamicRelocs->set(
23982427
dataDirOffset64 + EXCEPTION_TABLE * sizeof(data_directory) +
@@ -2651,6 +2680,19 @@ void Writer::createDynamicRelocs() {
26512680
Warn(ctx) << "'__chpe_metadata' is missing for ARM64X target";
26522681
}
26532682

2683+
if (ctx.symtab.edataStart != ctx.hybridSymtab->edataStart) {
2684+
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
2685+
dataDirOffset64 +
2686+
EXPORT_TABLE * sizeof(data_directory) +
2687+
offsetof(data_directory, RelativeVirtualAddress),
2688+
ctx.hybridSymtab->edataStart);
2689+
// The Size value is assigned after addresses are finalized.
2690+
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
2691+
dataDirOffset64 +
2692+
EXPORT_TABLE * sizeof(data_directory) +
2693+
offsetof(data_directory, Size));
2694+
}
2695+
26542696
if (pdata.first != hybridPdata.first) {
26552697
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
26562698
dataDirOffset64 +

lld/test/COFF/arm64x-export.test

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-func.s -o arm64ec-fun
55
RUN: llvm-mc -filetype=obj -triple=aarch64-windows arm64-func.s -o arm64-func.obj
66
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func-drectve.s -o arm64ec-drectve.obj
77
RUN: llvm-mc -filetype=obj -triple=aarch64-windows func-drectve.s -o arm64-drectve.obj
8+
RUN: llvm-mc -filetype=obj -triple=aarch64-windows edata.s -o arm64-edata.obj
9+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows edata.s -o arm64ec-edata.obj
810
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
911
RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64.s -o loadconfig-arm64.obj
1012

@@ -36,6 +38,15 @@ RUN: llvm-readobj --headers --coff-exports out-cmd.dll | FileCheck --check-prefi
3638
EXPORTS-EC: ExportTableRVA: 0x0
3739
EXPORTS-EC-NEXT: ExportTableSize: 0x0
3840
EXPORTS-EC-NOT: Name: func
41+
EXPORTS-EC: HybridObject {
42+
EXPORTS-EC: ExportTableRVA: 0x3{{.*}}
43+
EXPORTS-EC-NEXT: ExportTableSize: 0x4{{.*}}
44+
EXPORTS-EC: Export {
45+
EXPORTS-EC-NEXT: Ordinal: 1
46+
EXPORTS-EC-NEXT: Name: func
47+
EXPORTS-EC-NEXT: RVA: 0x2000
48+
EXPORTS-EC-NEXT: }
49+
EXPORTS-EC-NEXT: }
3950

4051
# Export using the EC .drectve section.
4152

@@ -44,6 +55,30 @@ RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj arm64ec-drectve.obj -n
4455
RUN: llvm-objdump -d out-drectve-ec.dll | FileCheck --check-prefix=DISASM-EC %s
4556
RUN: llvm-readobj --headers --coff-exports out-drectve-ec.dll | FileCheck --check-prefix=EXPORTS-EC %s
4657

58+
# Export using the EC .edata section.
59+
60+
RUN: lld-link -machine:arm64x -dll -out:out-edata-ec.dll arm64ec-func.obj arm64-func.obj \
61+
RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj arm64ec-edata.obj -noentry
62+
63+
RUN: llvm-objdump -d out-edata-ec.dll | FileCheck --check-prefix=DISASM-EDATA-EC %s
64+
DISASM-EDATA-EC: 0000000180001000 <.text>:
65+
DISASM-EDATA-EC-NEXT: 180001000: 52800040 mov w0, #0x2 // =2
66+
DISASM-EDATA-EC-NEXT: 180001004: d65f03c0 ret
67+
68+
RUN: llvm-readobj --headers --coff-exports out-edata-ec.dll | FileCheck --check-prefix=EXPORTS-EDATA-EC %s
69+
EXPORTS-EDATA-EC: ExportTableRVA: 0x0
70+
EXPORTS-EDATA-EC-NEXT: ExportTableSize: 0x0
71+
EXPORTS-EDATA-EC-NOT: Name: func
72+
EXPORTS-EDATA-EC: HybridObject {
73+
EXPORTS-EDATA-EC: ExportTableRVA: 0x2{{.*}}
74+
EXPORTS-EDATA-EC-NEXT: ExportTableSize: 0x4{{.*}}
75+
EXPORTS-EDATA-EC: Export {
76+
EXPORTS-EDATA-EC-NEXT: Ordinal: 1
77+
EXPORTS-EDATA-EC-NEXT: Name: func
78+
EXPORTS-EDATA-EC-NEXT: RVA: 0x1000
79+
EXPORTS-EDATA-EC-NEXT: }
80+
EXPORTS-EDATA-EC-NEXT: }
81+
4782
# Export using the native .drectve section.
4883

4984
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
6499
EXPORTS-NATIVE-NEXT: Name: func
65100
EXPORTS-NATIVE-NEXT: RVA: 0x1000
66101
EXPORTS-NATIVE-NEXT: }
102+
EXPORTS-NATIVE: HybridObject {
103+
EXPORTS-NATIVE: ExportTableRVA: 0x0
104+
EXPORTS-NATIVE-NEXT: ExportTableSize: 0x0
105+
EXPORTS-NATIVE-NOT: Name: func
106+
107+
# Export using the native .edata section.
108+
109+
RUN: lld-link -machine:arm64x -dll -out:out-edata.dll arm64ec-func.obj arm64-func.obj \
110+
RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj arm64-edata.obj -noentry
111+
RUN: llvm-objdump -d out-edata.dll | FileCheck --check-prefix=DISASM-NATIVE %s
112+
RUN: llvm-readobj --headers --coff-exports out-edata.dll | FileCheck --check-prefix=EXPORTS-NATIVE %s
67113

68114
# Export using both the native and EC .drectve sections.
69115

@@ -99,6 +145,37 @@ EXPORTS-BOTH-NEXT: Ordinal: 1
99145
EXPORTS-BOTH-NEXT: Name: func
100146
EXPORTS-BOTH-NEXT: RVA: 0x1000
101147
EXPORTS-BOTH-NEXT: }
148+
EXPORTS-BOTH: HybridObject {
149+
EXPORTS-BOTH: ExportTableRVA: 0x4{{.*}}
150+
EXPORTS-BOTH-NEXT: ExportTableSize: 0x4{{.*}}
151+
EXPORTS-BOTH: Export {
152+
EXPORTS-BOTH-NEXT: Ordinal: 1
153+
EXPORTS-BOTH-NEXT: Name: func
154+
EXPORTS-BOTH-NEXT: RVA: 0x3000
155+
EXPORTS-BOTH-NEXT: }
156+
EXPORTS-BOTH-NEXT: }
157+
158+
# Export using both the native and EC .edata sections.
159+
160+
RUN: lld-link -machine:arm64x -dll -out:out-edata-both.dll arm64ec-func.obj arm64-func.obj \
161+
RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj arm64-edata.obj arm64ec-edata.obj -noentry
162+
RUN: llvm-readobj --headers --coff-exports out-edata-both.dll | FileCheck --check-prefix=EXPORTS-EDATA-BOTH %s
163+
EXPORTS-EDATA-BOTH: ExportTableRVA: 0x3{{.*}}
164+
EXPORTS-EDATA-BOTH-NEXT: ExportTableSize: 0x4{{.*}}
165+
EXPORTS-EDATA-BOTH: Export {
166+
EXPORTS-EDATA-BOTH-NEXT: Ordinal: 1
167+
EXPORTS-EDATA-BOTH-NEXT: Name: func
168+
EXPORTS-EDATA-BOTH-NEXT: RVA: 0x1000
169+
EXPORTS-EDATA-BOTH-NEXT: }
170+
EXPORTS-EDATA-BOTH: HybridObject {
171+
EXPORTS-EDATA-BOTH: ExportTableRVA: 0x3{{.*}}
172+
EXPORTS-EDATA-BOTH-NEXT: ExportTableSize: 0x4{{.*}}
173+
EXPORTS-EDATA-BOTH: Export {
174+
EXPORTS-EDATA-BOTH-NEXT: Ordinal: 1
175+
EXPORTS-EDATA-BOTH-NEXT: Name: func
176+
EXPORTS-EDATA-BOTH-NEXT: RVA: 0x2000
177+
EXPORTS-EDATA-BOTH-NEXT: }
178+
EXPORTS-EDATA-BOTH-NEXT: }
102179

103180
#--- arm64-func.s
104181
.section .text,"xr",discard,func
@@ -119,3 +196,34 @@ func:
119196
#--- func-drectve.s
120197
.section .drectve
121198
.ascii "-export:func"
199+
200+
#--- edata.s
201+
.section .edata, "dr"
202+
.align 4
203+
exports:
204+
.long 0 // ExportFlags
205+
.long 0 // TimeDateStamp
206+
.long 0 // MajorVersion + MinorVersion
207+
.rva name // NameRVA
208+
.long 1 // OrdinalBase
209+
.long 1 // AddressTableEntries
210+
.long 1 // NumberOfNamePointers
211+
.rva functions // ExportAddressTableRVA
212+
.rva names // NamePointerRVA
213+
.rva nameordinals // OrdinalTableRVA
214+
215+
names:
216+
.rva funcname_func
217+
218+
nameordinals:
219+
.short 0
220+
221+
functions:
222+
.rva func
223+
.long 0
224+
225+
funcname_func:
226+
.asciz "func"
227+
228+
name:
229+
.asciz "out-edata.dll"

0 commit comments

Comments
 (0)