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>
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)
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.
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);
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);
}
}
thunks.push_back(tm);
+ if (pdataChunk)
+ pdata.push_back(pdataChunk);
StringRef tmName =
saver().save("__tailMerge_" + syms[0]->getDLLName().lower());
ctx.symtab.addSynthetic(tmName, tm);
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)));
}
}
}
+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) {
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();
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;
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;
# 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 {
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: ]