Skip to content

Commit 5d0ca88

Browse files
committed
[LLD][COFF] Add support for delay-load imports on ARM64X
For each imported module, emit null-terminated native import entries, followed by null-terminated EC entries. If a view lacks imports for a given module, only terminators are emitted. Use ARM64X relocations to skip native entries in the EC view. Move delayLoadHelper and tailMergeUnwindInfoChunk to SymbolTable since they are different for each symbol table.
1 parent bd38c49 commit 5d0ca88

File tree

7 files changed

+470
-79
lines changed

7 files changed

+470
-79
lines changed

lld/COFF/Config.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ struct Configuration {
164164
bool noimplib = false;
165165
std::set<std::string> delayLoads;
166166
std::map<std::string, int> dllOrder;
167-
Symbol *delayLoadHelper = nullptr;
168167
Symbol *arm64ECIcallHelper = nullptr;
169168

170169
llvm::DenseSet<llvm::StringRef> saveTempsArgs;

lld/COFF/DLL.cpp

Lines changed: 93 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -911,67 +911,99 @@ uint64_t DelayLoadContents::getDirSize() {
911911
return dirs.size() * sizeof(delay_import_directory_table_entry);
912912
}
913913

914-
void DelayLoadContents::create(Defined *h) {
915-
helper = h;
914+
void DelayLoadContents::create() {
916915
std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
917916

918-
Chunk *unwind = newTailMergeUnwindInfoChunk();
919-
920917
// Create .didat contents for each DLL.
921918
for (std::vector<DefinedImportData *> &syms : v) {
922919
// Create the delay import table header.
923920
dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));
924921
auto *dir = make<DelayDirectoryChunk>(dllNames.back());
925922

926923
size_t base = addresses.size();
927-
Chunk *tm = newTailMergeChunk(dir);
928-
Chunk *pdataChunk = unwind ? newTailMergePDataChunk(tm, unwind) : nullptr;
929-
for (DefinedImportData *s : syms) {
930-
Chunk *t = newThunkChunk(s, tm);
931-
auto *a = make<DelayAddressChunk>(ctx, t);
932-
addresses.push_back(a);
933-
thunks.push_back(t);
934-
StringRef extName = s->getExternalName();
935-
if (extName.empty()) {
936-
names.push_back(make<OrdinalOnlyChunk>(ctx, s->getOrdinal()));
937-
} else {
938-
auto *c = make<HintNameChunk>(extName, 0);
939-
names.push_back(make<LookupChunk>(ctx, c));
940-
hintNames.push_back(c);
941-
// Add a synthetic symbol for this load thunk, using the "__imp___load"
942-
// prefix, in case this thunk needs to be added to the list of valid
943-
// call targets for Control Flow Guard.
944-
StringRef symName = saver().save("__imp___load_" + extName);
945-
s->loadThunkSym =
946-
cast<DefinedSynthetic>(ctx.symtab.addSynthetic(symName, t));
924+
ctx.forEachSymtab([&](SymbolTable &symtab) {
925+
if (ctx.hybridSymtab && symtab.isEC()) {
926+
// For hybrid images, emit null-terminated native import entries
927+
// followed by null-terminated EC entries. If a view is missing imports
928+
// for a given module, only terminators are emitted. Emit ARM64X
929+
// relocations to skip native entries in the EC view.
930+
ctx.dynamicRelocs->add(
931+
IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
932+
Arm64XRelocVal(dir, offsetof(delay_import_directory_table_entry,
933+
DelayImportAddressTable)),
934+
(addresses.size() - base) * sizeof(uint64_t));
935+
ctx.dynamicRelocs->add(
936+
IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
937+
Arm64XRelocVal(dir, offsetof(delay_import_directory_table_entry,
938+
DelayImportNameTable)),
939+
(addresses.size() - base) * sizeof(uint64_t));
947940
}
948941

949-
if (s->file->impECSym) {
950-
auto chunk = make<AuxImportChunk>(s->file);
951-
auxIat.push_back(chunk);
952-
s->file->impECSym->setLocation(chunk);
942+
Chunk *tm = nullptr;
953943

954-
chunk = make<AuxImportChunk>(s->file);
955-
auxIatCopy.push_back(chunk);
956-
s->file->auxImpCopySym->setLocation(chunk);
944+
for (DefinedImportData *s : syms) {
945+
// Process only the symbols belonging to the current symtab.
946+
if (symtab.isEC() != s->file->isEC())
947+
continue;
948+
949+
if (!tm) {
950+
tm = newTailMergeChunk(symtab, dir);
951+
Chunk *pdataChunk = newTailMergePDataChunk(symtab, tm);
952+
if (pdataChunk)
953+
pdata.push_back(pdataChunk);
954+
}
955+
956+
Chunk *t = newThunkChunk(s, tm);
957+
auto *a = make<DelayAddressChunk>(ctx, t);
958+
addresses.push_back(a);
959+
s->setLocation(a);
960+
thunks.push_back(t);
961+
StringRef extName = s->getExternalName();
962+
if (extName.empty()) {
963+
names.push_back(make<OrdinalOnlyChunk>(ctx, s->getOrdinal()));
964+
} else {
965+
auto *c = make<HintNameChunk>(extName, 0);
966+
names.push_back(make<LookupChunk>(ctx, c));
967+
hintNames.push_back(c);
968+
// Add a synthetic symbol for this load thunk, using the
969+
// "__imp___load" prefix, in case this thunk needs to be added to the
970+
// list of valid call targets for Control Flow Guard.
971+
StringRef symName = saver().save("__imp___load_" + extName);
972+
s->loadThunkSym =
973+
cast<DefinedSynthetic>(symtab.addSynthetic(symName, t));
974+
}
975+
976+
if (symtab.isEC()) {
977+
auto chunk = make<AuxImportChunk>(s->file);
978+
auxIat.push_back(chunk);
979+
s->file->impECSym->setLocation(chunk);
980+
981+
chunk = make<AuxImportChunk>(s->file);
982+
auxIatCopy.push_back(chunk);
983+
s->file->auxImpCopySym->setLocation(chunk);
984+
} else if (ctx.hybridSymtab) {
985+
// Fill the auxiliary IAT with null chunks for native imports.
986+
auxIat.push_back(make<NullChunk>(ctx));
987+
auxIatCopy.push_back(make<NullChunk>(ctx));
988+
}
957989
}
958-
}
959-
thunks.push_back(tm);
960-
if (pdataChunk)
961-
pdata.push_back(pdataChunk);
962-
StringRef tmName =
963-
saver().save("__tailMerge_" + syms[0]->getDLLName().lower());
964-
ctx.symtab.addSynthetic(tmName, tm);
965-
// Terminate with null values.
966-
addresses.push_back(make<NullChunk>(ctx, 8));
967-
names.push_back(make<NullChunk>(ctx, 8));
968-
if (ctx.config.machine == ARM64EC) {
969-
auxIat.push_back(make<NullChunk>(ctx, 8));
970-
auxIatCopy.push_back(make<NullChunk>(ctx, 8));
971-
}
972990

973-
for (int i = 0, e = syms.size(); i < e; ++i)
974-
syms[i]->setLocation(addresses[base + i]);
991+
if (tm) {
992+
thunks.push_back(tm);
993+
StringRef tmName =
994+
saver().save("__tailMerge_" + syms[0]->getDLLName().lower());
995+
symtab.addSynthetic(tmName, tm);
996+
}
997+
998+
// Terminate with null values.
999+
addresses.push_back(make<NullChunk>(ctx, 8));
1000+
names.push_back(make<NullChunk>(ctx, 8));
1001+
if (ctx.symtabEC) {
1002+
auxIat.push_back(make<NullChunk>(ctx, 8));
1003+
auxIatCopy.push_back(make<NullChunk>(ctx, 8));
1004+
}
1005+
});
1006+
9751007
auto *mh = make<NullChunk>(8, 8);
9761008
moduleHandles.push_back(mh);
9771009

@@ -982,15 +1014,18 @@ void DelayLoadContents::create(Defined *h) {
9821014
dirs.push_back(dir);
9831015
}
9841016

985-
if (unwind)
986-
unwindinfo.push_back(unwind);
1017+
ctx.forEachSymtab([&](SymbolTable &symtab) {
1018+
if (symtab.tailMergeUnwindInfoChunk)
1019+
unwindinfo.push_back(symtab.tailMergeUnwindInfoChunk);
1020+
});
9871021
// Add null terminator.
9881022
dirs.push_back(
9891023
make<NullChunk>(sizeof(delay_import_directory_table_entry), 4));
9901024
}
9911025

992-
Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
993-
switch (ctx.config.machine) {
1026+
Chunk *DelayLoadContents::newTailMergeChunk(SymbolTable &symtab, Chunk *dir) {
1027+
auto helper = cast<Defined>(symtab.delayLoadHelper);
1028+
switch (symtab.machine) {
9941029
case AMD64:
9951030
case ARM64EC:
9961031
return make<TailMergeChunkX64>(dir, helper);
@@ -1005,21 +1040,14 @@ Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
10051040
}
10061041
}
10071042

1008-
Chunk *DelayLoadContents::newTailMergeUnwindInfoChunk() {
1009-
switch (ctx.config.machine) {
1010-
case AMD64:
1011-
case ARM64EC:
1012-
return make<TailMergeUnwindInfoX64>();
1013-
// FIXME: Add support for other architectures.
1014-
default:
1015-
return nullptr; // Just don't generate unwind info.
1016-
}
1017-
}
1018-
Chunk *DelayLoadContents::newTailMergePDataChunk(Chunk *tm, Chunk *unwind) {
1019-
switch (ctx.config.machine) {
1043+
Chunk *DelayLoadContents::newTailMergePDataChunk(SymbolTable &symtab,
1044+
Chunk *tm) {
1045+
switch (symtab.machine) {
10201046
case AMD64:
10211047
case ARM64EC:
1022-
return make<TailMergePDataChunkX64>(tm, unwind);
1048+
if (!symtab.tailMergeUnwindInfoChunk)
1049+
symtab.tailMergeUnwindInfoChunk = make<TailMergeUnwindInfoX64>();
1050+
return make<TailMergePDataChunkX64>(tm, symtab.tailMergeUnwindInfoChunk);
10231051
// FIXME: Add support for other architectures.
10241052
default:
10251053
return nullptr; // Just don't generate unwind info.
@@ -1028,7 +1056,7 @@ Chunk *DelayLoadContents::newTailMergePDataChunk(Chunk *tm, Chunk *unwind) {
10281056

10291057
Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
10301058
Chunk *tailMerge) {
1031-
switch (ctx.config.machine) {
1059+
switch (s->file->getMachineType()) {
10321060
case AMD64:
10331061
case ARM64EC:
10341062
return make<ThunkChunkX64>(s, tailMerge);

lld/COFF/DLL.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class DelayLoadContents {
4242
DelayLoadContents(COFFLinkerContext &ctx) : ctx(ctx) {}
4343
void add(DefinedImportData *sym) { imports.push_back(sym); }
4444
bool empty() { return imports.empty(); }
45-
void create(Defined *helper);
45+
void create();
4646
std::vector<Chunk *> getChunks();
4747
std::vector<Chunk *> getDataChunks();
4848
ArrayRef<Chunk *> getCodeChunks() { return thunks; }
@@ -56,11 +56,9 @@ class DelayLoadContents {
5656

5757
private:
5858
Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge);
59-
Chunk *newTailMergeChunk(Chunk *dir);
60-
Chunk *newTailMergePDataChunk(Chunk *tm, Chunk *unwind);
61-
Chunk *newTailMergeUnwindInfoChunk();
59+
Chunk *newTailMergeChunk(SymbolTable &symtab, Chunk *dir);
60+
Chunk *newTailMergePDataChunk(SymbolTable &symtab, Chunk *tm);
6261

63-
Defined *helper;
6462
std::vector<DefinedImportData *> imports;
6563
std::vector<Chunk *> dirs;
6664
std::vector<Chunk *> moduleHandles;

lld/COFF/Driver.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2353,12 +2353,13 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
23532353
llvm::TimeTraceScope timeScope("Delay load");
23542354
for (auto *arg : args.filtered(OPT_delayload)) {
23552355
config->delayLoads.insert(StringRef(arg->getValue()).lower());
2356-
if (config->machine == I386) {
2357-
config->delayLoadHelper = ctx.symtab.addGCRoot("___delayLoadHelper2@8");
2358-
} else {
2359-
config->delayLoadHelper =
2360-
ctx.symtab.addGCRoot("__delayLoadHelper2", true);
2361-
}
2356+
ctx.forEachSymtab([&](SymbolTable &symtab) {
2357+
if (symtab.machine == I386) {
2358+
symtab.delayLoadHelper = symtab.addGCRoot("___delayLoadHelper2@8");
2359+
} else {
2360+
symtab.delayLoadHelper = symtab.addGCRoot("__delayLoadHelper2", true);
2361+
}
2362+
});
23622363
}
23632364
}
23642365

lld/COFF/SymbolTable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ class SymbolTable {
158158
Chunk *edataStart = nullptr;
159159
Chunk *edataEnd = nullptr;
160160

161+
Symbol *delayLoadHelper = nullptr;
162+
Chunk *tailMergeUnwindInfoChunk = nullptr;
163+
161164
void fixupExports();
162165
void assignExportOrdinals();
163166
void parseModuleDefs(StringRef path);

lld/COFF/Writer.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,8 +1307,7 @@ void Writer::appendImportThunks() {
13071307
}
13081308

13091309
if (!delayIdata.empty()) {
1310-
Defined *helper = cast<Defined>(ctx.config.delayLoadHelper);
1311-
delayIdata.create(helper);
1310+
delayIdata.create();
13121311
for (Chunk *c : delayIdata.getChunks())
13131312
didatSec->addChunk(c);
13141313
for (Chunk *c : delayIdata.getDataChunks())

0 commit comments

Comments
 (0)