geode/loader/include/Geode/utils/addresser.hpp

182 lines
5.7 KiB
C++
Raw Normal View History

2022-07-30 12:24:03 -04:00
#pragma once
/**
* Adapted from https://gist.github.com/altalk23/29b97969e9f0624f783b673f6c1cd279
*/
2023-01-30 01:53:20 -05:00
#include "../utils/casts.hpp"
2022-10-30 14:59:20 -04:00
#include "casts.hpp"
#include <Geode/DefaultInclude.hpp>
2023-01-30 06:10:39 -05:00
#include <cocos-ext.h>
#include <concepts>
2022-07-30 12:24:03 -04:00
#include <cstdlib>
#include <stddef.h>
#include <type_traits>
namespace geode::addresser {
template <class Function>
intptr_t getVirtual(Function func);
2022-10-30 14:59:20 -04:00
template <class Function>
intptr_t getNonVirtual(Function func);
2022-10-30 14:59:20 -04:00
template <class Function, class Class>
Class thunkAdjust(Function func, Class self);
2022-10-30 14:59:20 -04:00
template <class Function, class Class>
Class rthunkAdjust(Function func, Class self);
2023-01-30 02:06:58 -05:00
2023-12-21 09:13:39 -05:00
template <class Class>
concept HasZeroConstructor = requires {
new Class(ZeroConstructor);
};
2022-10-30 14:59:20 -04:00
class GEODE_DLL Addresser final {
template <char C>
struct SingleInheritance {
virtual ~SingleInheritance() {}
};
struct MultipleInheritance : SingleInheritance<'L'>, SingleInheritance<'F'> {
virtual ~MultipleInheritance() {}
};
static MultipleInheritance* instance();
template <typename R, typename T, typename... Ps>
static ptrdiff_t indexOf(R (T::*func)(Ps...)) {
using method_t = ptrdiff_t (T::*)();
return (reinterpret_cast<T*>(instance())->*reinterpret_cast<method_t>(func))();
}
template <typename R, typename T, typename... Ps>
static ptrdiff_t indexOf(R (T::*func)(Ps...) const) {
return indexOf(reinterpret_cast<R (T::*)(Ps...)>(func));
}
template <typename T>
static ptrdiff_t thunkOf(T ptr) {
// msvc
if (sizeof(T) == sizeof(ptrdiff_t)) return 0;
// all
auto thunk = *(reinterpret_cast<ptrdiff_t*>(&ptr) + 1);
// arm
if (thunk & 1) thunk >>= 1;
return thunk;
}
2023-01-30 02:06:58 -05:00
template <class Class>
2023-12-21 09:13:39 -05:00
static Class* cachedInstance() requires HasZeroConstructor<Class> {
static auto ret = new Class(ZeroConstructor);
2023-01-30 03:36:31 -05:00
return ret;
2023-01-30 02:06:58 -05:00
}
2023-12-21 09:13:39 -05:00
template <class Class>
static Class* cachedInstance() requires (!HasZeroConstructor<Class>) {
return nullptr;
}
2022-10-30 14:59:20 -04:00
/**
* Specialized functionss
*/
template <class Return, class Class, class... Parameters>
static intptr_t addressOfVirtual(Return (Class::*func)(Parameters...)) {
2022-10-30 14:59:20 -04:00
using geode::cast::reference_cast;
auto ins = cachedInstance<Class>();
2023-02-25 13:25:12 -05:00
// generateInstance will return nullptr on most abstract classes,
// so dont bother getting the address
if (ins == nullptr) {
return 0;
}
2022-10-30 14:59:20 -04:00
auto index = indexOf(func);
auto thunk = thunkOf(func);
2023-02-25 13:25:12 -05:00
// [[this + thunk] + offset] is the function we want
auto address = *reinterpret_cast<intptr_t*>(
*reinterpret_cast<intptr_t*>(reinterpret_cast<intptr_t>(ins) + thunk) + index
);
2022-10-30 14:59:20 -04:00
2023-08-27 10:10:08 -04:00
#ifdef GEODE_IS_WINDOWS
// if the first instruction is a long jmp then this might still be a thunk
if (*reinterpret_cast<uint8_t*>(address) == 0xE9) {
auto relative = *reinterpret_cast<uint32_t*>(address + 1);
address = address + relative + 5;
}
#endif
address = followThunkFunction(address);
2022-10-30 14:59:20 -04:00
return address;
}
template <class Return, class Class, class... Parameters>
static intptr_t addressOfVirtual(Return (Class::*func)(Parameters...) const) {
return addressOfVirtual(reinterpret_cast<Return (Class::*)(Parameters...)>(func));
2022-10-30 14:59:20 -04:00
}
static intptr_t followThunkFunction(intptr_t address);
template <class FnPtr>
static intptr_t addressOfNonVirtual(FnPtr func) {
return followThunkFunction(geode::cast::reference_cast<intptr_t>(func));
2022-10-30 14:59:20 -04:00
}
template <class Function>
friend intptr_t getVirtual(Function func);
2022-10-30 14:59:20 -04:00
template <class Function>
friend intptr_t getNonVirtual(Function func);
2022-10-30 14:59:20 -04:00
template <class Function, class Class>
friend Class thunkAdjust(Function func, Class self);
2022-10-30 14:59:20 -04:00
template <class Function, class Class>
friend Class rthunkAdjust(Function func, Class self);
2022-10-30 14:59:20 -04:00
};
/**
* Gets the real address of a virtual function
*/
template <class Function>
inline intptr_t getVirtual(Function func) {
2022-10-30 14:59:20 -04:00
return Addresser::addressOfVirtual(func);
}
/**
* Gets the real address of a non-virtual function
*/
template <class Function>
inline intptr_t getNonVirtual(Function func) {
2022-10-30 14:59:20 -04:00
return Addresser::addressOfNonVirtual(func);
}
/**
* Adjusts a class instance to its appropriate base for the given virtual function
*/
template <class Function, class Class>
inline Class thunkAdjust(Function func, Class self) {
2023-01-30 01:53:20 -05:00
// do NOT delete the line below.
2023-01-23 12:31:38 -05:00
// doing so breaks thunk adjusting on windows.
// why? bruh idk
auto _ = *geode::cast::template union_cast<ptrdiff_t*>(&func);
return (Class)((intptr_t)self + Addresser::thunkOf(func));
2022-10-30 14:59:20 -04:00
}
/**
* Adjusts a class instance back from its appropriate base for the given virtual function to the original base
*/
template <class Function, class Class>
inline Class rthunkAdjust(Function func, Class self) {
2023-01-30 01:53:20 -05:00
// do NOT delete the line below.
2023-01-23 12:31:38 -05:00
// doing so breaks thunk adjusting on windows.
// why? bruh idk
auto _ = *geode::cast::template union_cast<ptrdiff_t*>(&func);
return (Class)((intptr_t)self - Addresser::thunkOf(func));
2022-10-30 14:59:20 -04:00
}
2022-07-30 12:24:03 -04:00
}