Skip to content

Commit 7c49ab1

Browse files
committed
[LLD][COFF] Use appropriate symbol table for -include argument on ARM64X
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 3b0daff commit 7c49ab1

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 .rectve -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 .rectve -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 .rectve -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)