enderalse/source/fs.dll/skse64/skse64_common/Relocation.cpp

31 lines
1.8 KiB
C++

#include "Relocation.h"
// the goal of this file is to support pointers in to a relocated binary with as little runtime overhead, code bloat, and hassle as possible
//
// since the main executable will always be loaded before the dll, the easiest solution is to perform the relocation in the constructor of
// a pointer class that supports conversion to T*. however, since we can't control anything about initialization order, each constructor
// must call GetModuleHandle(NULL) locally, which sucks. each pointer will need an entry in the static init table, and nothing can be done
// with the pointers in any other static constructors.
//
// one solution to this problem is init_seg(lib). any objects constructed in a file containing this pragma will be constructed before standard
// 'user' level code. this means we can use the constructor of that object to call GetModuleHandle once and initialize a global with the load
// address, which the pointer class constructor then references to fix up the addresses. this still creates an entry in the static init table
// for each pointer, but only calls GetModuleHandle once. pointers are not fixed up until all static init has finished, so other static ctors
// can't safely use pointers.
//
// the problem can't be solved further without moving the RelocPtr constructors in to init_seg(lib), which doesn't appear to be possible
// without forcing all pointers to be defined in a file with init_seg(lib). that is really ugly and doesn't seem like a good idea.
// anything in this file will initialized after the crt but before any user code
#pragma warning(disable: 4073) // yes this is intentional
#pragma init_seg(lib)
static RelocationManager s_relocMgr;
uintptr_t RelocationManager::s_baseAddr = 0;
RelocationManager::RelocationManager()
{
s_baseAddr = reinterpret_cast<uintptr_t>(GetModuleHandle(NULL));
}