Skip to content

Commit 0c80919

Browse files
committed
[LLD][COFF] Allow overriding EC alias symbols with lazy archive symbols
On ARM64EC, external functions do not emit undefined symbols. Instead, they emit a pair of weak-dependency aliases: `func` to `#func`, and `#func` to the func guess exit thunk. This change allows such aliases to be overridden by lazy archive symbols, similar to how we handle undefined symbols.
1 parent 8ae39c8 commit 0c80919

File tree

6 files changed

+126
-12
lines changed

6 files changed

+126
-12
lines changed

lld/COFF/InputFiles.cpp

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -455,11 +455,34 @@ void ObjFile::initializeSymbols() {
455455
COFFSymbolRef coffSym = check(coffObj->getSymbol(i));
456456
bool prevailingComdat;
457457
if (coffSym.isUndefined()) {
458-
symbols[i] = createUndefined(coffSym);
458+
symbols[i] = createUndefined(coffSym, false);
459459
} else if (coffSym.isWeakExternal()) {
460-
symbols[i] = createUndefined(coffSym);
461-
weakAliases.emplace_back(symbols[i],
462-
coffSym.getAux<coff_aux_weak_external>());
460+
auto aux = coffSym.getAux<coff_aux_weak_external>();
461+
bool overrideLazy = true;
462+
463+
// On ARM64EC, external functions don't emit undefined symbols. Instead,
464+
// they emit a pair of weak-dependency aliases: func to #func and
465+
// #func to the func guess exit thunk. Allow such aliases to be overridden
466+
// by lazy archive symbols, just as we would for undefined symbols.
467+
if (isArm64EC(getMachineType()) &&
468+
aux->Characteristics == IMAGE_WEAK_EXTERN_ANTI_DEPENDENCY) {
469+
COFFSymbolRef targetSym = check(coffObj->getSymbol(aux->TagIndex));
470+
if (!targetSym.isAnyUndefined()) {
471+
// If the target is defined, it may be either a guess exit thunk or
472+
// the actual implementation. If it's the latter, consider the alias
473+
// to be part of the implementation and override potential lazy
474+
// archive symbols.
475+
StringRef targetName = check(coffObj->getSymbolName(targetSym));
476+
StringRef name = check(coffObj->getSymbolName(coffSym));
477+
std::optional<std::string> mangledName =
478+
getArm64ECMangledFunctionName(name);
479+
overrideLazy = mangledName == targetName;
480+
} else {
481+
overrideLazy = false;
482+
}
483+
}
484+
symbols[i] = createUndefined(coffSym, overrideLazy);
485+
weakAliases.emplace_back(symbols[i], aux);
463486
} else if (std::optional<Symbol *> optSym =
464487
createDefined(coffSym, comdatDefs, prevailingComdat)) {
465488
symbols[i] = *optSym;
@@ -508,9 +531,9 @@ void ObjFile::initializeSymbols() {
508531
decltype(sparseChunks)().swap(sparseChunks);
509532
}
510533

511-
Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
534+
Symbol *ObjFile::createUndefined(COFFSymbolRef sym, bool overrideLazy) {
512535
StringRef name = check(coffObj->getSymbolName(sym));
513-
return ctx.symtab.addUndefined(name, this, sym.isWeakExternal());
536+
return ctx.symtab.addUndefined(name, this, overrideLazy);
514537
}
515538

516539
static const coff_aux_section_definition *findSectionDef(COFFObjectFile *obj,

lld/COFF/InputFiles.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ class ObjFile : public InputFile {
272272
&comdatDefs,
273273
bool &prevailingComdat);
274274
Symbol *createRegular(COFFSymbolRef sym);
275-
Symbol *createUndefined(COFFSymbolRef sym);
275+
Symbol *createUndefined(COFFSymbolRef sym, bool overrideLazy);
276276

277277
std::unique_ptr<COFFObjectFile> coffObj;
278278

lld/COFF/SymbolTable.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -620,9 +620,9 @@ void SymbolTable::initializeECThunks() {
620620
}
621621

622622
Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
623-
bool isWeakAlias) {
623+
bool overrideLazy) {
624624
auto [s, wasInserted] = insert(name, f);
625-
if (wasInserted || (s->isLazy() && isWeakAlias)) {
625+
if (wasInserted || (s->isLazy() && overrideLazy)) {
626626
replaceSymbol<Undefined>(s, name);
627627
return s;
628628
}
@@ -639,7 +639,8 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
639639
return;
640640
}
641641
auto *u = dyn_cast<Undefined>(s);
642-
if (!u || u->weakAlias || s->pendingArchiveLoad)
642+
if (!u || (u->weakAlias && !u->isECAlias(ctx.config.machine)) ||
643+
s->pendingArchiveLoad)
643644
return;
644645
s->pendingArchiveLoad = true;
645646
f->addMember(sym);
@@ -653,7 +654,8 @@ void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
653654
return;
654655
}
655656
auto *u = dyn_cast<Undefined>(s);
656-
if (!u || u->weakAlias || s->pendingArchiveLoad)
657+
if (!u || (u->weakAlias && !u->isECAlias(ctx.config.machine)) ||
658+
s->pendingArchiveLoad)
657659
return;
658660
s->pendingArchiveLoad = true;
659661
f->lazy = false;

lld/COFF/SymbolTable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class SymbolTable {
9191
Symbol *addSynthetic(StringRef n, Chunk *c);
9292
Symbol *addAbsolute(StringRef n, uint64_t va);
9393

94-
Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias);
94+
Symbol *addUndefined(StringRef name, InputFile *f, bool overrideLazy);
9595
void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym);
9696
void addLazyObject(InputFile *f, StringRef n);
9797
void addLazyDLLSymbol(DLLFile *f, DLLFile::Symbol *sym, StringRef n);

lld/COFF/Symbols.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,10 @@ class Undefined : public Symbol {
353353
isAntiDep = antiDep;
354354
}
355355

356+
bool isECAlias(MachineTypes machine) const {
357+
return weakAlias && isAntiDep && isArm64EC(machine);
358+
}
359+
356360
// If this symbol is external weak, replace this object with aliased symbol.
357361
bool resolveWeakAlias();
358362
};

lld/test/COFF/arm64ec-lib.test

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,16 @@ RUN: llvm-mc -filetype=obj -triple=aarch64-windows nsymref.s -o nsymref-aarch64.
77
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows sym.s -o sym-arm64ec.obj
88
RUN: llvm-mc -filetype=obj -triple=x86_64-windows sym.s -o sym-x86_64.obj
99
RUN: llvm-mc -filetype=obj -triple=aarch64-windows nsym.s -o nsym-aarch64.obj
10+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-alias.s -o ref-alias.obj
11+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-thunk.s -o ref-thunk.obj
12+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func.s -o func.obj
13+
RUN: llvm-mc -filetype=obj -triple=x86_64-windows func-x86_64.s -o func-x86_64.obj
1014
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
1115

1216
RUN: llvm-lib -machine:arm64ec -out:sym-arm64ec.lib sym-arm64ec.obj nsym-aarch64.obj
1317
RUN: llvm-lib -machine:amd64 -out:sym-x86_64.lib sym-x86_64.obj
18+
RUN: llvm-lib -machine:arm64ec -out:func.lib func.obj
19+
RUN: llvm-lib -machine:arm64ec -out:func-x86_64.lib func-x86_64.obj
1420

1521
Verify that a symbol can be referenced from ECSYMBOLS.
1622
RUN: lld-link -machine:arm64ec -dll -noentry -out:test.dll symref-arm64ec.obj sym-arm64ec.lib loadconfig-arm64ec.obj
@@ -26,6 +32,49 @@ RUN: not lld-link -machine:arm64ec -dll -noentry -out:test-err.dll nsymref-arm64
2632
RUN: FileCheck --check-prefix=ERR %s
2733
ERR: error: undefined symbol: nsym
2834

35+
Verify that a library symbol can be referenced, even if its name conflicts with an anti-dependency alias.
36+
RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-alias-1.dll ref-alias.obj func.lib loadconfig-arm64ec.obj
37+
RUN: llvm-objdump -d ref-alias-1.dll | FileCheck -check-prefix=DISASM %s
38+
DISASM: 0000000180001000 <.text>:
39+
DISASM-NEXT: 180001000: d65f03c0 ret
40+
DISASM-EMPTY:
41+
42+
RUN: llvm-readobj --hex-dump=.test ref-alias-1.dll | FileCheck -check-prefix=TESTSEC %s
43+
TESTSEC: 0x180004000 00100000
44+
45+
The same test, but with a different input order.
46+
RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-alias-2.dll func.lib ref-alias.obj loadconfig-arm64ec.obj
47+
RUN: llvm-objdump -d ref-alias-2.dll | FileCheck -check-prefix=DISASM %s
48+
RUN: llvm-readobj --hex-dump=.test ref-alias-2.dll | FileCheck -check-prefix=TESTSEC %s
49+
50+
Verify that when an anti-dependency to a guess exit thunk is present, it is overridden by an archive symbol.
51+
RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-thunk-1.dll ref-thunk.obj func.lib loadconfig-arm64ec.obj
52+
RUN: llvm-objdump -d ref-thunk-1.dll | FileCheck -check-prefix=DISASM %s
53+
RUN: llvm-readobj --hex-dump=.test ref-thunk-1.dll | FileCheck -check-prefix=TESTSEC %s
54+
55+
The same test, but with a different input order.
56+
RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-thunk-2.dll func.lib ref-thunk.obj loadconfig-arm64ec.obj
57+
RUN: llvm-objdump -d ref-thunk-2.dll | FileCheck -check-prefix=DISASM %s
58+
RUN: llvm-readobj --hex-dump=.test ref-thunk-2.dll | FileCheck -check-prefix=TESTSEC %s
59+
60+
Test linking against an x86_64 library (which uses a demangled function name).
61+
RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-x86-1.dll ref-thunk.obj func-x86_64.lib loadconfig-arm64ec.obj
62+
RUN: llvm-objdump -d ref-x86-1.dll | FileCheck -check-prefix=DISASM-X86 %s
63+
RUN: llvm-readobj --hex-dump=.test ref-x86-1.dll | FileCheck -check-prefix=TESTSEC %s
64+
65+
DISASM-X86: 0000000180001000 <.text>:
66+
DISASM-X86-NEXT: 180001000: c3 retq
67+
68+
The same test, but with a different input order.
69+
RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-x86-2.dll func-x86_64.lib ref-thunk.obj loadconfig-arm64ec.obj
70+
RUN: llvm-objdump -d ref-x86-2.dll | FileCheck -check-prefix=DISASM-X86 %s
71+
RUN: llvm-readobj --hex-dump=.test ref-x86-2.dll | FileCheck -check-prefix=TESTSEC %s
72+
73+
A similar test using -start-lib for linking.
74+
RUN: lld-link -machine:arm64ec -dll -noentry -out:start-lib-1.dll ref-thunk.obj -start-lib func.obj -end-lib loadconfig-arm64ec.obj
75+
RUN: llvm-objdump -d start-lib-1.dll | FileCheck -check-prefix=DISASM %s
76+
RUN: llvm-readobj --hex-dump=.test start-lib-1.dll | FileCheck -check-prefix=TESTSEC %s
77+
2978
#--- symref.s
3079
.data
3180
.rva sym
@@ -45,3 +94,39 @@ sym:
4594
.globl nsym
4695
nsym:
4796
.word 0
97+
98+
#--- ref-alias.s
99+
.weak_anti_dep func
100+
.set func,"#func"
101+
102+
.section .test, "r"
103+
.rva func
104+
105+
#--- ref-thunk.s
106+
.weak_anti_dep func
107+
.set func, "#func"
108+
.weak_anti_dep "#func"
109+
.set "#func", thunksym
110+
111+
.section .test, "r"
112+
.rva func
113+
114+
.section .thnk,"xr",discard,thunksym
115+
thunksym:
116+
mov w0, #2
117+
ret
118+
119+
#--- func.s
120+
.text
121+
.globl "#func"
122+
"#func":
123+
ret
124+
125+
.weak_anti_dep func
126+
.set func,"#func"
127+
128+
#--- func-x86_64.s
129+
.text
130+
.globl func
131+
func:
132+
ret

0 commit comments

Comments
 (0)