diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp index 11e7cf4346b23..a01c69c709876 100644 --- a/lld/COFF/Chunks.cpp +++ b/lld/COFF/Chunks.cpp @@ -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; } } diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp index ae3a8047b7008..b6fbd5a484b5e 100644 --- a/lld/COFF/DLL.cpp +++ b/lld/COFF/DLL.cpp @@ -716,6 +716,63 @@ class ExportOrdinalChunk : public NonSectionChunk { void IdataContents::create(COFFLinkerContext &ctx) { std::vector> 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 &syms : v) { + std::vector 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 &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(ctx, ord)); - addresses.push_back(make(ctx, ord)); + lookupsChunk = make(ctx, ord); + addressesChunk = make(ctx, ord); } else { - auto *c = make(s->getExternalName(), ord); - lookups.push_back(make(ctx, c)); - addresses.push_back(make(ctx, c)); - hints.push_back(c); + hintChunk = make(s->getExternalName(), ord); + lookupsChunk = make(ctx, hintChunk); + addressesChunk = make(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(ctx); + addressesChunk = make(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(s->file); auxIat.push_back(chunk); s->file->impECSym->setLocation(chunk); @@ -743,18 +837,27 @@ void IdataContents::create(COFFLinkerContext &ctx) { chunk = make(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(ctx)); + auxIatCopy.push_back(make(ctx)); } } // Terminate with null values. - lookups.push_back(make(ctx)); - addresses.push_back(make(ctx)); - if (ctx.config.machine == ARM64EC) { + lookups.push_back(lookupsTerminator ? lookupsTerminator + : make(ctx)); + addresses.push_back(addressesTerminator ? addressesTerminator + : make(ctx)); + if (ctx.symtabEC) { auxIat.push_back(make(ctx)); auxIatCopy.push_back(make(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(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(sizeof(ImportDirectoryTableEntry), 4)); diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp index 47faf70e099e1..7b105fb4c17a2 100644 --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -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(mb.getBufferStart()) - ->Machine; + reinterpret_cast(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: diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h index 823561cda247a..21b9aeef21d4f 100644 --- a/lld/COFF/InputFiles.h +++ b/lld/COFF/InputFiles.h @@ -351,11 +351,15 @@ class ImportFile : public InputFile { 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: diff --git a/lld/test/COFF/arm64x-import.test b/lld/test/COFF/arm64x-import.test new file mode 100644 index 0000000000000..bc202e1d17251 --- /dev/null +++ b/lld/test/COFF/arm64x-import.test @@ -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