Skip to content

Commit 251ef3f

Browse files
authored
[LLD][COFF] Use appropriate symbol table for -include argument on ARM64X (#122554)
Move `LinkerDriver::addUndefined` to` SymbolTable` to allow its use with both symbol tables on ARM64X and rename it to `addGCRoot` to clarify its distinct role compared to the existing `SymbolTable::addUndefined`. Command-line `-include` arguments now apply to the EC symbol table, with `mainSymtab` introduced in `linkerMain`. There will be more similar cases. For `.drectve` sections, the corresponding symbol table is used based on the context.
1 parent ab02319 commit 251ef3f

File tree

5 files changed

+117
-47
lines changed

5 files changed

+117
-47
lines changed

lld/COFF/Driver.cpp

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
479479

480480
// Handle /include: in bulk.
481481
for (StringRef inc : directives.includes)
482-
addUndefined(inc);
482+
file->symtab.addGCRoot(inc);
483483

484484
// Handle /exclude-symbols: in bulk.
485485
for (StringRef e : directives.excludes) {
@@ -505,13 +505,13 @@ void LinkerDriver::parseDirectives(InputFile *file) {
505505
case OPT_entry:
506506
if (!arg->getValue()[0])
507507
Fatal(ctx) << "missing entry point symbol name";
508-
ctx.config.entry = addUndefined(mangle(arg->getValue()), true);
508+
ctx.config.entry = file->symtab.addGCRoot(mangle(arg->getValue()), true);
509509
break;
510510
case OPT_failifmismatch:
511511
checkFailIfMismatch(arg->getValue(), file);
512512
break;
513513
case OPT_incl:
514-
addUndefined(arg->getValue());
514+
file->symtab.addGCRoot(arg->getValue());
515515
break;
516516
case OPT_manifestdependency:
517517
ctx.config.manifestDependencies.insert(arg->getValue());
@@ -805,35 +805,6 @@ void LinkerDriver::addLibSearchPaths() {
805805
}
806806
}
807807

808-
Symbol *LinkerDriver::addUndefined(StringRef name, bool aliasEC) {
809-
Symbol *b = ctx.symtab.addUndefined(name);
810-
if (!b->isGCRoot) {
811-
b->isGCRoot = true;
812-
ctx.config.gcroot.push_back(b);
813-
}
814-
815-
// On ARM64EC, a symbol may be defined in either its mangled or demangled form
816-
// (or both). Define an anti-dependency symbol that binds both forms, similar
817-
// to how compiler-generated code references external functions.
818-
if (aliasEC && isArm64EC(ctx.config.machine)) {
819-
if (std::optional<std::string> mangledName =
820-
getArm64ECMangledFunctionName(name)) {
821-
auto u = dyn_cast<Undefined>(b);
822-
if (u && !u->weakAlias) {
823-
Symbol *t = ctx.symtab.addUndefined(saver().save(*mangledName));
824-
u->setWeakAlias(t, true);
825-
}
826-
} else if (std::optional<std::string> demangledName =
827-
getArm64ECDemangledFunctionName(name)) {
828-
Symbol *us = ctx.symtab.addUndefined(saver().save(*demangledName));
829-
auto u = dyn_cast<Undefined>(us);
830-
if (u && !u->weakAlias)
831-
u->setWeakAlias(b, true);
832-
}
833-
}
834-
return b;
835-
}
836-
837808
void LinkerDriver::addUndefinedGlob(StringRef arg) {
838809
Expected<GlobPattern> pat = GlobPattern::create(arg);
839810
if (!pat) {
@@ -849,7 +820,7 @@ void LinkerDriver::addUndefinedGlob(StringRef arg) {
849820
});
850821

851822
for (Symbol *sym : syms)
852-
addUndefined(sym->getName());
823+
ctx.symtab.addGCRoot(sym->getName());
853824
}
854825

855826
StringRef LinkerDriver::mangleMaybe(Symbol *s) {
@@ -1487,7 +1458,7 @@ void LinkerDriver::maybeCreateECExportThunk(StringRef name, Symbol *&sym) {
14871458
expName = saver().save("EXP+" + *mangledName);
14881459
else
14891460
expName = saver().save("EXP+" + name);
1490-
sym = addUndefined(expName);
1461+
sym = ctx.symtabEC->addGCRoot(expName);
14911462
if (auto undef = dyn_cast<Undefined>(sym)) {
14921463
if (!undef->getWeakAlias()) {
14931464
auto thunk = make<ECExportThunkChunk>(def);
@@ -1537,7 +1508,8 @@ void LinkerDriver::createECExportThunks() {
15371508

15381509
void LinkerDriver::pullArm64ECIcallHelper() {
15391510
if (!ctx.config.arm64ECIcallHelper)
1540-
ctx.config.arm64ECIcallHelper = addUndefined("__icall_helper_arm64ec");
1511+
ctx.config.arm64ECIcallHelper =
1512+
ctx.symtabEC->addGCRoot("__icall_helper_arm64ec");
15411513
}
15421514

15431515
// In MinGW, if no symbols are chosen to be exported, then all symbols are
@@ -1976,6 +1948,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
19761948
setMachine(machine);
19771949
}
19781950
}
1951+
SymbolTable &mainSymtab = ctx.hybridSymtab ? *ctx.hybridSymtab : ctx.symtab;
19791952

19801953
// Handle /nodefaultlib:<filename>
19811954
{
@@ -2062,7 +2035,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
20622035

20632036
// Handle /include
20642037
for (auto *arg : args.filtered(OPT_incl))
2065-
addUndefined(arg->getValue());
2038+
mainSymtab.addGCRoot(arg->getValue());
20662039

20672040
// Handle /implib
20682041
if (auto *arg = args.getLastArg(OPT_implib))
@@ -2493,22 +2466,22 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
24932466
if (auto *arg = args.getLastArg(OPT_entry)) {
24942467
if (!arg->getValue()[0])
24952468
Fatal(ctx) << "missing entry point symbol name";
2496-
config->entry = addUndefined(mangle(arg->getValue()), true);
2469+
config->entry = ctx.symtab.addGCRoot(mangle(arg->getValue()), true);
24972470
} else if (!config->entry && !config->noEntry) {
24982471
if (args.hasArg(OPT_dll)) {
24992472
StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12"
25002473
: "_DllMainCRTStartup";
2501-
config->entry = addUndefined(s, true);
2474+
config->entry = ctx.symtab.addGCRoot(s, true);
25022475
} else if (config->driverWdm) {
25032476
// /driver:wdm implies /entry:_NtProcessStartup
2504-
config->entry = addUndefined(mangle("_NtProcessStartup"), true);
2477+
config->entry = ctx.symtab.addGCRoot(mangle("_NtProcessStartup"), true);
25052478
} else {
25062479
// Windows specific -- If entry point name is not given, we need to
25072480
// infer that from user-defined entry name.
25082481
StringRef s = findDefaultEntry();
25092482
if (s.empty())
25102483
Fatal(ctx) << "entry point must be defined";
2511-
config->entry = addUndefined(s, true);
2484+
config->entry = ctx.symtab.addGCRoot(s, true);
25122485
Log(ctx) << "Entry name inferred: " << s;
25132486
}
25142487
}
@@ -2520,9 +2493,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
25202493
for (auto *arg : args.filtered(OPT_delayload)) {
25212494
config->delayLoads.insert(StringRef(arg->getValue()).lower());
25222495
if (config->machine == I386) {
2523-
config->delayLoadHelper = addUndefined("___delayLoadHelper2@8");
2496+
config->delayLoadHelper = ctx.symtab.addGCRoot("___delayLoadHelper2@8");
25242497
} else {
2525-
config->delayLoadHelper = addUndefined("__delayLoadHelper2", true);
2498+
config->delayLoadHelper =
2499+
ctx.symtab.addGCRoot("__delayLoadHelper2", true);
25262500
}
25272501
}
25282502
}
@@ -2659,7 +2633,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
26592633
for (Export &e : config->exports) {
26602634
if (!e.forwardTo.empty())
26612635
continue;
2662-
e.sym = addUndefined(e.name, !e.data);
2636+
e.sym = ctx.symtab.addGCRoot(e.name, !e.data);
26632637
if (e.source != ExportSource::Directives)
26642638
e.symbolName = mangleMaybe(e.sym);
26652639
}
@@ -2701,13 +2675,13 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
27012675

27022676
// Windows specific -- if __load_config_used can be resolved, resolve it.
27032677
if (ctx.symtab.findUnderscore("_load_config_used"))
2704-
addUndefined(mangle("_load_config_used"));
2678+
ctx.symtab.addGCRoot(mangle("_load_config_used"));
27052679

27062680
if (args.hasArg(OPT_include_optional)) {
27072681
// Handle /includeoptional
27082682
for (auto *arg : args.filtered(OPT_include_optional))
27092683
if (isa_and_nonnull<LazyArchive>(ctx.symtab.find(arg->getValue())))
2710-
addUndefined(arg->getValue());
2684+
ctx.symtab.addGCRoot(arg->getValue());
27112685
}
27122686
} while (run());
27132687
}

lld/COFF/Driver.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,6 @@ class LinkerDriver {
173173

174174
std::set<std::string> visitedLibs;
175175

176-
Symbol *addUndefined(StringRef sym, bool aliasEC = false);
177-
178176
void addUndefinedGlob(StringRef arg);
179177

180178
StringRef mangleMaybe(Symbol *s);

lld/COFF/SymbolTable.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,35 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
651651
return s;
652652
}
653653

654+
Symbol *SymbolTable::addGCRoot(StringRef name, bool aliasEC) {
655+
Symbol *b = addUndefined(name);
656+
if (!b->isGCRoot) {
657+
b->isGCRoot = true;
658+
ctx.config.gcroot.push_back(b);
659+
}
660+
661+
// On ARM64EC, a symbol may be defined in either its mangled or demangled form
662+
// (or both). Define an anti-dependency symbol that binds both forms, similar
663+
// to how compiler-generated code references external functions.
664+
if (aliasEC && isEC()) {
665+
if (std::optional<std::string> mangledName =
666+
getArm64ECMangledFunctionName(name)) {
667+
auto u = dyn_cast<Undefined>(b);
668+
if (u && !u->weakAlias) {
669+
Symbol *t = addUndefined(saver().save(*mangledName));
670+
u->setWeakAlias(t, true);
671+
}
672+
} else if (std::optional<std::string> demangledName =
673+
getArm64ECDemangledFunctionName(name)) {
674+
Symbol *us = addUndefined(saver().save(*demangledName));
675+
auto u = dyn_cast<Undefined>(us);
676+
if (u && !u->weakAlias)
677+
u->setWeakAlias(b, true);
678+
}
679+
}
680+
return b;
681+
}
682+
654683
// On ARM64EC, a function symbol may appear in both mangled and demangled forms:
655684
// - ARM64EC archives contain only the mangled name, while the demangled symbol
656685
// is defined by the object file as an alias.

lld/COFF/SymbolTable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ class SymbolTable {
8585
// added and before the writer writes results to a file.
8686
void compileBitcodeFiles();
8787

88+
// Creates an Undefined symbol and marks it as live.
89+
Symbol *addGCRoot(StringRef sym, bool aliasEC = false);
90+
8891
// Creates an Undefined symbol for a given name.
8992
Symbol *addUndefined(StringRef name);
9093

lld/test/COFF/arm64x-incl.s

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// REQUIRES: aarch64
2+
// RUN: split-file %s %t.dir && cd %t.dir
3+
4+
// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows sym-arm64ec.s -o sym-arm64ec.obj
5+
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows sym-aarch64.s -o sym-aarch64.obj
6+
// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows drectve.s -o drectve-arm64ec.obj
7+
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows drectve.s -o drectve-aarch64.obj
8+
// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
9+
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64.s -o loadconfig-arm64.obj
10+
// RUN: llvm-lib -machine:arm64x -out:sym.lib sym-arm64ec.obj sym-aarch64.obj
11+
12+
// Check that the command-line -include argument ensures the EC symbol is included.
13+
14+
// RUN: lld-link -machine:arm64x -out:out-arg.dll -dll -noentry loadconfig-arm64.obj loadconfig-arm64ec.obj sym.lib -include:sym
15+
// RUN: llvm-readobj --hex-dump=.test out-arg.dll | FileCheck --check-prefix=EC %s
16+
// EC: 0x180004000 02000000 ....
17+
18+
// Check that the native .drectve -include argument ensures the native symbol is included.
19+
20+
// RUN: lld-link -machine:arm64x -out:out-native.dll -dll -noentry loadconfig-arm64.obj loadconfig-arm64ec.obj sym.lib drectve-aarch64.obj
21+
// RUN: llvm-readobj --hex-dump=.test out-native.dll | FileCheck --check-prefix=NATIVE %s
22+
// NATIVE: 0x180004000 01000000 ....
23+
24+
// Check that the EC .drectve -include argument ensures the EC symbol is included.
25+
26+
// RUN: lld-link -machine:arm64x -out:out-ec.dll -dll -noentry loadconfig-arm64.obj loadconfig-arm64ec.obj sym.lib drectve-arm64ec.obj
27+
// RUN: llvm-readobj --hex-dump=.test out-ec.dll | FileCheck --check-prefix=EC %s
28+
29+
// Check that both native and EC .drectve -include arguments ensure both symbols are included.
30+
31+
// RUN: lld-link -machine:arm64x -out:out-arg-native.dll -dll -noentry loadconfig-arm64.obj loadconfig-arm64ec.obj sym.lib \
32+
// RUN: -include:sym drectve-aarch64.obj
33+
// RUN: llvm-readobj --hex-dump=.test out-arg-native.dll | FileCheck --check-prefix=BOTH %s
34+
// BOTH: 0x180004000 02000000 01000000 ........
35+
36+
// RUN: lld-link -machine:arm64x -out:out-both.dll -dll -noentry loadconfig-arm64.obj loadconfig-arm64ec.obj sym.lib \
37+
// RUN: drectve-arm64ec.obj drectve-aarch64.obj
38+
// RUN: llvm-readobj --hex-dump=.test out-both.dll | FileCheck --check-prefix=BOTH %s
39+
40+
// Check that including a missing symbol results in an error.
41+
42+
// RUN: not lld-link -machine:arm64x -out:err.dll -dll -noentry loadconfig-arm64.obj loadconfig-arm64ec.obj -include:sym sym-aarch64.obj \
43+
// RUN: 2>&1 | FileCheck --check-prefix=ERR %s
44+
// ERR: lld-link: error: <root>: undefined symbol: sym
45+
46+
// RUN: not lld-link -machine:arm64x -out:err.dll -dll -noentry loadconfig-arm64.obj loadconfig-arm64ec.obj drectve-arm64ec.obj sym-aarch64.obj \
47+
// RUN: 2>&1 | FileCheck --check-prefix=ERR %s
48+
49+
// RUN: not lld-link -machine:arm64x -out:err.dll -dll -noentry loadconfig-arm64.obj loadconfig-arm64ec.obj drectve-aarch64.obj sym-arm64ec.obj \
50+
// RUN: 2>&1 | FileCheck --check-prefix=ERR %s
51+
52+
#--- sym-aarch64.s
53+
.section ".test","dr"
54+
.globl sym
55+
sym:
56+
.word 1
57+
58+
#--- sym-arm64ec.s
59+
.section ".test","dr"
60+
.globl sym
61+
sym:
62+
.word 2
63+
64+
#--- drectve.s
65+
.section .drectve, "yn"
66+
.ascii " -include:sym"

0 commit comments

Comments
 (0)