// Mod SDK runtime (Windows): applies lld's MinGW-style runtime pseudo-relocations on // the plain MSVC CRT. This is what lets mods reference game data (`extern` globals) with no // __declspec(dllimport) annotations: `lld-link -lldmingw` auto-imports the data through IAT // slots and records fixups, and this module applies them at load time. // // The fixup pass runs as an early CRT initializer, and the IAT slots it reads were already // bound by the OS loader, so even mod static initializers observe patched references. #include #include #include #include extern "C" char __RUNTIME_PSEUDO_RELOC_LIST__; extern "C" char __RUNTIME_PSEUDO_RELOC_LIST_END__; extern "C" IMAGE_DOS_HEADER __ImageBase; namespace { struct HdrV2 { uint32_t magic1; uint32_t magic2; uint32_t version; }; struct ItemV2 { uint32_t sym; // RVA of the __imp_ slot (OS-bound IAT entry) uint32_t target; // RVA of the reference to patch uint32_t flags; // low 8 bits: bit width of the reference }; bool g_relocsFailed = false; void report(const char* fmt, ...) { char buf[512]; va_list args; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); fprintf(stderr, "%s\n", buf); OutputDebugStringA(buf); } bool compute_fixup(const ItemV2& item, char* base, intptr_t* out) { char* impSlot = base + item.sym; const intptr_t real = *reinterpret_cast(impSlot); char* target = base + item.target; const int bits = static_cast(item.flags & 0xff); intptr_t reldata; switch (bits) { case 8: reldata = *reinterpret_cast(target); break; case 16: reldata = *reinterpret_cast(target); break; case 32: reldata = *reinterpret_cast(target); break; case 64: reldata = *reinterpret_cast(target); break; default: report("unsupported %d-bit pseudo-relocation at RVA 0x%x", bits, item.target); return false; } reldata -= reinterpret_cast(impSlot); reldata += real; if (bits < 64) { const intptr_t maxUnsigned = (intptr_t{1} << bits) - 1; const intptr_t minSigned = -(intptr_t{1} << (bits - 1)); if (reldata > maxUnsigned || reldata < minSigned) { report("%d-bit data fixup at RVA 0x%x is out of range (delta %+lld)", bits, item.target, static_cast(reldata)); return false; } } *out = reldata; return true; } } // namespace // lld refuses to emit runtime pseudo-relocs unless a function with exactly this (mingw CRT) // name exists in the image. It is also our actual entry point. extern "C" void _pei386_runtime_relocator() { char* base = reinterpret_cast(&__ImageBase); char* start = &__RUNTIME_PSEUDO_RELOC_LIST__; char* end = &__RUNTIME_PSEUDO_RELOC_LIST_END__; if (end - start < static_cast(sizeof(HdrV2))) { return; // no data auto-imports in this mod } const HdrV2* hdr = reinterpret_cast(start); if (hdr->magic1 != 0 || hdr->magic2 != 0 || hdr->version != 1) { report("unexpected pseudo-relocation list format"); g_relocsFailed = true; return; } const ItemV2* items = reinterpret_cast(hdr + 1); const ItemV2* itemsEnd = reinterpret_cast(end); // Validate everything before writing anything, so a bad fixup can't leave the image // half-patched. intptr_t scratch; for (const ItemV2* it = items; it < itemsEnd; ++it) { if (!compute_fixup(*it, base, &scratch)) { g_relocsFailed = true; return; } } for (const ItemV2* it = items; it < itemsEnd; ++it) { intptr_t reldata; compute_fixup(*it, base, &reldata); char* target = base + it->target; const size_t len = static_cast(it->flags & 0xff) / 8; DWORD old = 0; if (!VirtualProtect(target, len, PAGE_EXECUTE_READWRITE, &old)) { report("VirtualProtect failed at RVA 0x%x", it->target); g_relocsFailed = true; return; } std::memcpy(target, &reldata, len); VirtualProtect(target, len, old, &old); } } using PVFV = void (*)(); #pragma section(".CRT$XCB", read) extern "C" __declspec(allocate(".CRT$XCB")) PVFV dusk_mod_pseudo_reloc_init = _pei386_runtime_relocator; BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID) { if (reason == DLL_PROCESS_ATTACH && g_relocsFailed) { return FALSE; } return TRUE; }