[LLD][COFF] Implement support for hybrid IAT on ARM64X (#124189)

In hybrid images, the PE header references a single IAT for both native
and EC views, merging entries where possible. When merging isn't
feasible, different imports are grouped together, and ARM64X relocations
are emitted as needed.
This commit is contained in:
Jacek Caban
2025-01-26 22:11:40 +01:00
committed by GitHub
parent 1c4341d176
commit fb01a28903
5 changed files with 706 additions and 20 deletions

View File

@@ -1172,11 +1172,12 @@ uint64_t Arm64XRelocVal::get() const {
size_t Arm64XDynamicRelocEntry::getSize() const {
switch (type) {
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL:
return sizeof(uint16_t); // Just a header.
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE:
return sizeof(uint16_t) + size; // A header and a payload.
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA:
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL:
llvm_unreachable("unsupported type");
return 2 * sizeof(uint16_t); // A header and a delta.
}
llvm_unreachable("invalid type");
}
@@ -1186,6 +1187,9 @@ void Arm64XDynamicRelocEntry::writeTo(uint8_t *buf) const {
*out = (offset.get() & 0xfff) | (type << 12);
switch (type) {
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL:
*out |= ((bit_width(size) - 1) << 14); // Encode the size.
break;
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE:
*out |= ((bit_width(size) - 1) << 14); // Encode the size.
switch (size) {
@@ -1203,8 +1207,23 @@ void Arm64XDynamicRelocEntry::writeTo(uint8_t *buf) const {
}
break;
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA:
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL:
llvm_unreachable("unsupported type");
int delta = value.get();
// Negative offsets use a sign bit in the header.
if (delta < 0) {
*out |= 1 << 14;
delta = -delta;
}
// Depending on the value, the delta is encoded with a shift of 2 or 3 bits.
if (delta & 7) {
assert(!(delta & 3));
delta >>= 2;
} else {
*out |= (1 << 15);
delta >>= 3;
}
out[1] = delta;
assert(!(delta & ~0xffff));
break;
}
}

View File

@@ -716,6 +716,63 @@ private:
void IdataContents::create(COFFLinkerContext &ctx) {
std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
// In hybrid images, EC and native code are usually very similar,
// resulting in a highly similar set of imported symbols. Consequently,
// their import tables can be shared, with ARM64X relocations handling any
// differences. Identify matching import files used by EC and native code, and
// merge them into a single hybrid import entry.
if (ctx.hybridSymtab) {
for (std::vector<DefinedImportData *> &syms : v) {
std::vector<DefinedImportData *> hybridSyms;
ImportFile *prev = nullptr;
for (DefinedImportData *sym : syms) {
ImportFile *file = sym->file;
// At this stage, symbols are sorted by base name, ensuring that
// compatible import files, if present, are adjacent. Check if the
// current symbol's file imports the same symbol as the previously added
// one (if any and if it was not already merged). Additionally, verify
// that one of them is native while the other is EC. In rare cases,
// separate matching import entries may exist within the same namespace,
// which cannot be merged.
if (!prev || file->isEC() == prev->isEC() ||
!file->isSameImport(prev)) {
// We can't merge the import file, just add it to hybridSyms
// and set prev to its file so that we can try to match the next
// symbol.
hybridSyms.push_back(sym);
prev = file;
continue;
}
// A matching symbol may appear in syms in any order. The native variant
// exposes a subset of EC symbols and chunks, so always use the EC
// variant as the hybrid import file. If the native file was already
// added, replace it with the EC symbol in hybridSyms. Otherwise, the EC
// variant is already pushed, so we can simply merge it.
if (file->isEC()) {
hybridSyms.pop_back();
hybridSyms.push_back(sym);
}
// Merge import files by storing their hybrid form in the corresponding
// file class.
prev->hybridFile = file;
file->hybridFile = prev;
prev = nullptr; // A hybrid import file cannot be merged again.
}
// Sort symbols by type: native-only files first, followed by merged
// hybrid files, and then EC-only files.
llvm::stable_sort(hybridSyms,
[](DefinedImportData *a, DefinedImportData *b) {
if (a->file->hybridFile)
return !b->file->hybridFile && b->file->isEC();
return !a->file->isEC() && b->file->isEC();
});
syms = std::move(hybridSyms);
}
}
// Create .idata contents for each DLL.
for (std::vector<DefinedImportData *> &syms : v) {
// Create lookup and address tables. If they have external names,
@@ -723,19 +780,56 @@ void IdataContents::create(COFFLinkerContext &ctx) {
// If they don't (if they are import-by-ordinals), we store only
// ordinal values to the table.
size_t base = lookups.size();
Chunk *lookupsTerminator = nullptr, *addressesTerminator = nullptr;
for (DefinedImportData *s : syms) {
uint16_t ord = s->getOrdinal();
HintNameChunk *hintChunk = nullptr;
Chunk *lookupsChunk, *addressesChunk;
if (s->getExternalName().empty()) {
lookups.push_back(make<OrdinalOnlyChunk>(ctx, ord));
addresses.push_back(make<OrdinalOnlyChunk>(ctx, ord));
lookupsChunk = make<OrdinalOnlyChunk>(ctx, ord);
addressesChunk = make<OrdinalOnlyChunk>(ctx, ord);
} else {
auto *c = make<HintNameChunk>(s->getExternalName(), ord);
lookups.push_back(make<LookupChunk>(ctx, c));
addresses.push_back(make<LookupChunk>(ctx, c));
hints.push_back(c);
hintChunk = make<HintNameChunk>(s->getExternalName(), ord);
lookupsChunk = make<LookupChunk>(ctx, hintChunk);
addressesChunk = make<LookupChunk>(ctx, hintChunk);
hints.push_back(hintChunk);
}
if (s->file->impECSym) {
// Detect the first EC-only import in the hybrid IAT. Emit null chunk
// as a terminator for the native view, and add an ARM64X relocation to
// replace it with the correct import for the EC view.
//
// Additionally, for MSVC compatibility, store the lookup and address
// chunks and append them at the end of EC-only imports, where a null
// terminator chunk would typically be placed. Since they appear after
// the native terminator, they will be ignored in the native view.
// In the EC view, they should act as terminators, so emit ZEROFILL
// relocations overriding them.
if (ctx.hybridSymtab && !lookupsTerminator && s->file->isEC() &&
!s->file->hybridFile) {
lookupsTerminator = lookupsChunk;
addressesTerminator = addressesChunk;
lookupsChunk = make<NullChunk>(ctx);
addressesChunk = make<NullChunk>(ctx);
Arm64XRelocVal relocVal = hintChunk;
if (!hintChunk)
relocVal = (1ULL << 63) | ord;
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE,
sizeof(uint64_t), lookupsChunk, relocVal);
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE,
sizeof(uint64_t), addressesChunk, relocVal);
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL,
sizeof(uint64_t), lookupsTerminator);
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL,
sizeof(uint64_t), addressesTerminator);
}
lookups.push_back(lookupsChunk);
addresses.push_back(addressesChunk);
if (s->file->isEC()) {
auto chunk = make<AuxImportChunk>(s->file);
auxIat.push_back(chunk);
s->file->impECSym->setLocation(chunk);
@@ -743,18 +837,27 @@ void IdataContents::create(COFFLinkerContext &ctx) {
chunk = make<AuxImportChunk>(s->file);
auxIatCopy.push_back(chunk);
s->file->auxImpCopySym->setLocation(chunk);
} else if (ctx.hybridSymtab) {
// Fill the auxiliary IAT with null chunks for native-only imports.
auxIat.push_back(make<NullChunk>(ctx));
auxIatCopy.push_back(make<NullChunk>(ctx));
}
}
// Terminate with null values.
lookups.push_back(make<NullChunk>(ctx));
addresses.push_back(make<NullChunk>(ctx));
if (ctx.config.machine == ARM64EC) {
lookups.push_back(lookupsTerminator ? lookupsTerminator
: make<NullChunk>(ctx));
addresses.push_back(addressesTerminator ? addressesTerminator
: make<NullChunk>(ctx));
if (ctx.symtabEC) {
auxIat.push_back(make<NullChunk>(ctx));
auxIatCopy.push_back(make<NullChunk>(ctx));
}
for (int i = 0, e = syms.size(); i < e; ++i)
for (int i = 0, e = syms.size(); i < e; ++i) {
syms[i]->setLocation(addresses[base + i]);
if (syms[i]->file->hybridFile)
syms[i]->file->hybridFile->impSym->setLocation(addresses[base + i]);
}
// Create the import table header.
dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));
@@ -762,6 +865,27 @@ void IdataContents::create(COFFLinkerContext &ctx) {
dir->lookupTab = lookups[base];
dir->addressTab = addresses[base];
dirs.push_back(dir);
if (ctx.hybridSymtab) {
// If native-only imports exist, they will appear as a prefix to all
// imports. Emit ARM64X relocations to skip them in the EC view.
uint32_t nativeOnly =
llvm::find_if(syms,
[](DefinedImportData *s) { return s->file->isEC(); }) -
syms.begin();
if (nativeOnly) {
ctx.dynamicRelocs->add(
IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
Arm64XRelocVal(
dir, offsetof(ImportDirectoryTableEntry, ImportLookupTableRVA)),
nativeOnly * sizeof(uint64_t));
ctx.dynamicRelocs->add(
IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
Arm64XRelocVal(dir, offsetof(ImportDirectoryTableEntry,
ImportAddressTableRVA)),
nativeOnly * sizeof(uint64_t));
}
}
}
// Add null terminator.
dirs.push_back(make<NullChunk>(sizeof(ImportDirectoryTableEntry), 4));

View File

@@ -1129,15 +1129,21 @@ void ObjFile::enqueuePdbFile(StringRef path, ObjFile *fromFile) {
}
ImportFile::ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m)
: InputFile(ctx.symtab, ImportKind, m), live(!ctx.config.doGC) {}
: InputFile(ctx.getSymtab(getMachineType(m)), ImportKind, m),
live(!ctx.config.doGC) {}
MachineTypes ImportFile::getMachineType() const {
MachineTypes ImportFile::getMachineType(MemoryBufferRef m) {
uint16_t machine =
reinterpret_cast<const coff_import_header *>(mb.getBufferStart())
->Machine;
reinterpret_cast<const coff_import_header *>(m.getBufferStart())->Machine;
return MachineTypes(machine);
}
bool ImportFile::isSameImport(const ImportFile *other) const {
if (!externalName.empty())
return other->externalName == externalName;
return hdr->OrdinalHint == other->hdr->OrdinalHint;
}
ImportThunkChunk *ImportFile::makeImportThunk() {
switch (hdr->Machine) {
case AMD64:

View File

@@ -351,11 +351,15 @@ public:
explicit ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m);
static bool classof(const InputFile *f) { return f->kind() == ImportKind; }
MachineTypes getMachineType() const override;
MachineTypes getMachineType() const override { return getMachineType(mb); }
static MachineTypes getMachineType(MemoryBufferRef m);
bool isSameImport(const ImportFile *other) const;
bool isEC() const { return impECSym != nullptr; }
DefinedImportData *impSym = nullptr;
Defined *thunkSym = nullptr;
ImportThunkChunkARM64EC *impchkThunk = nullptr;
ImportFile *hybridFile = nullptr;
std::string dllName;
private:

View File

@@ -0,0 +1,533 @@
REQUIRES: aarch64
RUN: split-file %s %t.dir && cd %t.dir
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func12-thunks-arm64ec.s -o func12-thunks-arm64ec.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows func12-thunks-arm64.s -o func12-thunks-arm64.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func12-arm64ec.s -o func12-arm64ec.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows func123-arm64.s -o func123-arm64.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func123-arm64ec.s -o func123-arm64ec.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows func12-arm64.s -o func12-arm64.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows func234-arm64.s -o func234-arm64.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func12o-arm64ec.s -o func12o-arm64ec.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows func34-arm64.s -o func34-arm64.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows func34o-arm64.s -o func34o-arm64.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows funco-arm64.s -o funco-arm64.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows icall.s -o icall.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
RUN: llvm-lib -machine:arm64ec -def:imp.def -out:imp-arm64ec.lib
RUN: llvm-lib -machine:arm64 -def:imp.def -out:imp-arm64.lib
RUN: llvm-lib -machine:arm64x -def:imp.def -defArm64Native:imp.def -out:imp-arm64x.lib
RUN: llvm-lib -machine:arm64x -def:imp-ord10.def -defArm64Native:imp.def -out:imp-ecord.lib
RUN: llvm-lib -machine:arm64x -def:imp-ord10.def -defArm64Native:imp-ord20.def -out:imp-ecord.lib
RUN: llvm-lib -machine:arm64x -def:imp2.def -defArm64Native:imp2.def -out:imp2.lib
RUN: llvm-lib -machine:arm64x -def:noname-ec.def -defArm64Native:noname-native.def -out:noname.lib
RUN: llvm-lib -machine:arm64x -def:dup-ec.def -defArm64Native:dup-native.def -out:dup.lib
# Link to the imported func1, func2, and func1's thunks from both native and EC code.
RUN: lld-link -machine:arm64x -dll -noentry -out:test-12-thunks.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func12-thunks-arm64ec.obj func12-thunks-arm64.obj imp-arm64ec.lib imp-arm64.lib
RUN: llvm-objdump -d test-12-thunks.dll | FileCheck --check-prefix=DISASM-12T %s
DISASM-12T: 0000000180001000 <.text>:
DISASM-12T-NEXT: 180001000: f0000010 adrp x16, 0x180004000
DISASM-12T-NEXT: 180001004: f9400610 ldr x16, [x16, #0x8]
DISASM-12T-NEXT: 180001008: d61f0200 br x16
DISASM-12T-NEXT: ...
DISASM-12T-NEXT: 180002000: 52800040 mov w0, #0x2 // =2
DISASM-12T-NEXT: 180002004: d65f03c0 ret
DISASM-12T-NEXT: 180002008: 90000030 adrp x16, 0x180006000
DISASM-12T-NEXT: 18000200c: f9400210 ldr x16, [x16]
DISASM-12T-NEXT: 180002010: d61f0200 br x16
DISASM-12T-NEXT: 180002014: d000000b adrp x11, 0x180004000
DISASM-12T-NEXT: 180002018: f940016b ldr x11, [x11]
DISASM-12T-NEXT: 18000201c: 9000000a adrp x10, 0x180002000 <.text+0x1000>
DISASM-12T-NEXT: 180002020: 9100f14a add x10, x10, #0x3c
DISASM-12T-NEXT: 180002024: 17fffff7 b 0x180002000 <.text+0x1000>
DISASM-12T-NEXT: 180002028: d000000b adrp x11, 0x180004000
DISASM-12T-NEXT: 18000202c: f940056b ldr x11, [x11, #0x8]
DISASM-12T-NEXT: 180002030: d0ffffea adrp x10, 0x180000000
DISASM-12T-NEXT: 180002034: 9100014a add x10, x10, #0x0
DISASM-12T-NEXT: 180002038: 17fffff2 b 0x180002000 <.text+0x1000>
DISASM-12T-NEXT: 18000203c: 52800060 mov w0, #0x3 // =3
DISASM-12T-NEXT: 180002040: d65f03c0 ret
DISASM-12T-NEXT: ...
DISASM-12T-NEXT: 180003000: ff 25 fa 0f 00 00 jmpq *0xffa(%rip) # 0x180004000
RUN: llvm-readobj --coff-imports test-12-thunks.dll | FileCheck --check-prefix=IMPORTS-12 %s
IMPORTS-12: Import {
IMPORTS-12-NEXT: Name: test.dll
IMPORTS-12-NEXT: ImportLookupTableRVA: 0x5348
IMPORTS-12-NEXT: ImportAddressTableRVA: 0x4000
IMPORTS-12-NEXT: Symbol: func1 (0)
IMPORTS-12-NEXT: Symbol: func2 (0)
IMPORTS-12-NEXT: }
IMPORTS-12-NEXT: HybridObject {
IMPORTS-12: Import {
IMPORTS-12-NEXT: Name: test.dll
IMPORTS-12-NEXT: ImportLookupTableRVA: 0x5348
IMPORTS-12-NEXT: ImportAddressTableRVA: 0x4000
IMPORTS-12-NEXT: Symbol: func1 (0)
IMPORTS-12-NEXT: Symbol: func2 (0)
IMPORTS-12-NEXT: }
IMPORTS-12-NEXT: }
RUN: llvm-readobj --hex-dump=.test test-12-thunks.dll | FileCheck --check-prefix=FUNC-12-THUNKS %s
FUNC-12-THUNKS: 0x180009000 00600000 00400000 00300000 08200000
FUNC-12-THUNKS-NEXT: 0x180009010 08600000 08400000
RUN: llvm-readobj --hex-dump=.testa test-12-thunks.dll | FileCheck --check-prefix=FUNC-12-THUNKSA %s
FUNC-12-THUNKSA: 0x18000a000 00400000 08400000 00100000
# If the ordinals of named imports don't match, use the EC value.
RUN: lld-link -machine:arm64x -dll -noentry -out:test-12-thunks-ord.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func12-thunks-arm64ec.obj func12-thunks-arm64.obj imp-ecord.lib
RUN: llvm-readobj --coff-imports test-12-thunks-ord.dll | FileCheck --check-prefix=IMPORTS-ORD %s
IMPORTS-ORD: Format: COFF-ARM64X
IMPORTS-ORD-NEXT: Arch: aarch64
IMPORTS-ORD-NEXT: AddressSize: 64bit
IMPORTS-ORD-NEXT: Import {
IMPORTS-ORD-NEXT: Name: test.dll
IMPORTS-ORD-NEXT: ImportLookupTableRVA: 0x5348
IMPORTS-ORD-NEXT: ImportAddressTableRVA: 0x4000
IMPORTS-ORD-NEXT: Symbol: func1 (11)
IMPORTS-ORD-NEXT: Symbol: func2 (12)
IMPORTS-ORD-NEXT: }
IMPORTS-ORD-NEXT: HybridObject {
IMPORTS-ORD-NEXT: Format: COFF-ARM64EC
IMPORTS-ORD-NEXT: Arch: aarch64
IMPORTS-ORD-NEXT: AddressSize: 64bit
IMPORTS-ORD-NEXT: Import {
IMPORTS-ORD-NEXT: Name: test.dll
IMPORTS-ORD-NEXT: ImportLookupTableRVA: 0x5348
IMPORTS-ORD-NEXT: ImportAddressTableRVA: 0x4000
IMPORTS-ORD-NEXT: Symbol: func1 (11)
IMPORTS-ORD-NEXT: Symbol: func2 (12)
IMPORTS-ORD-NEXT: }
IMPORTS-ORD-NEXT: }
# Link to NONAME imports.
RUN: lld-link -machine:arm64x -dll -noentry -out:test-noname.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func12-thunks-arm64ec.obj func12-thunks-arm64.obj noname.lib
RUN: llvm-readobj --coff-imports test-noname.dll | FileCheck --check-prefix=IMPORTS-ORD2 %s
IMPORTS-ORD2: Format: COFF-ARM64X
IMPORTS-ORD2-NEXT: Arch: aarch64
IMPORTS-ORD2-NEXT: AddressSize: 64bit
IMPORTS-ORD2-NEXT: Import {
IMPORTS-ORD2-NEXT: Name: test.dll
IMPORTS-ORD2-NEXT: ImportLookupTableRVA: 0x5348
IMPORTS-ORD2-NEXT: ImportAddressTableRVA: 0x4000
IMPORTS-ORD2-NEXT: Symbol: (12)
IMPORTS-ORD2-NEXT: Symbol: (11)
IMPORTS-ORD2-NEXT: }
IMPORTS-ORD2-NEXT: HybridObject {
IMPORTS-ORD2-NEXT: Format: COFF-ARM64EC
IMPORTS-ORD2-NEXT: Arch: aarch64
IMPORTS-ORD2-NEXT: AddressSize: 64bit
IMPORTS-ORD2-NEXT: Import {
IMPORTS-ORD2-NEXT: Name: test.dll
IMPORTS-ORD2-NEXT: ImportLookupTableRVA: 0x5350
IMPORTS-ORD2-NEXT: ImportAddressTableRVA: 0x4008
IMPORTS-ORD2-NEXT: Symbol: (11)
IMPORTS-ORD2-NEXT: Symbol: (10)
IMPORTS-ORD2-NEXT: }
IMPORTS-ORD2-NEXT: }
# Link to the imported func1 and func2 from both native and EC code, and func3 from native code.
RUN: lld-link -machine:arm64x -dll -noentry -out:test2.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func12-arm64ec.obj func123-arm64.obj imp-arm64x.lib
RUN: llvm-readobj --coff-imports test2.dll | FileCheck --check-prefix=IMPORTS-123-12 %s
IMPORTS-123-12: Import {
IMPORTS-123-12-NEXT: Name: test.dll
IMPORTS-123-12-NEXT: ImportLookupTableRVA: 0x3338
IMPORTS-123-12-NEXT: ImportAddressTableRVA: 0x2000
IMPORTS-123-12-NEXT: Symbol: func3 (0)
IMPORTS-123-12-NEXT: Symbol: func1 (0)
IMPORTS-123-12-NEXT: Symbol: func2 (0)
IMPORTS-123-12-NEXT: }
IMPORTS-123-12-NEXT: HybridObject {
IMPORTS-123-12: Import {
IMPORTS-123-12-NEXT: Name: test.dll
IMPORTS-123-12-NEXT: ImportLookupTableRVA: 0x3340
IMPORTS-123-12-NEXT: ImportAddressTableRVA: 0x2008
IMPORTS-123-12-NEXT: Symbol: func1 (0)
IMPORTS-123-12-NEXT: Symbol: func2 (0)
IMPORTS-123-12-NEXT: }
IMPORTS-123-12-NEXT: }
RUN: llvm-readobj --hex-dump=.test test2.dll | FileCheck --check-prefix=TEST-123-12 %s
TEST-123-12: 0x180007000 08400000 08200000 10400000 10200000
RUN: llvm-readobj --hex-dump=.testa test2.dll | FileCheck --check-prefix=TEST-123-12A %s
TEST-123-12A: 0x180008000 08200000 10200000 00200000
RUN: llvm-readobj --hex-dump=.rdata test2.dll | FileCheck --check-prefix=TEST-123-12AUX %s
TEST-123-12AUX: 0x180004000 00000000 00000000 08100080 01000000
TEST-123-12AUX-NEXT: 0x180004010 1c100080 01000000 00000000 00000000
# Link to the imported func1 and func2 from both native and EC code, and func3 from EC code.
RUN: lld-link -machine:arm64x -dll -noentry -out:func-12-123.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func123-arm64ec.obj func12-arm64.obj imp-arm64x.lib
RUN: llvm-readobj --coff-imports func-12-123.dll | FileCheck --check-prefix=IMPORTS-12-123 %s
IMPORTS-12-123: Import {
IMPORTS-12-123-NEXT: Name: test.dll
IMPORTS-12-123-NEXT: ImportLookupTableRVA: 0x3338
IMPORTS-12-123-NEXT: ImportAddressTableRVA: 0x2000
IMPORTS-12-123-NEXT: Symbol: func1 (0)
IMPORTS-12-123-NEXT: Symbol: func2 (0)
IMPORTS-12-123-NEXT: }
IMPORTS-12-123-NEXT: HybridObject {
IMPORTS-12-123: Import {
IMPORTS-12-123-NEXT: Name: test.dll
IMPORTS-12-123-NEXT: ImportLookupTableRVA: 0x3338
IMPORTS-12-123-NEXT: ImportAddressTableRVA: 0x2000
IMPORTS-12-123-NEXT: Symbol: func1 (0)
IMPORTS-12-123-NEXT: Symbol: func2 (0)
IMPORTS-12-123-NEXT: Symbol: func3 (0)
IMPORTS-12-123-NEXT: }
IMPORTS-12-123-NEXT: }
RUN: llvm-readobj --hex-dump=.test func-12-123.dll | FileCheck --check-prefix=TEST-12-123 %s
TEST-12-123: 0x180007000 00400000 00200000 08400000 08200000
TEST-12-123-NEXT: 0x180007010 10400000 10200000
RUN: llvm-readobj --hex-dump=.testa func-12-123.dll | FileCheck --check-prefix=TEST-12-123A %s
TEST-12-123A: 0x180008000 00200000 08200000
RUN: llvm-readobj --hex-dump=.rdata func-12-123.dll | FileCheck --check-prefix=TEST-12-123AUX %s
TEST-12-123AUX: 0x180004000 08100080 01000000 1c100080 01000000
TEST-12-123AUX-NEXT: 0x180004010 30100080 01000000 00000000 00000000
# Link to the imported func2 and func3 from both native and EC code, func4 from native code,
# and func1 from EC code.
RUN: lld-link -machine:arm64x -dll -noentry -out:test-234-123.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func123-arm64ec.obj func234-arm64.obj imp-arm64x.lib
RUN: llvm-readobj --coff-imports test-234-123.dll | FileCheck --check-prefix=IMPORTS-234-123 %s
IMPORTS-234-123: Import {
IMPORTS-234-123-NEXT: Name: test.dll
IMPORTS-234-123-NEXT: ImportLookupTableRVA: 0x3338
IMPORTS-234-123-NEXT: ImportAddressTableRVA: 0x2000
IMPORTS-234-123-NEXT: Symbol: func4 (0)
IMPORTS-234-123-NEXT: Symbol: func2 (0)
IMPORTS-234-123-NEXT: Symbol: func3 (0)
IMPORTS-234-123-NEXT: }
IMPORTS-234-123-NEXT: HybridObject {
IMPORTS-234-123: Import {
IMPORTS-234-123-NEXT: Name: test.dll
IMPORTS-234-123-NEXT: ImportLookupTableRVA: 0x3340
IMPORTS-234-123-NEXT: ImportAddressTableRVA: 0x2008
IMPORTS-234-123-NEXT: Symbol: func2 (0)
IMPORTS-234-123-NEXT: Symbol: func3 (0)
IMPORTS-234-123-NEXT: Symbol: func1 (0)
IMPORTS-234-123-NEXT: }
IMPORTS-234-123-NEXT: }
RUN: llvm-readobj --hex-dump=.test test-234-123.dll | FileCheck --check-prefix=TEST-234-123 %s
TEST-234-123: 0x180007000 18400000 18200000 08400000 08200000
TEST-234-123-NEXT: 0x180007010 10400000 1020000
RUN: llvm-readobj --hex-dump=.testa test-234-123.dll | FileCheck --check-prefix=TEST-234-123A %s
TEST-234-123A: 0x180008000 08200000 10200000 00200000
# Link to the imported func3 and func4 from native code, and func1 and func2 from EC code.
RUN: lld-link -machine:arm64x -dll -noentry -out:test-34-12.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func12o-arm64ec.obj func34o-arm64.obj imp-arm64x.lib imp2.lib
RUN: llvm-readobj --coff-imports test-34-12.dll | FileCheck --check-prefix=IMPORTS-34-12 %s
IMPORTS-34-12: Import {
IMPORTS-34-12-NEXT: Name: test.dll
IMPORTS-34-12-NEXT: ImportLookupTableRVA: 0x3350
IMPORTS-34-12-NEXT: ImportAddressTableRVA: 0x2000
IMPORTS-34-12-NEXT: Symbol: func3 (0)
IMPORTS-34-12-NEXT: Symbol: func4 (0)
IMPORTS-34-12-NEXT: }
IMPORTS-34-12-NEXT: Import {
IMPORTS-34-12-NEXT: Name: test2.dll
IMPORTS-34-12-NEXT: ImportLookupTableRVA: 0x3378
IMPORTS-34-12-NEXT: ImportAddressTableRVA: 0x2028
IMPORTS-34-12-NEXT: Symbol: otherfunc (0)
IMPORTS-34-12-NEXT: }
IMPORTS-34-12-NEXT: HybridObject {
IMPORTS-34-12: Import {
IMPORTS-34-12-NEXT: Name: test.dll
IMPORTS-34-12-NEXT: ImportLookupTableRVA: 0x3360
IMPORTS-34-12-NEXT: ImportAddressTableRVA: 0x2010
IMPORTS-34-12-NEXT: Symbol: func1 (0)
IMPORTS-34-12-NEXT: Symbol: func2 (0)
IMPORTS-34-12-NEXT: }
IMPORTS-34-12-NEXT: Import {
IMPORTS-34-12-NEXT: Name: test2.dll
IMPORTS-34-12-NEXT: ImportLookupTableRVA: 0x3378
IMPORTS-34-12-NEXT: ImportAddressTableRVA: 0x2028
IMPORTS-34-12-NEXT: Symbol: otherfunc (0)
IMPORTS-34-12-NEXT: }
IMPORTS-34-12-NEXT: }
RUN: llvm-readobj --hex-dump=.test test-34-12.dll | FileCheck --check-prefix=TEST-23-12 %s
TEST-23-12: 0x180007000 10400000 10200000 18400000 18200000
TEST-23-12-NEXT: 0x180007010 28400000 28200000
RUN: llvm-readobj --hex-dump=.testa test-34-12.dll | FileCheck --check-prefix=TEST-23-12A %s
TEST-23-12A: 0x180008000 00200000 08200000 28200000
# Link only to imported EC functions, with no native imports.
RUN: lld-link -machine:arm64x -dll -noentry -out:test-ec12.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func12-arm64ec.obj funco-arm64.obj imp-arm64x.lib imp2.lib
RUN: llvm-readobj --coff-imports test-ec12.dll | FileCheck --check-prefix=IMPORTS-EC12 %s
IMPORTS-EC12: File: test-ec12.dll
IMPORTS-EC12-NEXT: Format: COFF-ARM64X
IMPORTS-EC12-NEXT: Arch: aarch64
IMPORTS-EC12-NEXT: AddressSize: 64bit
IMPORTS-EC12-NEXT: Import {
IMPORTS-EC12-NEXT: Name: test.dll
IMPORTS-EC12-NEXT: ImportLookupTableRVA: 0x3350
IMPORTS-EC12-NEXT: ImportAddressTableRVA: 0x2000
IMPORTS-EC12-NEXT: }
IMPORTS-EC12-NEXT: Import {
IMPORTS-EC12-NEXT: Name: test2.dll
IMPORTS-EC12-NEXT: ImportLookupTableRVA: 0x3368
IMPORTS-EC12-NEXT: ImportAddressTableRVA: 0x2018
IMPORTS-EC12-NEXT: Symbol: otherfunc (0)
IMPORTS-EC12-NEXT: }
IMPORTS-EC12-NEXT: HybridObject {
IMPORTS-EC12-NEXT: Format: COFF-ARM64EC
IMPORTS-EC12-NEXT: Arch: aarch64
IMPORTS-EC12-NEXT: AddressSize: 64bit
IMPORTS-EC12-NEXT: Import {
IMPORTS-EC12-NEXT: Name: test.dll
IMPORTS-EC12-NEXT: ImportLookupTableRVA: 0x3350
IMPORTS-EC12-NEXT: ImportAddressTableRVA: 0x2000
IMPORTS-EC12-NEXT: Symbol: func1 (0)
IMPORTS-EC12-NEXT: Symbol: func2 (0)
IMPORTS-EC12-NEXT: }
IMPORTS-EC12-NEXT: Import {
IMPORTS-EC12-NEXT: Name: test2.dll
IMPORTS-EC12-NEXT: ImportLookupTableRVA: 0x3370
IMPORTS-EC12-NEXT: ImportAddressTableRVA: 0x2020
IMPORTS-EC12-NEXT: }
IMPORTS-EC12-NEXT: }
# Link only to imported native functions, with no EC imports.
RUN: lld-link -machine:arm64x -dll -noentry -out:test-n12.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: func12-arm64.obj imp-arm64x.lib
RUN: llvm-readobj --coff-imports test-n12.dll | FileCheck --check-prefix=IMPORTS-N12 %s
IMPORTS-N12: Arch: aarch64
IMPORTS-N12-NEXT: AddressSize: 64bit
IMPORTS-N12-NEXT: Import {
IMPORTS-N12-NEXT: Name: test.dll
IMPORTS-N12-NEXT: ImportLookupTableRVA: 0x2330
IMPORTS-N12-NEXT: ImportAddressTableRVA: 0x1000
IMPORTS-N12-NEXT: Symbol: func1 (0)
IMPORTS-N12-NEXT: Symbol: func2 (0)
IMPORTS-N12-NEXT: }
IMPORTS-N12-NEXT: HybridObject {
IMPORTS-N12-NEXT: Format: COFF-ARM64EC
IMPORTS-N12-NEXT: Arch: aarch64
IMPORTS-N12-NEXT: AddressSize: 64bit
IMPORTS-N12-NEXT: Import {
IMPORTS-N12-NEXT: Name: test.dll
IMPORTS-N12-NEXT: ImportLookupTableRVA: 0x2340
IMPORTS-N12-NEXT: ImportAddressTableRVA: 0x1010
IMPORTS-N12-NEXT: }
IMPORTS-N12-NEXT: }
RUN: lld-link -machine:arm64x -dll -noentry -out:test-dup.dll loadconfig-arm64.obj loadconfig-arm64ec.obj icall.obj \
RUN: func12-arm64ec.obj func34-arm64.obj dup.lib
RUN: llvm-readobj --coff-imports test-dup.dll | FileCheck --check-prefix=IMPORTS-DUP %s
IMPORTS-DUP: Format: COFF-ARM64X
IMPORTS-DUP-NEXT: Arch: aarch64
IMPORTS-DUP-NEXT: AddressSize: 64bit
IMPORTS-DUP-NEXT: Import {
IMPORTS-DUP-NEXT: Name: test.dll
IMPORTS-DUP-NEXT: ImportLookupTableRVA: 0x3338
IMPORTS-DUP-NEXT: ImportAddressTableRVA: 0x2000
IMPORTS-DUP-NEXT: Symbol: func4 (0)
IMPORTS-DUP-NEXT: Symbol: func4 (0)
IMPORTS-DUP-NEXT: }
IMPORTS-DUP-NEXT: HybridObject {
IMPORTS-DUP-NEXT: Format: COFF-ARM64EC
IMPORTS-DUP-NEXT: Arch: aarch64
IMPORTS-DUP-NEXT: AddressSize: 64bit
IMPORTS-DUP-NEXT: Import {
IMPORTS-DUP-NEXT: Name: test.dll
IMPORTS-DUP-NEXT: ImportLookupTableRVA: 0x3348
IMPORTS-DUP-NEXT: ImportAddressTableRVA: 0x2010
IMPORTS-DUP-NEXT: Symbol: func1 (0)
IMPORTS-DUP-NEXT: Symbol: func1 (0)
IMPORTS-DUP-NEXT: }
IMPORTS-DUP-NEXT: }
#--- func12-thunks-arm64ec.s
.section .test, "r"
.rva __imp_func1
.rva __imp_aux_func1
.rva func1
.rva "#func1"
.rva __imp_func2
.rva __imp_aux_func2
#--- func12-thunks-arm64.s
.section .testa, "r"
.rva __imp_func1
.rva __imp_func2
.rva func2
#--- func12-arm64ec.s
.section .test, "r"
.rva __imp_func1
.rva __imp_aux_func1
.rva __imp_func2
.rva __imp_aux_func2
#--- func123-arm64.s
.section .testa, "r"
.rva __imp_func1
.rva __imp_func2
.rva __imp_func3
#--- func123-arm64ec.s
.section .test, "r"
.rva __imp_func1
.rva __imp_aux_func1
.rva __imp_func2
.rva __imp_aux_func2
.rva __imp_func3
.rva __imp_aux_func3
#--- func12-arm64.s
.section .testa, "r"
.rva __imp_func1
.rva __imp_func2
#--- func234-arm64.s
.section .testa, "r"
.rva __imp_func2
.rva __imp_func3
.rva __imp_func4
#--- func12o-arm64ec.s
.section .test, "r"
.rva __imp_func1
.rva __imp_aux_func1
.rva __imp_func2
.rva __imp_aux_func2
.rva __imp_otherfunc
.rva __imp_aux_otherfunc
#--- func34-arm64.s
.section .testa, "r"
.rva __imp_func3
.rva __imp_func4
#--- func34o-arm64.s
.section .testa, "r"
.rva __imp_func3
.rva __imp_func4
.rva __imp_otherfunc
#--- funco-arm64.s
.section .testa, "r"
.rva __imp_otherfunc
#--- icall.s
.text
.globl __icall_helper_arm64ec
.p2align 2, 0x0
__icall_helper_arm64ec:
mov w0, #2
ret
.section .hybmp$x, "yi"
.symidx __imp_func1
.symidx func1_exit_thunk
.word 4
.section .wowthk$aa,"xr",discard,func1_exit_thunk
.globl func1_exit_thunk
func1_exit_thunk:
mov w0, #3
ret
#--- imp.def
NAME test.dll
EXPORTS
data_sym DATA
func1
func2
func3
func4
#--- imp-ord10.def
NAME test.dll
EXPORTS
data_sym DATA @10
func1 @11
func2 @12
func3 @13
func4 @14
#--- imp-ord20.def
NAME test.dll
EXPORTS
data_sym DATA @10
func1 @21
func2 @22
func3 @23
func4 @24
#--- imp2.def
NAME test2.dll
EXPORTS
otherfunc
#--- noname-ec.def
NAME test.dll
EXPORTS
func1 @10 NONAME
func2 @11 NONAME
#--- noname-native.def
NAME test.dll
EXPORTS
func1 @12 NONAME
func2 @11 NONAME
#--- dup-ec.def
NAME test.dll
EXPORTS
func1
func2 EXPORTAS func1
#--- dup-native.def
NAME test.dll
EXPORTS
func3 EXPORTAS func4
func4