[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 <yjuglaret@mozilla.com> for contributing the unwinding info and for his support while crafting this patch. Fix #59639 Differential Revision: https://reviews.llvm.org/D141691
This commit is contained in:
parent
48e862d06d
commit
c512eda38e
|
@ -229,6 +229,19 @@ static const uint8_t tailMergeX64[] = {
|
|||
0xFF, 0xE0, // jmp rax
|
||||
};
|
||||
|
||||
static const uint8_t tailMergeUnwindInfoX64[] = {
|
||||
0x01, // Version=1, Flags=UNW_FLAG_NHANDLER
|
||||
0x0a, // Size of prolog
|
||||
0x05, // Count of unwind codes
|
||||
0x00, // No frame register
|
||||
0x0a, 0x82, // Offset 0xa: UWOP_ALLOC_SMALL(0x48)
|
||||
0x06, 0x02, // Offset 6: UWOP_ALLOC_SMALL(8)
|
||||
0x04, 0x02, // Offset 4: UWOP_ALLOC_SMALL(8)
|
||||
0x02, 0x02, // Offset 2: UWOP_ALLOC_SMALL(8)
|
||||
0x01, 0x02, // Offset 1: UWOP_ALLOC_SMALL(8)
|
||||
0x00, 0x00 // Padding to align on 32-bits
|
||||
};
|
||||
|
||||
static const uint8_t thunkX86[] = {
|
||||
0xB8, 0, 0, 0, 0, // mov eax, offset ___imp__<FUNCNAME>
|
||||
0xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib>
|
||||
|
@ -332,6 +345,41 @@ public:
|
|||
Defined *helper = nullptr;
|
||||
};
|
||||
|
||||
class TailMergePDataChunkX64 : public NonSectionChunk {
|
||||
public:
|
||||
TailMergePDataChunkX64(Chunk *tm, Chunk *unwind) : tm(tm), unwind(unwind) {
|
||||
// See
|
||||
// https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function
|
||||
setAlignment(4);
|
||||
}
|
||||
|
||||
size_t getSize() const override { return 3 * sizeof(uint32_t); }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
write32le(buf + 0, tm->getRVA()); // TailMergeChunk start RVA
|
||||
write32le(buf + 4, tm->getRVA() + tm->getSize()); // TailMergeChunk stop RVA
|
||||
write32le(buf + 8, unwind->getRVA()); // UnwindInfo RVA
|
||||
}
|
||||
|
||||
Chunk *tm = nullptr;
|
||||
Chunk *unwind = nullptr;
|
||||
};
|
||||
|
||||
class TailMergeUnwindInfoX64 : public NonSectionChunk {
|
||||
public:
|
||||
TailMergeUnwindInfoX64() {
|
||||
// See
|
||||
// https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info
|
||||
setAlignment(4);
|
||||
}
|
||||
|
||||
size_t getSize() const override { return sizeof(tailMergeUnwindInfoX64); }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memcpy(buf, tailMergeUnwindInfoX64, sizeof(tailMergeUnwindInfoX64));
|
||||
}
|
||||
};
|
||||
|
||||
class ThunkChunkX86 : public NonSectionChunk {
|
||||
public:
|
||||
ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
|
||||
|
@ -672,6 +720,8 @@ void DelayLoadContents::create(Defined *h) {
|
|||
helper = h;
|
||||
std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
|
||||
|
||||
Chunk *unwind = newTailMergeUnwindInfoChunk();
|
||||
|
||||
// Create .didat contents for each DLL.
|
||||
for (std::vector<DefinedImportData *> &syms : v) {
|
||||
// Create the delay import table header.
|
||||
|
@ -680,6 +730,7 @@ void DelayLoadContents::create(Defined *h) {
|
|||
|
||||
size_t base = addresses.size();
|
||||
Chunk *tm = newTailMergeChunk(dir);
|
||||
Chunk *pdataChunk = unwind ? newTailMergePDataChunk(tm, unwind) : nullptr;
|
||||
for (DefinedImportData *s : syms) {
|
||||
Chunk *t = newThunkChunk(s, tm);
|
||||
auto *a = make<DelayAddressChunk>(ctx, t);
|
||||
|
@ -692,7 +743,7 @@ void DelayLoadContents::create(Defined *h) {
|
|||
auto *c = make<HintNameChunk>(extName, 0);
|
||||
names.push_back(make<LookupChunk>(ctx, c));
|
||||
hintNames.push_back(c);
|
||||
// Add a syntentic symbol for this load thunk, using the "__imp___load"
|
||||
// Add a synthetic symbol for this load thunk, using the "__imp___load"
|
||||
// prefix, in case this thunk needs to be added to the list of valid
|
||||
// call targets for Control Flow Guard.
|
||||
StringRef symName = saver().save("__imp___load_" + extName);
|
||||
|
@ -701,6 +752,8 @@ void DelayLoadContents::create(Defined *h) {
|
|||
}
|
||||
}
|
||||
thunks.push_back(tm);
|
||||
if (pdataChunk)
|
||||
pdata.push_back(pdataChunk);
|
||||
StringRef tmName =
|
||||
saver().save("__tailMerge_" + syms[0]->getDLLName().lower());
|
||||
ctx.symtab.addSynthetic(tmName, tm);
|
||||
|
@ -720,6 +773,9 @@ void DelayLoadContents::create(Defined *h) {
|
|||
dir->nameTab = names[base];
|
||||
dirs.push_back(dir);
|
||||
}
|
||||
|
||||
if (unwind)
|
||||
unwindinfo.push_back(unwind);
|
||||
// Add null terminator.
|
||||
dirs.push_back(make<NullChunk>(sizeof(delay_import_directory_table_entry)));
|
||||
}
|
||||
|
@ -739,6 +795,25 @@ Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
|
|||
}
|
||||
}
|
||||
|
||||
Chunk *DelayLoadContents::newTailMergeUnwindInfoChunk() {
|
||||
switch (ctx.config.machine) {
|
||||
case AMD64:
|
||||
return make<TailMergeUnwindInfoX64>();
|
||||
// FIXME: Add support for other architectures.
|
||||
default:
|
||||
return nullptr; // Just don't generate unwind info.
|
||||
}
|
||||
}
|
||||
Chunk *DelayLoadContents::newTailMergePDataChunk(Chunk *tm, Chunk *unwind) {
|
||||
switch (ctx.config.machine) {
|
||||
case AMD64:
|
||||
return make<TailMergePDataChunkX64>(tm, unwind);
|
||||
// FIXME: Add support for other architectures.
|
||||
default:
|
||||
return nullptr; // Just don't generate unwind info.
|
||||
}
|
||||
}
|
||||
|
||||
Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
|
||||
Chunk *tailMerge) {
|
||||
switch (ctx.config.machine) {
|
||||
|
|
|
@ -44,6 +44,8 @@ public:
|
|||
std::vector<Chunk *> getChunks();
|
||||
std::vector<Chunk *> getDataChunks();
|
||||
ArrayRef<Chunk *> getCodeChunks() { return thunks; }
|
||||
ArrayRef<Chunk *> getCodePData() { return pdata; }
|
||||
ArrayRef<Chunk *> getCodeUnwindInfo() { return unwindinfo; }
|
||||
|
||||
uint64_t getDirRVA() { return dirs[0]->getRVA(); }
|
||||
uint64_t getDirSize();
|
||||
|
@ -51,6 +53,8 @@ public:
|
|||
private:
|
||||
Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge);
|
||||
Chunk *newTailMergeChunk(Chunk *dir);
|
||||
Chunk *newTailMergePDataChunk(Chunk *tm, Chunk *unwind);
|
||||
Chunk *newTailMergeUnwindInfoChunk();
|
||||
|
||||
Defined *helper;
|
||||
std::vector<DefinedImportData *> imports;
|
||||
|
@ -60,6 +64,8 @@ private:
|
|||
std::vector<Chunk *> names;
|
||||
std::vector<Chunk *> hintNames;
|
||||
std::vector<Chunk *> thunks;
|
||||
std::vector<Chunk *> pdata;
|
||||
std::vector<Chunk *> unwindinfo;
|
||||
std::vector<Chunk *> dllNames;
|
||||
|
||||
COFFLinkerContext &ctx;
|
||||
|
|
|
@ -1121,6 +1121,10 @@ void Writer::appendImportThunks() {
|
|||
dataSec->addChunk(c);
|
||||
for (Chunk *c : delayIdata.getCodeChunks())
|
||||
textSec->addChunk(c);
|
||||
for (Chunk *c : delayIdata.getCodePData())
|
||||
pdataSec->addChunk(c);
|
||||
for (Chunk *c : delayIdata.getCodeUnwindInfo())
|
||||
rdataSec->addChunk(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
# RUN: /alternatename:__delayLoadHelper2=main
|
||||
# RUN: llvm-readobj --coff-imports %t.exe | FileCheck -check-prefix=IMPORT %s
|
||||
# RUN: llvm-readobj --coff-basereloc %t.exe | FileCheck -check-prefix=BASEREL %s
|
||||
# RUN: llvm-readobj --unwind %t.exe | FileCheck -check-prefix=UNWIND %s
|
||||
|
||||
IMPORT: DelayImport {
|
||||
IMPORT-NEXT: Name: std64.dll
|
||||
IMPORT-NEXT: Attributes: 0x1
|
||||
IMPORT-NEXT: ModuleHandle: 0x3018
|
||||
IMPORT-NEXT: ImportAddressTable: 0x3020
|
||||
IMPORT-NEXT: ImportNameTable: 0x2040
|
||||
IMPORT-NEXT: ImportNameTable: 0x2050
|
||||
IMPORT-NEXT: BoundDelayImportTable: 0x0
|
||||
IMPORT-NEXT: UnloadDelayImportTable: 0x0
|
||||
IMPORT-NEXT: Import {
|
||||
|
@ -39,3 +40,27 @@ BASEREL-NEXT: Entry {
|
|||
BASEREL-NEXT: Type: DIR64
|
||||
BASEREL-NEXT: Address: 0x3030
|
||||
BASEREL-NEXT: }
|
||||
|
||||
UNWIND: UnwindInformation [
|
||||
UNWIND-NEXT: RuntimeFunction {
|
||||
UNWIND-NEXT: StartAddress: (0x14000108A)
|
||||
UNWIND-NEXT: EndAddress: (0x1400010DD)
|
||||
UNWIND-NEXT: UnwindInfoAddress: (0x140002000)
|
||||
UNWIND-NEXT: UnwindInfo {
|
||||
UNWIND-NEXT: Version: 1
|
||||
UNWIND-NEXT: Flags [ (0x0)
|
||||
UNWIND-NEXT: ]
|
||||
UNWIND-NEXT: PrologSize: 10
|
||||
UNWIND-NEXT: FrameRegister: -
|
||||
UNWIND-NEXT: FrameOffset: -
|
||||
UNWIND-NEXT: UnwindCodeCount: 5
|
||||
UNWIND-NEXT: UnwindCodes [
|
||||
UNWIND-NEXT: 0x0A: ALLOC_SMALL size=72
|
||||
UNWIND-NEXT: 0x06: ALLOC_SMALL size=8
|
||||
UNWIND-NEXT: 0x04: ALLOC_SMALL size=8
|
||||
UNWIND-NEXT: 0x02: ALLOC_SMALL size=8
|
||||
UNWIND-NEXT: 0x01: ALLOC_SMALL size=8
|
||||
UNWIND-NEXT: ]
|
||||
UNWIND-NEXT: }
|
||||
UNWIND-NEXT: }
|
||||
UNWIND-NEXT: ]
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
# CHECK-NEXT: Attributes: 0x1
|
||||
# CHECK-NEXT: ModuleHandle: 0x3000
|
||||
# CHECK-NEXT: ImportAddressTable: 0x3010
|
||||
# CHECK-NEXT: ImportNameTable: 0x2060
|
||||
# CHECK-NEXT: ImportNameTable: 0x2070
|
||||
# CHECK-NEXT: BoundDelayImportTable: 0x0
|
||||
# CHECK-NEXT: UnloadDelayImportTable: 0x0
|
||||
# CHECK-NEXT: Import {
|
||||
|
@ -31,7 +31,7 @@
|
|||
# CHECK-NEXT: Attributes: 0x1
|
||||
# CHECK-NEXT: ModuleHandle: 0x3008
|
||||
# CHECK-NEXT: ImportAddressTable: 0x3028
|
||||
# CHECK-NEXT: ImportNameTable: 0x2078
|
||||
# CHECK-NEXT: ImportNameTable: 0x2088
|
||||
# CHECK-NEXT: BoundDelayImportTable: 0x0
|
||||
# CHECK-NEXT: UnloadDelayImportTable: 0x0
|
||||
# CHECK-NEXT: Import {
|
||||
|
|
|
@ -37,14 +37,14 @@
|
|||
|
||||
# DELAY-CHECK: ImageBase: 0x140000000
|
||||
# DELAY-CHECK: LoadConfig [
|
||||
# DELAY-CHECK: GuardCFFunctionTable: 0x140002114
|
||||
# DELAY-CHECK: GuardCFFunctionTable: 0x140002124
|
||||
# DELAY-CHECK: GuardCFFunctionCount: 2
|
||||
# DELAY-CHECK: GuardFlags [ (0x10500)
|
||||
# DELAY-CHECK: CF_FUNCTION_TABLE_PRESENT (0x400)
|
||||
# DELAY-CHECK: CF_INSTRUMENTED (0x100)
|
||||
# DELAY-CHECK: CF_LONGJUMP_TABLE_PRESENT (0x10000)
|
||||
# DELAY-CHECK: ]
|
||||
# DELAY-CHECK: GuardAddressTakenIatEntryTable: 0x14000211C
|
||||
# DELAY-CHECK: GuardAddressTakenIatEntryTable: 0x14000212C
|
||||
# DELAY-CHECK: GuardAddressTakenIatEntryCount: 1
|
||||
# DELAY-CHECK: ]
|
||||
# DELAY-CHECK: GuardFidTable [
|
||||
|
@ -122,4 +122,4 @@ _load_config_used:
|
|||
.quad __guard_iat_count
|
||||
.quad __guard_longjmp_table
|
||||
.quad __guard_fids_count
|
||||
.fill 84, 1, 0
|
||||
.fill 84, 1, 0
|
||||
|
|
Loading…
Reference in New Issue
Block a user