Skip to content

Commit c512eda

Browse files
[lld][COFF] Provide unwinding information for Chunk injected by /delayloaded
For each symbol in a /delayloaded library, lld injects a small piece of code to handle the symbol lazy loading. This code doesn't have unwind information, which may be troublesome. Provide these information for AMD64. Thanks to Yannis Juglaret <[email protected]> for contributing the unwinding info and for his support while crafting this patch. Fix #59639 Differential Revision: https://reviews.llvm.org/D141691
1 parent 48e862d commit c512eda

File tree

6 files changed

+117
-7
lines changed

6 files changed

+117
-7
lines changed

lld/COFF/DLL.cpp

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,19 @@ static const uint8_t tailMergeX64[] = {
229229
0xFF, 0xE0, // jmp rax
230230
};
231231

232+
static const uint8_t tailMergeUnwindInfoX64[] = {
233+
0x01, // Version=1, Flags=UNW_FLAG_NHANDLER
234+
0x0a, // Size of prolog
235+
0x05, // Count of unwind codes
236+
0x00, // No frame register
237+
0x0a, 0x82, // Offset 0xa: UWOP_ALLOC_SMALL(0x48)
238+
0x06, 0x02, // Offset 6: UWOP_ALLOC_SMALL(8)
239+
0x04, 0x02, // Offset 4: UWOP_ALLOC_SMALL(8)
240+
0x02, 0x02, // Offset 2: UWOP_ALLOC_SMALL(8)
241+
0x01, 0x02, // Offset 1: UWOP_ALLOC_SMALL(8)
242+
0x00, 0x00 // Padding to align on 32-bits
243+
};
244+
232245
static const uint8_t thunkX86[] = {
233246
0xB8, 0, 0, 0, 0, // mov eax, offset ___imp__<FUNCNAME>
234247
0xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib>
@@ -332,6 +345,41 @@ class TailMergeChunkX64 : public NonSectionChunk {
332345
Defined *helper = nullptr;
333346
};
334347

348+
class TailMergePDataChunkX64 : public NonSectionChunk {
349+
public:
350+
TailMergePDataChunkX64(Chunk *tm, Chunk *unwind) : tm(tm), unwind(unwind) {
351+
// See
352+
// https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function
353+
setAlignment(4);
354+
}
355+
356+
size_t getSize() const override { return 3 * sizeof(uint32_t); }
357+
358+
void writeTo(uint8_t *buf) const override {
359+
write32le(buf + 0, tm->getRVA()); // TailMergeChunk start RVA
360+
write32le(buf + 4, tm->getRVA() + tm->getSize()); // TailMergeChunk stop RVA
361+
write32le(buf + 8, unwind->getRVA()); // UnwindInfo RVA
362+
}
363+
364+
Chunk *tm = nullptr;
365+
Chunk *unwind = nullptr;
366+
};
367+
368+
class TailMergeUnwindInfoX64 : public NonSectionChunk {
369+
public:
370+
TailMergeUnwindInfoX64() {
371+
// See
372+
// https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info
373+
setAlignment(4);
374+
}
375+
376+
size_t getSize() const override { return sizeof(tailMergeUnwindInfoX64); }
377+
378+
void writeTo(uint8_t *buf) const override {
379+
memcpy(buf, tailMergeUnwindInfoX64, sizeof(tailMergeUnwindInfoX64));
380+
}
381+
};
382+
335383
class ThunkChunkX86 : public NonSectionChunk {
336384
public:
337385
ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
@@ -672,6 +720,8 @@ void DelayLoadContents::create(Defined *h) {
672720
helper = h;
673721
std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
674722

723+
Chunk *unwind = newTailMergeUnwindInfoChunk();
724+
675725
// Create .didat contents for each DLL.
676726
for (std::vector<DefinedImportData *> &syms : v) {
677727
// Create the delay import table header.
@@ -680,6 +730,7 @@ void DelayLoadContents::create(Defined *h) {
680730

681731
size_t base = addresses.size();
682732
Chunk *tm = newTailMergeChunk(dir);
733+
Chunk *pdataChunk = unwind ? newTailMergePDataChunk(tm, unwind) : nullptr;
683734
for (DefinedImportData *s : syms) {
684735
Chunk *t = newThunkChunk(s, tm);
685736
auto *a = make<DelayAddressChunk>(ctx, t);
@@ -692,7 +743,7 @@ void DelayLoadContents::create(Defined *h) {
692743
auto *c = make<HintNameChunk>(extName, 0);
693744
names.push_back(make<LookupChunk>(ctx, c));
694745
hintNames.push_back(c);
695-
// Add a syntentic symbol for this load thunk, using the "__imp___load"
746+
// Add a synthetic symbol for this load thunk, using the "__imp___load"
696747
// prefix, in case this thunk needs to be added to the list of valid
697748
// call targets for Control Flow Guard.
698749
StringRef symName = saver().save("__imp___load_" + extName);
@@ -701,6 +752,8 @@ void DelayLoadContents::create(Defined *h) {
701752
}
702753
}
703754
thunks.push_back(tm);
755+
if (pdataChunk)
756+
pdata.push_back(pdataChunk);
704757
StringRef tmName =
705758
saver().save("__tailMerge_" + syms[0]->getDLLName().lower());
706759
ctx.symtab.addSynthetic(tmName, tm);
@@ -720,6 +773,9 @@ void DelayLoadContents::create(Defined *h) {
720773
dir->nameTab = names[base];
721774
dirs.push_back(dir);
722775
}
776+
777+
if (unwind)
778+
unwindinfo.push_back(unwind);
723779
// Add null terminator.
724780
dirs.push_back(make<NullChunk>(sizeof(delay_import_directory_table_entry)));
725781
}
@@ -739,6 +795,25 @@ Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
739795
}
740796
}
741797

798+
Chunk *DelayLoadContents::newTailMergeUnwindInfoChunk() {
799+
switch (ctx.config.machine) {
800+
case AMD64:
801+
return make<TailMergeUnwindInfoX64>();
802+
// FIXME: Add support for other architectures.
803+
default:
804+
return nullptr; // Just don't generate unwind info.
805+
}
806+
}
807+
Chunk *DelayLoadContents::newTailMergePDataChunk(Chunk *tm, Chunk *unwind) {
808+
switch (ctx.config.machine) {
809+
case AMD64:
810+
return make<TailMergePDataChunkX64>(tm, unwind);
811+
// FIXME: Add support for other architectures.
812+
default:
813+
return nullptr; // Just don't generate unwind info.
814+
}
815+
}
816+
742817
Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
743818
Chunk *tailMerge) {
744819
switch (ctx.config.machine) {

lld/COFF/DLL.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,17 @@ class DelayLoadContents {
4444
std::vector<Chunk *> getChunks();
4545
std::vector<Chunk *> getDataChunks();
4646
ArrayRef<Chunk *> getCodeChunks() { return thunks; }
47+
ArrayRef<Chunk *> getCodePData() { return pdata; }
48+
ArrayRef<Chunk *> getCodeUnwindInfo() { return unwindinfo; }
4749

4850
uint64_t getDirRVA() { return dirs[0]->getRVA(); }
4951
uint64_t getDirSize();
5052

5153
private:
5254
Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge);
5355
Chunk *newTailMergeChunk(Chunk *dir);
56+
Chunk *newTailMergePDataChunk(Chunk *tm, Chunk *unwind);
57+
Chunk *newTailMergeUnwindInfoChunk();
5458

5559
Defined *helper;
5660
std::vector<DefinedImportData *> imports;
@@ -60,6 +64,8 @@ class DelayLoadContents {
6064
std::vector<Chunk *> names;
6165
std::vector<Chunk *> hintNames;
6266
std::vector<Chunk *> thunks;
67+
std::vector<Chunk *> pdata;
68+
std::vector<Chunk *> unwindinfo;
6369
std::vector<Chunk *> dllNames;
6470

6571
COFFLinkerContext &ctx;

lld/COFF/Writer.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,10 @@ void Writer::appendImportThunks() {
11211121
dataSec->addChunk(c);
11221122
for (Chunk *c : delayIdata.getCodeChunks())
11231123
textSec->addChunk(c);
1124+
for (Chunk *c : delayIdata.getCodePData())
1125+
pdataSec->addChunk(c);
1126+
for (Chunk *c : delayIdata.getCodeUnwindInfo())
1127+
rdataSec->addChunk(c);
11241128
}
11251129
}
11261130

lld/test/COFF/delayimports.test

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
# RUN: /alternatename:__delayLoadHelper2=main
44
# RUN: llvm-readobj --coff-imports %t.exe | FileCheck -check-prefix=IMPORT %s
55
# RUN: llvm-readobj --coff-basereloc %t.exe | FileCheck -check-prefix=BASEREL %s
6+
# RUN: llvm-readobj --unwind %t.exe | FileCheck -check-prefix=UNWIND %s
67

78
IMPORT: DelayImport {
89
IMPORT-NEXT: Name: std64.dll
910
IMPORT-NEXT: Attributes: 0x1
1011
IMPORT-NEXT: ModuleHandle: 0x3018
1112
IMPORT-NEXT: ImportAddressTable: 0x3020
12-
IMPORT-NEXT: ImportNameTable: 0x2040
13+
IMPORT-NEXT: ImportNameTable: 0x2050
1314
IMPORT-NEXT: BoundDelayImportTable: 0x0
1415
IMPORT-NEXT: UnloadDelayImportTable: 0x0
1516
IMPORT-NEXT: Import {
@@ -39,3 +40,27 @@ BASEREL-NEXT: Entry {
3940
BASEREL-NEXT: Type: DIR64
4041
BASEREL-NEXT: Address: 0x3030
4142
BASEREL-NEXT: }
43+
44+
UNWIND: UnwindInformation [
45+
UNWIND-NEXT: RuntimeFunction {
46+
UNWIND-NEXT: StartAddress: (0x14000108A)
47+
UNWIND-NEXT: EndAddress: (0x1400010DD)
48+
UNWIND-NEXT: UnwindInfoAddress: (0x140002000)
49+
UNWIND-NEXT: UnwindInfo {
50+
UNWIND-NEXT: Version: 1
51+
UNWIND-NEXT: Flags [ (0x0)
52+
UNWIND-NEXT: ]
53+
UNWIND-NEXT: PrologSize: 10
54+
UNWIND-NEXT: FrameRegister: -
55+
UNWIND-NEXT: FrameOffset: -
56+
UNWIND-NEXT: UnwindCodeCount: 5
57+
UNWIND-NEXT: UnwindCodes [
58+
UNWIND-NEXT: 0x0A: ALLOC_SMALL size=72
59+
UNWIND-NEXT: 0x06: ALLOC_SMALL size=8
60+
UNWIND-NEXT: 0x04: ALLOC_SMALL size=8
61+
UNWIND-NEXT: 0x02: ALLOC_SMALL size=8
62+
UNWIND-NEXT: 0x01: ALLOC_SMALL size=8
63+
UNWIND-NEXT: ]
64+
UNWIND-NEXT: }
65+
UNWIND-NEXT: }
66+
UNWIND-NEXT: ]

lld/test/COFF/delayimporttables.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# CHECK-NEXT: Attributes: 0x1
1515
# CHECK-NEXT: ModuleHandle: 0x3000
1616
# CHECK-NEXT: ImportAddressTable: 0x3010
17-
# CHECK-NEXT: ImportNameTable: 0x2060
17+
# CHECK-NEXT: ImportNameTable: 0x2070
1818
# CHECK-NEXT: BoundDelayImportTable: 0x0
1919
# CHECK-NEXT: UnloadDelayImportTable: 0x0
2020
# CHECK-NEXT: Import {
@@ -31,7 +31,7 @@
3131
# CHECK-NEXT: Attributes: 0x1
3232
# CHECK-NEXT: ModuleHandle: 0x3008
3333
# CHECK-NEXT: ImportAddressTable: 0x3028
34-
# CHECK-NEXT: ImportNameTable: 0x2078
34+
# CHECK-NEXT: ImportNameTable: 0x2088
3535
# CHECK-NEXT: BoundDelayImportTable: 0x0
3636
# CHECK-NEXT: UnloadDelayImportTable: 0x0
3737
# CHECK-NEXT: Import {

lld/test/COFF/giats.s

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@
3737

3838
# DELAY-CHECK: ImageBase: 0x140000000
3939
# DELAY-CHECK: LoadConfig [
40-
# DELAY-CHECK: GuardCFFunctionTable: 0x140002114
40+
# DELAY-CHECK: GuardCFFunctionTable: 0x140002124
4141
# DELAY-CHECK: GuardCFFunctionCount: 2
4242
# DELAY-CHECK: GuardFlags [ (0x10500)
4343
# DELAY-CHECK: CF_FUNCTION_TABLE_PRESENT (0x400)
4444
# DELAY-CHECK: CF_INSTRUMENTED (0x100)
4545
# DELAY-CHECK: CF_LONGJUMP_TABLE_PRESENT (0x10000)
4646
# DELAY-CHECK: ]
47-
# DELAY-CHECK: GuardAddressTakenIatEntryTable: 0x14000211C
47+
# DELAY-CHECK: GuardAddressTakenIatEntryTable: 0x14000212C
4848
# DELAY-CHECK: GuardAddressTakenIatEntryCount: 1
4949
# DELAY-CHECK: ]
5050
# DELAY-CHECK: GuardFidTable [
@@ -122,4 +122,4 @@ _load_config_used:
122122
.quad __guard_iat_count
123123
.quad __guard_longjmp_table
124124
.quad __guard_fids_count
125-
.fill 84, 1, 0
125+
.fill 84, 1, 0

0 commit comments

Comments
 (0)