From 9b88792291c6441aae7c66c046a9460984ddc7d2 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Wed, 23 Oct 2024 12:43:38 +0200 Subject: [PATCH] [LLD][COFF] Allow overriding EC alias symbols with lazy archive symbols (#113283) On ARM64EC, external function calls emit a pair of weak-dependency aliases: `func` to `#func` and `#func` to the `func` guess exit thunk (instead of a single undefined `func` symbol, which would be emitted on other targets). Allow such aliases to be overridden by lazy archive symbols, just as we would for undefined symbols. --- lld/COFF/InputFiles.cpp | 36 +++++++++++--- lld/COFF/InputFiles.h | 2 +- lld/COFF/SymbolTable.cpp | 10 ++-- lld/COFF/SymbolTable.h | 2 +- lld/COFF/Symbols.h | 4 ++ lld/test/COFF/arm64ec-lib.test | 90 ++++++++++++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 12 deletions(-) diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp index 292c3bfc1eaa9d..b2ea4bdfe64e3c 100644 --- a/lld/COFF/InputFiles.cpp +++ b/lld/COFF/InputFiles.cpp @@ -455,11 +455,35 @@ void ObjFile::initializeSymbols() { COFFSymbolRef coffSym = check(coffObj->getSymbol(i)); bool prevailingComdat; if (coffSym.isUndefined()) { - symbols[i] = createUndefined(coffSym); + symbols[i] = createUndefined(coffSym, false); } else if (coffSym.isWeakExternal()) { - symbols[i] = createUndefined(coffSym); - weakAliases.emplace_back(symbols[i], - coffSym.getAux()); + auto aux = coffSym.getAux(); + bool overrideLazy = true; + + // On ARM64EC, external function calls emit a pair of weak-dependency + // aliases: func to #func and #func to the func guess exit thunk + // (instead of a single undefined func symbol, which would be emitted on + // other targets). Allow such aliases to be overridden by lazy archive + // symbols, just as we would for undefined symbols. + if (isArm64EC(getMachineType()) && + aux->Characteristics == IMAGE_WEAK_EXTERN_ANTI_DEPENDENCY) { + COFFSymbolRef targetSym = check(coffObj->getSymbol(aux->TagIndex)); + if (!targetSym.isAnyUndefined()) { + // If the target is defined, it may be either a guess exit thunk or + // the actual implementation. If it's the latter, consider the alias + // to be part of the implementation and override potential lazy + // archive symbols. + StringRef targetName = check(coffObj->getSymbolName(targetSym)); + StringRef name = check(coffObj->getSymbolName(coffSym)); + std::optional mangledName = + getArm64ECMangledFunctionName(name); + overrideLazy = mangledName == targetName; + } else { + overrideLazy = false; + } + } + symbols[i] = createUndefined(coffSym, overrideLazy); + weakAliases.emplace_back(symbols[i], aux); } else if (std::optional optSym = createDefined(coffSym, comdatDefs, prevailingComdat)) { symbols[i] = *optSym; @@ -508,9 +532,9 @@ void ObjFile::initializeSymbols() { decltype(sparseChunks)().swap(sparseChunks); } -Symbol *ObjFile::createUndefined(COFFSymbolRef sym) { +Symbol *ObjFile::createUndefined(COFFSymbolRef sym, bool overrideLazy) { StringRef name = check(coffObj->getSymbolName(sym)); - return ctx.symtab.addUndefined(name, this, sym.isWeakExternal()); + return ctx.symtab.addUndefined(name, this, overrideLazy); } static const coff_aux_section_definition *findSectionDef(COFFObjectFile *obj, diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h index a20b097cbe04af..77f7e298166eec 100644 --- a/lld/COFF/InputFiles.h +++ b/lld/COFF/InputFiles.h @@ -272,7 +272,7 @@ class ObjFile : public InputFile { &comdatDefs, bool &prevailingComdat); Symbol *createRegular(COFFSymbolRef sym); - Symbol *createUndefined(COFFSymbolRef sym); + Symbol *createUndefined(COFFSymbolRef sym, bool overrideLazy); std::unique_ptr coffObj; diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp index 230ae74dfb21d0..435b3bf4dbab80 100644 --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -620,9 +620,9 @@ void SymbolTable::initializeECThunks() { } Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f, - bool isWeakAlias) { + bool overrideLazy) { auto [s, wasInserted] = insert(name, f); - if (wasInserted || (s->isLazy() && isWeakAlias)) { + if (wasInserted || (s->isLazy() && overrideLazy)) { replaceSymbol(s, name); return s; } @@ -639,7 +639,8 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) { return; } auto *u = dyn_cast(s); - if (!u || u->weakAlias || s->pendingArchiveLoad) + if (!u || (u->weakAlias && !u->isECAlias(ctx.config.machine)) || + s->pendingArchiveLoad) return; s->pendingArchiveLoad = true; f->addMember(sym); @@ -653,7 +654,8 @@ void SymbolTable::addLazyObject(InputFile *f, StringRef n) { return; } auto *u = dyn_cast(s); - if (!u || u->weakAlias || s->pendingArchiveLoad) + if (!u || (u->weakAlias && !u->isECAlias(ctx.config.machine)) || + s->pendingArchiveLoad) return; s->pendingArchiveLoad = true; f->lazy = false; diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h index dab03afde3f987..1d9e908b8b9918 100644 --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -91,7 +91,7 @@ class SymbolTable { Symbol *addSynthetic(StringRef n, Chunk *c); Symbol *addAbsolute(StringRef n, uint64_t va); - Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias); + Symbol *addUndefined(StringRef name, InputFile *f, bool overrideLazy); void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym); void addLazyObject(InputFile *f, StringRef n); void addLazyDLLSymbol(DLLFile *f, DLLFile::Symbol *sym, StringRef n); diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h index ff84ff8ad7b28b..203a542466c68e 100644 --- a/lld/COFF/Symbols.h +++ b/lld/COFF/Symbols.h @@ -353,6 +353,10 @@ class Undefined : public Symbol { isAntiDep = antiDep; } + bool isECAlias(MachineTypes machine) const { + return weakAlias && isAntiDep && isArm64EC(machine); + } + // If this symbol is external weak, replace this object with aliased symbol. bool resolveWeakAlias(); }; diff --git a/lld/test/COFF/arm64ec-lib.test b/lld/test/COFF/arm64ec-lib.test index a26c098547fdbe..617728dac0ab25 100644 --- a/lld/test/COFF/arm64ec-lib.test +++ b/lld/test/COFF/arm64ec-lib.test @@ -7,10 +7,16 @@ RUN: llvm-mc -filetype=obj -triple=aarch64-windows nsymref.s -o nsymref-aarch64. RUN: llvm-mc -filetype=obj -triple=arm64ec-windows sym.s -o sym-arm64ec.obj RUN: llvm-mc -filetype=obj -triple=x86_64-windows sym.s -o sym-x86_64.obj RUN: llvm-mc -filetype=obj -triple=aarch64-windows nsym.s -o nsym-aarch64.obj +RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-alias.s -o ref-alias.obj +RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-thunk.s -o ref-thunk.obj +RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func.s -o func.obj +RUN: llvm-mc -filetype=obj -triple=x86_64-windows func-x86_64.s -o func-x86_64.obj RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj RUN: llvm-lib -machine:arm64ec -out:sym-arm64ec.lib sym-arm64ec.obj nsym-aarch64.obj RUN: llvm-lib -machine:amd64 -out:sym-x86_64.lib sym-x86_64.obj +RUN: llvm-lib -machine:arm64ec -out:func.lib func.obj +RUN: llvm-lib -machine:arm64ec -out:func-x86_64.lib func-x86_64.obj Verify that a symbol can be referenced from ECSYMBOLS. RUN: lld-link -machine:arm64ec -dll -noentry -out:test.dll symref-arm64ec.obj sym-arm64ec.lib loadconfig-arm64ec.obj @@ -26,6 +32,54 @@ RUN: not lld-link -machine:arm64ec -dll -noentry -out:test-err.dll nsymref-arm64 RUN: FileCheck --check-prefix=ERR %s ERR: error: undefined symbol: nsym +Verify that a library symbol can be referenced, even if its name conflicts with an anti-dependency alias. +RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-alias-1.dll ref-alias.obj func.lib loadconfig-arm64ec.obj +RUN: llvm-objdump -d ref-alias-1.dll | FileCheck -check-prefix=DISASM %s +DISASM: 0000000180001000 <.text>: +DISASM-NEXT: 180001000: d65f03c0 ret +DISASM-EMPTY: + +RUN: llvm-readobj --hex-dump=.test ref-alias-1.dll | FileCheck -check-prefix=TESTSEC %s +TESTSEC: 0x180004000 00100000 + +The same test, but with a different input order. +RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-alias-2.dll func.lib ref-alias.obj loadconfig-arm64ec.obj +RUN: llvm-objdump -d ref-alias-2.dll | FileCheck -check-prefix=DISASM %s +RUN: llvm-readobj --hex-dump=.test ref-alias-2.dll | FileCheck -check-prefix=TESTSEC %s + +Verify that when an anti-dependency to a guess exit thunk is present, it is overridden by an archive symbol. +RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-thunk-1.dll ref-thunk.obj func.lib loadconfig-arm64ec.obj +RUN: llvm-objdump -d ref-thunk-1.dll | FileCheck -check-prefix=DISASM %s +RUN: llvm-readobj --hex-dump=.test ref-thunk-1.dll | FileCheck -check-prefix=TESTSEC %s + +The same test, but with a different input order. +RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-thunk-2.dll func.lib ref-thunk.obj loadconfig-arm64ec.obj +RUN: llvm-objdump -d ref-thunk-2.dll | FileCheck -check-prefix=DISASM %s +RUN: llvm-readobj --hex-dump=.test ref-thunk-2.dll | FileCheck -check-prefix=TESTSEC %s + +Test linking against an x86_64 library (which uses a demangled function name). +RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-x86-1.dll ref-thunk.obj func-x86_64.lib loadconfig-arm64ec.obj +RUN: llvm-objdump -d ref-x86-1.dll | FileCheck -check-prefix=DISASM-X86 %s +RUN: llvm-readobj --hex-dump=.test ref-x86-1.dll | FileCheck -check-prefix=TESTSEC %s + +DISASM-X86: 0000000180001000 <.text>: +DISASM-X86-NEXT: 180001000: c3 retq + +The same test, but with a different input order. +RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-x86-2.dll func-x86_64.lib ref-thunk.obj loadconfig-arm64ec.obj +RUN: llvm-objdump -d ref-x86-2.dll | FileCheck -check-prefix=DISASM-X86 %s +RUN: llvm-readobj --hex-dump=.test ref-x86-2.dll | FileCheck -check-prefix=TESTSEC %s + +Check that an alias to the implementation takes precedence over an archive symbol. +RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-x86-2.dll func-x86_64.lib func.obj ref-thunk.obj loadconfig-arm64ec.obj +RUN: llvm-objdump -d ref-x86-2.dll | FileCheck -check-prefix=DISASM %s +RUN: llvm-readobj --hex-dump=.test ref-x86-2.dll | FileCheck -check-prefix=TESTSEC %s + +A similar test using -start-lib for linking. +RUN: lld-link -machine:arm64ec -dll -noentry -out:start-lib-1.dll ref-thunk.obj -start-lib func.obj -end-lib loadconfig-arm64ec.obj +RUN: llvm-objdump -d start-lib-1.dll | FileCheck -check-prefix=DISASM %s +RUN: llvm-readobj --hex-dump=.test start-lib-1.dll | FileCheck -check-prefix=TESTSEC %s + #--- symref.s .data .rva sym @@ -45,3 +99,39 @@ sym: .globl nsym nsym: .word 0 + +#--- ref-alias.s + .weak_anti_dep func +.set func,"#func" + + .section .test, "r" + .rva func + +#--- ref-thunk.s + .weak_anti_dep func +.set func, "#func" + .weak_anti_dep "#func" +.set "#func", thunksym + + .section .test, "r" + .rva func + + .section .thnk,"xr",discard,thunksym +thunksym: + mov w0, #2 + ret + +#--- func.s + .text + .globl "#func" +"#func": + ret + + .weak_anti_dep func +.set func,"#func" + +#--- func-x86_64.s + .text + .globl func +func: + ret