Skip to content

Commit 9b88792

Browse files
authored
[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.
1 parent 2c5208a commit 9b88792

File tree

6 files changed

+132
-12
lines changed

6 files changed

+132
-12
lines changed

lld/COFF/InputFiles.cpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -455,11 +455,35 @@ 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 function calls emit a pair of weak-dependency
464+
// aliases: func to #func and #func to the func guess exit thunk
465+
// (instead of a single undefined func symbol, which would be emitted on
466+
// other targets). Allow such aliases to be overridden by lazy archive
467+
// symbols, just as we would for undefined symbols.
468+
if (isArm64EC(getMachineType()) &&
469+
aux->Characteristics == IMAGE_WEAK_EXTERN_ANTI_DEPENDENCY) {
470+
COFFSymbolRef targetSym = check(coffObj->getSymbol(aux->TagIndex));
471+
if (!targetSym.isAnyUndefined()) {
472+
// If the target is defined, it may be either a guess exit thunk or
473+
// the actual implementation. If it's the latter, consider the alias
474+
// to be part of the implementation and override potential lazy
475+
// archive symbols.
476+
StringRef targetName = check(coffObj->getSymbolName(targetSym));
477+
StringRef name = check(coffObj->getSymbolName(coffSym));
478+
std::optional<std::string> mangledName =
479+
getArm64ECMangledFunctionName(name);
480+
overrideLazy = mangledName == targetName;
481+
} else {
482+
overrideLazy = false;
483+
}
484+
}
485+
symbols[i] = createUndefined(coffSym, overrideLazy);
486+
weakAliases.emplace_back(symbols[i], aux);
463487
} else if (std::optional<Symbol *> optSym =
464488
createDefined(coffSym, comdatDefs, prevailingComdat)) {
465489
symbols[i] = *optSym;
@@ -508,9 +532,9 @@ void ObjFile::initializeSymbols() {
508532
decltype(sparseChunks)().swap(sparseChunks);
509533
}
510534

511-
Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
535+
Symbol *ObjFile::createUndefined(COFFSymbolRef sym, bool overrideLazy) {
512536
StringRef name = check(coffObj->getSymbolName(sym));
513-
return ctx.symtab.addUndefined(name, this, sym.isWeakExternal());
537+
return ctx.symtab.addUndefined(name, this, overrideLazy);
514538
}
515539

516540
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: 90 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,54 @@ 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+
Check that an alias to the implementation takes precedence over an archive symbol.
74+
RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-x86-2.dll func-x86_64.lib func.obj ref-thunk.obj loadconfig-arm64ec.obj
75+
RUN: llvm-objdump -d ref-x86-2.dll | FileCheck -check-prefix=DISASM %s
76+
RUN: llvm-readobj --hex-dump=.test ref-x86-2.dll | FileCheck -check-prefix=TESTSEC %s
77+
78+
A similar test using -start-lib for linking.
79+
RUN: lld-link -machine:arm64ec -dll -noentry -out:start-lib-1.dll ref-thunk.obj -start-lib func.obj -end-lib loadconfig-arm64ec.obj
80+
RUN: llvm-objdump -d start-lib-1.dll | FileCheck -check-prefix=DISASM %s
81+
RUN: llvm-readobj --hex-dump=.test start-lib-1.dll | FileCheck -check-prefix=TESTSEC %s
82+
2983
#--- symref.s
3084
.data
3185
.rva sym
@@ -45,3 +99,39 @@ sym:
4599
.globl nsym
46100
nsym:
47101
.word 0
102+
103+
#--- ref-alias.s
104+
.weak_anti_dep func
105+
.set func,"#func"
106+
107+
.section .test, "r"
108+
.rva func
109+
110+
#--- ref-thunk.s
111+
.weak_anti_dep func
112+
.set func, "#func"
113+
.weak_anti_dep "#func"
114+
.set "#func", thunksym
115+
116+
.section .test, "r"
117+
.rva func
118+
119+
.section .thnk,"xr",discard,thunksym
120+
thunksym:
121+
mov w0, #2
122+
ret
123+
124+
#--- func.s
125+
.text
126+
.globl "#func"
127+
"#func":
128+
ret
129+
130+
.weak_anti_dep func
131+
.set func,"#func"
132+
133+
#--- func-x86_64.s
134+
.text
135+
.globl func
136+
func:
137+
ret

0 commit comments

Comments
 (0)