Skip to content

[LLD][COFF] Allow overriding EC alias symbols with lazy archive symbols #113283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading