diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index c08ffbd8ae83..024cb2c95cd2 100644 --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -294,16 +294,24 @@ void LinkerDriver::addBuffer(std::unique_ptr mb, case file_magic::archive: { std::unique_ptr file = CHECK(Archive::create(mbref), filename + ": failed to parse archive"); - if (wholeArchive) { + + // On ARM64EC/ARM64X, the archive may contain both, potentially conflicting, + // native and EC symbols in the symbol table. Regular archives handle this + // using the extended archive format, which stores the EC symbol table in a + // separate section, but it is not available for thin archives. + // Work around this limitation by lazily parsing all thin archive members + // instead of relying on the archive symbol table. + if (wholeArchive || (ctx.symtab.isEC() && file->isThin())) { Archive *archive = file.get(); make>(std::move(file)); // take ownership int memberIndex = 0; for (MemoryBufferRef m : getArchiveMembers(ctx, archive)) { if (!archive->isThin()) - addArchiveBuffer(m, "", filename, memberIndex++); + addArchiveBuffer(m, "", filename, memberIndex++, + !wholeArchive); else - addThinArchiveBuffer(m, ""); + addThinArchiveBuffer(m, "", !wholeArchive); } return; @@ -418,7 +426,7 @@ void LinkerDriver::enqueuePath(StringRef path, bool lazy, InputOpt inputOpt) { void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, StringRef parentName, - uint64_t offsetInArchive) { + uint64_t offsetInArchive, bool lazy) { file_magic magic = identify_magic(mb.getBuffer()); if (magic == file_magic::coff_import_library) { InputFile *imp = make(ctx, mb); @@ -429,11 +437,9 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, InputFile *obj; if (magic == file_magic::coff_object) { - obj = tryCreateFatLTOFile(ctx, mb, parentName, offsetInArchive, - /*lazy=*/false); + obj = tryCreateFatLTOFile(ctx, mb, parentName, offsetInArchive, lazy); } else if (magic == file_magic::bitcode) { - obj = BitcodeFile::create(ctx, mb, parentName, offsetInArchive, - /*lazy=*/false); + obj = BitcodeFile::create(ctx, mb, parentName, offsetInArchive, lazy); } else if (magic == file_magic::coff_cl_gl_object) { Err(ctx) << mb.getBufferIdentifier() << ": is not a native COFF file. Recompile without /GL?"; @@ -448,12 +454,13 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName, Log(ctx) << "Loaded " << obj << " for " << symName; } -void LinkerDriver::addThinArchiveBuffer(MemoryBufferRef mb, StringRef symName) { +void LinkerDriver::addThinArchiveBuffer(MemoryBufferRef mb, StringRef symName, + bool lazy) { // Pass an empty string as the archive name and an offset of 0 so that // the original filename is used as the buffer identifier. This is // useful for DTLTO, where having the member identifier be the actual // path on disk enables distribution of bitcode files during ThinLTO. - addArchiveBuffer(mb, symName, /*parentName=*/"", /*OffsetInArchive=*/0); + addArchiveBuffer(mb, symName, /*parentName=*/"", /*OffsetInArchive=*/0, lazy); } void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, @@ -478,7 +485,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, enqueueTask([=]() { llvm::TimeTraceScope timeScope("Archive: ", mb.getBufferIdentifier()); ctx.driver.addArchiveBuffer(mb, toCOFFString(ctx, sym), parentName, - offsetInArchive); + offsetInArchive, false); }); return; } @@ -496,7 +503,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c, llvm::TimeTraceScope timeScope("Archive: ", mbOrErr.first->getBufferIdentifier()); ctx.driver.addThinArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), - toCOFFString(ctx, sym)); + toCOFFString(ctx, sym), false); }); } diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h index e0c447cfc7f8..e7a7acebc6e4 100644 --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -178,8 +178,10 @@ private: void addBuffer(std::unique_ptr mb, bool wholeArchive, bool lazy); void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName, - StringRef parentName, uint64_t offsetInArchive); - void addThinArchiveBuffer(MemoryBufferRef mbref, StringRef symName); + StringRef parentName, uint64_t offsetInArchive, + bool lazy); + void addThinArchiveBuffer(MemoryBufferRef mbref, StringRef symName, + bool lazy); void enqueueTask(std::function task); bool run(); diff --git a/lld/test/COFF/arm64ec-thin-lib.s b/lld/test/COFF/arm64ec-thin-lib.s new file mode 100644 index 000000000000..d41bdbf1b30e --- /dev/null +++ b/lld/test/COFF/arm64ec-thin-lib.s @@ -0,0 +1,105 @@ +// REQUIRES: aarch64, x86 +// RUN: split-file %s %t.dir && cd %t.dir + +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows symref.s -o symref-arm64ec.obj +// RUN: llvm-mc -filetype=obj -triple=aarch64-windows symref.s -o symref-aarch64.obj +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows sym.s -o sym-arm64ec.obj +// RUN: llvm-mc -filetype=obj -triple=aarch64-windows sym.s -o sym-aarch64.obj +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows undefref.s -o undefref-arm64ec.obj +// RUN: llvm-mc -filetype=obj -triple=aarch64-windows undefref.s -o undefref-aarch64.obj +// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64.s -o loadconfig-aarch64.obj +// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj + +// RUN: rm -f thin.lib +// RUN: llvm-ar rcs --thin thin.lib sym-arm64ec.obj sym-aarch64.obj undefref-arm64ec.obj undefref-aarch64.obj loadconfig-arm64ec.obj loadconfig-aarch64.obj + +// Test linking an ARM64EC module against a thin library containing both EC and native symbols. + +// RUN: lld-link -machine:arm64ec -dll -noentry -out:test-arm64ec.dll symref-arm64ec.obj thin.lib +// RUN: llvm-readobj --coff-exports test-arm64ec.dll | FileCheck --check-prefix=EXPORTS-ARM64EC %s +// EXPORTS-ARM64EC: Format: COFF-ARM64EC +// EXPORTS-ARM64EC-NEXT: Arch: aarch64 +// EXPORTS-ARM64EC-NEXT: AddressSize: 64bit +// EXPORTS-ARM64EC-NEXT: Export { +// EXPORTS-ARM64EC-NEXT: Ordinal: 1 +// EXPORTS-ARM64EC-NEXT: Name: sym +// EXPORTS-ARM64EC-NEXT: RVA: +// EXPORTS-ARM64EC-NEXT: } + +// Test linking an ARM64X module referencing both EC and native symbols. + +// RUN: lld-link -machine:arm64x -dll -noentry -out:test-arm64x.dll symref-arm64ec.obj symref-aarch64.obj thin.lib +// RUN: llvm-readobj --coff-exports test-arm64x.dll | FileCheck --check-prefix=EXPORTS-ARM64X %s +// EXPORTS-ARM64X: Format: COFF-ARM64X +// EXPORTS-ARM64X-NEXT: Arch: aarch64 +// EXPORTS-ARM64X-NEXT: AddressSize: 64bit +// EXPORTS-ARM64X-NEXT: Export { +// EXPORTS-ARM64X-NEXT: Ordinal: 1 +// EXPORTS-ARM64X-NEXT: Name: sym +// EXPORTS-ARM64X-NEXT: RVA: +// EXPORTS-ARM64X-NEXT: } +// EXPORTS-ARM64X-NEXT: HybridObject { +// EXPORTS-ARM64X-NEXT: Format: COFF-ARM64EC +// EXPORTS-ARM64X-NEXT: Arch: aarch64 +// EXPORTS-ARM64X-NEXT: AddressSize: 64bit +// EXPORTS-ARM64X-NEXT: Export { +// EXPORTS-ARM64X-NEXT: Ordinal: 1 +// EXPORTS-ARM64X-NEXT: Name: sym +// EXPORTS-ARM64X-NEXT: RVA: +// EXPORTS-ARM64X-NEXT: } +// EXPORTS-ARM64X-NEXT: } + +// Test linking an ARM64X module referencing only EC symbol. + +// RUN: lld-link -machine:arm64x -dll -noentry -out:test-arm64x-ecref.dll symref-arm64ec.obj thin.lib +// RUN: llvm-readobj --coff-exports test-arm64x-ecref.dll | FileCheck --check-prefix=EXPORTS-ARM64X2 %s +// EXPORTS-ARM64X2: Format: COFF-ARM64X +// EXPORTS-ARM64X2-NEXT: Arch: aarch64 +// EXPORTS-ARM64X2-NEXT: AddressSize: 64bit +// EXPORTS-ARM64X2-NEXT: HybridObject { +// EXPORTS-ARM64X2-NEXT: Format: COFF-ARM64EC +// EXPORTS-ARM64X2-NEXT: Arch: aarch64 +// EXPORTS-ARM64X2-NEXT: AddressSize: 64bit +// EXPORTS-ARM64X2-NEXT: Export { +// EXPORTS-ARM64X2-NEXT: Ordinal: 1 +// EXPORTS-ARM64X2-NEXT: Name: sym +// EXPORTS-ARM64X2-NEXT: RVA: +// EXPORTS-ARM64X2-NEXT: } +// EXPORTS-ARM64X2-NEXT: } + +// Test linking an ARM64X module referencing only native symbol. + +// RUN: lld-link -machine:arm64x -dll -noentry -out:test-arm64x-nativeref.dll symref-aarch64.obj thin.lib +// RUN: llvm-readobj --coff-exports test-arm64x-nativeref.dll | FileCheck --check-prefix=EXPORTS-ARM64X3 %s +// EXPORTS-ARM64X3: Format: COFF-ARM64X +// EXPORTS-ARM64X3-NEXT: Arch: aarch64 +// EXPORTS-ARM64X3-NEXT: AddressSize: 64bit +// EXPORTS-ARM64X3-NEXT: Export { +// EXPORTS-ARM64X3-NEXT: Ordinal: 1 +// EXPORTS-ARM64X3-NEXT: Name: sym +// EXPORTS-ARM64X3-NEXT: RVA: +// EXPORTS-ARM64X3-NEXT: } +// EXPORTS-ARM64X3-NEXT: HybridObject { +// EXPORTS-ARM64X3-NEXT: Format: COFF-ARM64EC +// EXPORTS-ARM64X3-NEXT: Arch: aarch64 +// EXPORTS-ARM64X3-NEXT: AddressSize: 64bit +// EXPORTS-ARM64X3-NEXT: } + +#--- symref.s + .data + .rva sym + +#--- sym.s + .data + .globl sym +sym: + .word 0 + .section .drectve, "yn" + .ascii " -export:sym,DATA" + +#--- undefref.s + .data + .globl undefref +undefref: + .rva undefsym +