[LLD] [COFF] Fix linking directly against an ARM64X DLL without import library (#210311)

In mingw mode, when linking against a DLL, the user can either provide a
regular import library, or provide the actual DLL. When linking against
an ARM64X image, add both native and EC views to the symbol table on EC
targets. Also getMachine() on such images returns ARM64X, treat it as
ARM64 instead.
This commit is contained in:
Jacek Caban 2026-07-17 17:30:31 +02:00 committed by GitHub
parent 45f76eac19
commit 30f270edb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 103 additions and 18 deletions

View file

@ -356,7 +356,21 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
break;
case file_magic::pecoff_executable:
if (ctx.config.mingw) {
addFile(make<DLLFile>(ctx.symtab, mbref));
std::unique_ptr<COFFObjectFile> obj =
ObjFile::createCOFFObject(ctx, mbref);
if (ctx.symtab.isEC()) {
// When importing an ARM64X image, add both the native and EC views.
if (std::unique_ptr<MemoryBuffer> hybridView =
obj->getHybridObjectView()) {
std::unique_ptr<COFFObjectFile> hybridObj =
ObjFile::createCOFFObject(ctx, takeBuffer(std::move(hybridView)));
addFile(make<DLLFile>(ctx.symtab, hybridObj));
addFile(make<DLLFile>(*ctx.hybridSymtab, obj));
break;
}
}
auto machine = static_cast<MachineTypes>(obj->getMachine());
addFile(make<DLLFile>(ctx.getSymtab(machine), obj));
break;
}
if (filename.ends_with_insensitive(".dll")) {

View file

@ -1506,17 +1506,6 @@ static bool isRVACode(COFFObjectFile *coffObj, uint64_t rva, InputFile *file) {
}
void DLLFile::parse() {
// Parse a memory buffer as a PE-COFF executable.
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
if (auto *obj = dyn_cast<COFFObjectFile>(bin.get())) {
bin.release();
coffObj.reset(obj);
} else {
Err(symtab.ctx) << toString(this) << " is not a COFF file";
return;
}
if (!coffObj->getPE32Header() && !coffObj->getPE32PlusHeader()) {
Err(symtab.ctx) << toString(this) << " is not a PE-COFF executable";
return;
@ -1564,9 +1553,8 @@ void DLLFile::parse() {
}
MachineTypes DLLFile::getMachineType() const {
if (coffObj)
return static_cast<MachineTypes>(coffObj->getMachine());
return IMAGE_FILE_MACHINE_UNKNOWN;
auto machine = static_cast<MachineTypes>(coffObj->getMachine());
return machine == ARM64X ? ARM64 : machine;
}
void DLLFile::makeImport(DLLFile::Symbol *s) {
@ -1581,7 +1569,7 @@ void DLLFile::makeImport(DLLFile::Symbol *s) {
auto *imp = reinterpret_cast<coff_import_header *>(p);
p += sizeof(*imp);
imp->Sig2 = 0xFFFF;
imp->Machine = coffObj->getMachine();
imp->Machine = static_cast<uint16_t>(getMachineType());
imp->SizeOfData = impSize;
imp->OrdinalHint = 0; // Only linking by name
imp->TypeInfo = (s->nameType << 2) | s->importType;

View file

@ -424,8 +424,10 @@ private:
// .dll file. MinGW only.
class DLLFile : public InputFile {
public:
explicit DLLFile(SymbolTable &symtab, MemoryBufferRef m)
: InputFile(symtab, DLLKind, m) {}
explicit DLLFile(SymbolTable &symtab, std::unique_ptr<COFFObjectFile> &obj)
: InputFile(symtab, DLLKind, obj->getMemoryBufferRef()) {
coffObj.swap(obj);
}
static bool classof(const InputFile *f) { return f->kind() == DLLKind; }
void parse() override;
MachineTypes getMachineType() const override;

View file

@ -0,0 +1,81 @@
REQUIRES: aarch64, x86
RUN: split-file %s %t.dir && cd %t.dir
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows sym-ec.s -o sym-ec.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-ec.s -o ref-ec.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows sym-native.s -o sym-native.obj
RUN: llvm-mc -filetype=obj -triple=aarch64-windows ref-native.s -o ref-native.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: lld-link -machine:arm64x -dll -noentry -out:import.dll sym-ec.obj sym-native.obj loadconfig-arm64ec.obj loadconfig-arm64.obj
RUN: lld-link -machine:arm64 -dll -noentry -out:out-arm64.dll ref-native.obj import.dll \
RUN: -lldmingw -exclude-all-symbols -auto-import:no
RUN: llvm-readobj --coff-imports out-arm64.dll | FileCheck --check-prefix=NATIVE %s
NATIVE: Import {
NATIVE-NEXT: Name: import.dll
NATIVE-NEXT: ImportLookupTableRVA:
NATIVE-NEXT: ImportAddressTableRVA:
NATIVE-NEXT: Symbol: native_data (0)
NATIVE-NEXT: }
RUN: lld-link -machine:arm64ec -dll -noentry -out:out-arm64ec.dll ref-ec.obj import.dll \
RUN: loadconfig-arm64ec.obj -lldmingw -exclude-all-symbols -auto-import:no
RUN: llvm-readobj --coff-imports out-arm64ec.dll | FileCheck --check-prefix=EC %s
EC: Import {
EC-NEXT: Name: import.dll
EC-NEXT: ImportLookupTableRVA:
EC-NEXT: ImportAddressTableRVA:
EC-NEXT: Symbol: ec_data (0)
EC-NEXT: }
RUN: lld-link -machine:arm64x -dll -noentry -out:out-arm64x.dll ref-ec.obj ref-native.obj import.dll \
RUN: loadconfig-arm64ec.obj loadconfig-arm64.obj -lldmingw -exclude-all-symbols -auto-import:no
RUN: llvm-readobj --coff-imports out-arm64x.dll | FileCheck --check-prefix=ARM64X %s
ARM64X: Import {
ARM64X-NEXT: Name: import.dll
ARM64X-NEXT: ImportLookupTableRVA:
ARM64X-NEXT: ImportAddressTableRVA:
ARM64X-NEXT: Symbol: native_data (0)
ARM64X-NEXT: }
ARM64X-NEXT: HybridObject {
ARM64X-NEXT: Format: COFF-ARM64EC
ARM64X-NEXT: Arch: aarch64
ARM64X-NEXT: AddressSize: 64bit
ARM64X-NEXT: Import {
ARM64X-NEXT: Name: import.dll
ARM64X-NEXT: ImportLookupTableRVA:
ARM64X-NEXT: ImportAddressTableRVA:
ARM64X-NEXT: Symbol: ec_data (0)
ARM64X-NEXT: }
ARM64X-NEXT: }
#--- sym-ec.s
.data
.globl ec_data
ec_data:
.word 0
.section .drectve, "yn"
.ascii " -export:ec_data"
#--- sym-native.s
.data
.globl native_data
native_data:
.word 0
.section .drectve, "yn"
.ascii " -export:native_data"
#--- ref-ec.s
.data
.rva __imp_ec_data
#--- ref-native.s
.data
.rva __imp_native_data