Skip to content

Commit

Permalink
[LLD][COFF] Allow overriding EC alias symbols with lazy archive symbo…
Browse files Browse the repository at this point in the history
…ls (llvm#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.
  • Loading branch information
cjacek authored Oct 23, 2024
1 parent 2c5208a commit 9b88792
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 12 deletions.
36 changes: 30 additions & 6 deletions lld/COFF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<coff_aux_weak_external>());
auto aux = coffSym.getAux<coff_aux_weak_external>();
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<std::string> mangledName =
getArm64ECMangledFunctionName(name);
overrideLazy = mangledName == targetName;
} else {
overrideLazy = false;
}
}
symbols[i] = createUndefined(coffSym, overrideLazy);
weakAliases.emplace_back(symbols[i], aux);
} else if (std::optional<Symbol *> optSym =
createDefined(coffSym, comdatDefs, prevailingComdat)) {
symbols[i] = *optSym;
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion lld/COFF/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<COFFObjectFile> coffObj;

Expand Down
10 changes: 6 additions & 4 deletions lld/COFF/SymbolTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Undefined>(s, name);
return s;
}
Expand All @@ -639,7 +639,8 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
return;
}
auto *u = dyn_cast<Undefined>(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);
Expand All @@ -653,7 +654,8 @@ void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
return;
}
auto *u = dyn_cast<Undefined>(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;
Expand Down
2 changes: 1 addition & 1 deletion lld/COFF/SymbolTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions lld/COFF/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
};
Expand Down
90 changes: 90 additions & 0 deletions lld/test/COFF/arm64ec-lib.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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

0 comments on commit 9b88792

Please sign in to comment.