mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-22 23:48:08 -05:00
remove meta and hook-core remnants
This commit is contained in:
parent
8551071ac9
commit
a90b3e18dd
29 changed files with 4 additions and 1694 deletions
|
@ -1,43 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../meta/meta.hpp"
|
||||
|
||||
namespace geode::core {
|
||||
|
||||
namespace impl {
|
||||
/* the handler itself */
|
||||
template <auto& Det, class Ret, class... Args>
|
||||
Ret handler(Args... args) {
|
||||
static thread_local int counter = 0;
|
||||
|
||||
if constexpr (std::is_same_v<Ret, void>) {
|
||||
if (counter == (int)Det->size()) counter = 0;
|
||||
|
||||
Det->at(counter++)(args...);
|
||||
|
||||
if (--counter < 0) counter = Det->size() - 1;
|
||||
}
|
||||
else {
|
||||
if (counter == (int)Det->size()) counter = 0;
|
||||
|
||||
Ret ret = Det->at(counter++)(args...);
|
||||
|
||||
if (--counter < 0) counter = Det->size() - 1;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
template <template <class, class...> class Conv, auto& Func, class Ret, class... Args>
|
||||
Ret trampoline(Args... args) {
|
||||
return meta::Function<Ret(Args...), Conv>(Func)(args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <template <class, class...> class Conv, auto& Det, class Ret, class... Args>
|
||||
static inline auto handler =
|
||||
Conv<Ret, Args...>::template get_wrapper<&impl::handler<Det, Ret, Args...>>();
|
||||
|
||||
template <template <class, class...> class Conv, auto& Det, class Ret, class... Args>
|
||||
static constexpr inline auto trampoline = &impl::trampoline<Conv, Det, Ret, Args...>;
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../utils/Result.hpp"
|
||||
#include "Handler.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace geode::core {
|
||||
|
||||
// The handle returned from adding a hook. It is used to remove a hook.
|
||||
struct HookHandle {
|
||||
void* handler;
|
||||
void* address;
|
||||
void* detour;
|
||||
void* trampoline;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
// a type alias for convenience and for clear type differentiation inside the source file
|
||||
using VectorPointer = std::vector<void*>*;
|
||||
|
||||
GEODE_DLL void addHook(
|
||||
void* address, void* detour, VectorPointer* detourVectorAddress, void* generatedHandler,
|
||||
void** originalTrampolineAddress, void* generatedTrampoline
|
||||
);
|
||||
|
||||
GEODE_DLL void removeHook(HookHandle const& handle);
|
||||
}
|
||||
|
||||
namespace hook {
|
||||
template <auto Detour, template <class, class...> class Conv, class Ret, class... Args>
|
||||
Result<HookHandle> add(Ret (*address)(Args...)) {
|
||||
static std::vector<decltype(Detour)>* detourVector;
|
||||
static decltype(Detour) originalTrampoline;
|
||||
|
||||
void* generatedHandler = (void*)handler<Conv, detourVector, Ret, Args...>;
|
||||
void* generatedTrampoline = (void*)trampoline<Conv, originalTrampoline, Ret, Args...>;
|
||||
|
||||
impl::addHook(
|
||||
(void*)address, (void*)Detour, (impl::VectorPointer*)&detourVector,
|
||||
(void*)generatedHandler, (void**)&originalTrampoline, (void*)generatedTrampoline
|
||||
);
|
||||
|
||||
return Ok<HookHandle>({ (void*)generatedHandler, (void*)address, (void*)Detour,
|
||||
(void*)generatedTrampoline });
|
||||
}
|
||||
|
||||
inline Result<> remove(HookHandle const& handle) {
|
||||
impl::removeHook(handle);
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "../DefaultInclude.hpp"
|
||||
#include "../hook-core/Hook.hpp"
|
||||
#include "../utils/general.hpp"
|
||||
#include "../external/json/json_fwd.hpp"
|
||||
#include "Tulip.hpp"
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_CALLCONV_HPP
|
||||
#define GEODE_CORE_META_CALLCONV_HPP
|
||||
|
||||
#include "tuple.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
namespace geode::core::meta {
|
||||
/* CRTP class for creating calling conventions for Function and Hook.
|
||||
* Provides some utilities for less verbose filtering of parameters, and
|
||||
* some wrappers to require the custom calling convention to supply an
|
||||
* invoker and a way to get a wrapper for hooks.
|
||||
*/
|
||||
template <class Ret, class... Args>
|
||||
class CallConv {
|
||||
protected:
|
||||
using MyTuple = Tuple<Args...>;
|
||||
|
||||
template <auto>
|
||||
class arr_to_seq_impl;
|
||||
|
||||
template <class Type, size_t length, std::array<Type, length> const* arr>
|
||||
class arr_to_seq_impl<arr> {
|
||||
private:
|
||||
template <class>
|
||||
class getter;
|
||||
|
||||
template <size_t... indices>
|
||||
class getter<std::index_sequence<indices...>> {
|
||||
public:
|
||||
using result = std::index_sequence<arr->at(indices)...>;
|
||||
};
|
||||
|
||||
public:
|
||||
using result = typename getter<std::make_index_sequence<length>>::result;
|
||||
};
|
||||
|
||||
template <auto& arr>
|
||||
using arr_to_seq = typename arr_to_seq_impl<&arr>::result;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_CALLCONV_HPP */
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_CDECL_HPP
|
||||
#define GEODE_CORE_META_CDECL_HPP
|
||||
|
||||
namespace geode::core::meta::x86 {
|
||||
template <class Ret, class... Args>
|
||||
class Cdecl {
|
||||
private:
|
||||
template <Ret (*detour)(Args...)>
|
||||
static Ret __cdecl wrapper(Args... all) {
|
||||
return detour(all...);
|
||||
}
|
||||
|
||||
public:
|
||||
static Ret invoke(void* address, Args... all) {
|
||||
Ret(__cdecl * raw)(Args...) = reinterpret_cast<decltype(raw)>(address);
|
||||
return raw(all...);
|
||||
}
|
||||
|
||||
template <Ret (*detour)(Args...)>
|
||||
static auto get_wrapper() {
|
||||
return reinterpret_cast<void*>(&wrapper<detour>);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_THISCALL_HPP */
|
|
@ -1,14 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_COMMON_HPP
|
||||
#define GEODE_CORE_META_COMMON_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace geode::core::meta {
|
||||
template <class Type, class... Compare>
|
||||
static constexpr bool any_of = (std::is_same_v<Type, Compare> || ...);
|
||||
|
||||
template <class... Classes>
|
||||
static constexpr bool always_false = false;
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_COMMON_HPP */
|
|
@ -1,20 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_DEFAULTCONV_HPP
|
||||
#define GEODE_CORE_META_DEFAULTCONV_HPP
|
||||
|
||||
namespace geode::core::meta {
|
||||
template <class Ret, class... Args>
|
||||
class DefaultConv {
|
||||
public:
|
||||
static Ret invoke(void* address, Args... all) {
|
||||
Ret (*raw)(Args...) = reinterpret_cast<decltype(raw)>(address);
|
||||
return raw(all...);
|
||||
}
|
||||
|
||||
template <Ret (*detour)(Args...)>
|
||||
static auto get_wrapper() {
|
||||
return reinterpret_cast<void*>(detour);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_DEFAULTCONV_HPP */
|
|
@ -1,37 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_FUNCTION_HPP
|
||||
#define GEODE_CORE_META_FUNCTION_HPP
|
||||
|
||||
#include "callconv.hpp"
|
||||
#include "common.hpp"
|
||||
#include "tuple.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace geode::core::meta {
|
||||
/* The Geode Function class wraps functions with unconventional
|
||||
* calling conventions (how ironic).
|
||||
*/
|
||||
template <class Func, template <class, class...> class Conv>
|
||||
class Function {
|
||||
static_assert(always_false<Func>, "Not a valid function pointer!");
|
||||
};
|
||||
|
||||
template <class Ret, class... Args, template <class, class...> class Conv>
|
||||
class Function<Ret(Args...), Conv> {
|
||||
private:
|
||||
using MyConv = Conv<Ret, Args...>;
|
||||
|
||||
private:
|
||||
void* addr;
|
||||
|
||||
public:
|
||||
template <class Pointer>
|
||||
Function(Pointer const& addr) : addr(reinterpret_cast<void*>(addr)) {}
|
||||
|
||||
decltype(auto) operator()(Args... all) const {
|
||||
return MyConv::invoke(addr, all...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_FUNCTION_HPP */
|
|
@ -1,251 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_MEMBERCALL_HPP
|
||||
#define GEODE_CORE_META_MEMBERCALL_HPP
|
||||
|
||||
#include "callconv.hpp"
|
||||
#include "tuple.hpp"
|
||||
#include "x86.hpp"
|
||||
|
||||
namespace geode::core::meta::x86 {
|
||||
template <class T>
|
||||
concept MembercallStructReturn = std::is_class_v<T> && sizeof(T) > 8;
|
||||
|
||||
template <class Ret, class... Args>
|
||||
class Membercall {};
|
||||
|
||||
template <class Ret, class... Args>
|
||||
requires (!MembercallStructReturn<Ret>)
|
||||
class Membercall<Ret, Args...> : public CallConv<Ret, Args...> {
|
||||
protected:
|
||||
// Metaprogramming / typedefs we need for the rest of the class.
|
||||
using MyConv = CallConv<Ret, Args...>;
|
||||
|
||||
public:
|
||||
class Sequences {
|
||||
private:
|
||||
// These are required for proper reordering.
|
||||
static constexpr size_t length = sizeof...(Args);
|
||||
|
||||
static constexpr size_t SSES = 3;
|
||||
static constexpr bool is_sse[length] = { sse_passable<Args>... };
|
||||
|
||||
static constexpr bool is_gpr[length] = { gpr_passable<Args>... };
|
||||
|
||||
static constexpr auto reordered_arr = reorder_pack<Args...>();
|
||||
|
||||
// Setup call from our caller, to "foreign" function
|
||||
static constexpr auto filter_to() {
|
||||
/* The size of our output may be longer than the input.
|
||||
* For the third time, annoyingly, we need to make a lambda for this.
|
||||
*/
|
||||
constexpr auto arr_size = []() -> size_t {
|
||||
// Magic constant 5: XMM0, XMM4, XMM5, ECX, and EDX.
|
||||
size_t size = length + SSES + 5;
|
||||
|
||||
// Check our only GPR.
|
||||
if (0 < length && is_gpr[reordered_arr[0]]) {
|
||||
--size;
|
||||
}
|
||||
|
||||
/* We assume there are no SSES initially.
|
||||
* Any SSES we encounter, we have to remove a "duplicate".
|
||||
*/
|
||||
for (size_t i = 1; i < SSES + 1; ++i) {
|
||||
if (i < length && is_sse[reordered_arr[i]]) {
|
||||
--size;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
};
|
||||
std::array<size_t, arr_size()> to = {};
|
||||
|
||||
/* These are the indices of the placeholder float and int, for clobbering SSEs and
|
||||
* GPRs, respectively.
|
||||
*/
|
||||
constexpr size_t CLOBBER_SSE = length;
|
||||
constexpr size_t CLOBBER_GPR = length + 1;
|
||||
|
||||
// Put the SSEs into the right XMM registers, if they exist.
|
||||
for (size_t i = 1; i < SSES + 1; ++i) {
|
||||
if (i < length && is_sse[reordered_arr[i]]) {
|
||||
to[i] = reordered_arr[i];
|
||||
}
|
||||
else {
|
||||
to[i] = CLOBBER_SSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Clobber XMM0, XMM4, XMM5, and EDX.
|
||||
to[0] = CLOBBER_SSE;
|
||||
to[4] = CLOBBER_SSE;
|
||||
to[5] = CLOBBER_SSE;
|
||||
to[7] = CLOBBER_GPR;
|
||||
|
||||
// Handle our GPR and put it in ECX if we can.
|
||||
if (length > 0 && is_gpr[reordered_arr[0]]) {
|
||||
to[6] = reordered_arr[0];
|
||||
}
|
||||
else {
|
||||
to[6] = CLOBBER_GPR;
|
||||
}
|
||||
|
||||
for (size_t in = 1, out = SSES + 5; in < length; ++in) {
|
||||
// Put all non SSEs and non GPRs in their places.
|
||||
size_t current = reordered_arr[in];
|
||||
if (!(is_sse[current] && in < SSES + 1)) {
|
||||
to[out] = current;
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
return to;
|
||||
}
|
||||
|
||||
// Setup call from "foreign" function, to one of ours.
|
||||
static constexpr auto filter_from() {
|
||||
std::array<size_t, length> from = {};
|
||||
|
||||
constexpr size_t CLOBBER_SSE = length;
|
||||
constexpr size_t CLOBBER_GPR = length + 1;
|
||||
|
||||
if (length > 0 && is_gpr[reordered_arr[0]]) {
|
||||
// SSES + 3 = ECX
|
||||
from[reordered_arr[0]] = SSES + 3;
|
||||
}
|
||||
|
||||
for (size_t i = 1, offset = 0; i < length; ++i) {
|
||||
size_t current = reordered_arr[i];
|
||||
if (is_sse[current] && i < SSES + 1) {
|
||||
// If in SSE, retain index
|
||||
from[current] = i;
|
||||
}
|
||||
else {
|
||||
// If on stack, offset by 8 (6 SSE + 2 GPR registers available)
|
||||
from[current] = offset + SSES + 5;
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
|
||||
return from;
|
||||
}
|
||||
|
||||
// Annoyingly, unless we're using C++20, we can't eliminate these intermediates. (afaik)
|
||||
static constexpr auto to_arr = filter_to();
|
||||
static constexpr auto from_arr = filter_from();
|
||||
|
||||
public:
|
||||
using to = typename MyConv::template arr_to_seq<to_arr>;
|
||||
using from = typename MyConv::template arr_to_seq<from_arr>;
|
||||
};
|
||||
|
||||
protected:
|
||||
// Where all the logic is actually implemented.
|
||||
template <class Class, class>
|
||||
class Impl {
|
||||
static_assert(
|
||||
always_false<Class>,
|
||||
"Please report a bug to the Geode developers! This should never be reached.\n"
|
||||
"SFINAE didn't reach the right overload!"
|
||||
);
|
||||
};
|
||||
|
||||
template <size_t... to, size_t... from>
|
||||
class Impl<std::index_sequence<to...>, std::index_sequence<from...>> {
|
||||
public:
|
||||
static Ret invoke(void* address, Tuple<Args..., float, int> const& all) {
|
||||
return reinterpret_cast<Ret(__vectorcall*)(
|
||||
typename Tuple<Args..., float, int>::template type_at<to>...
|
||||
)>(address)(all.template at<to>()...);
|
||||
}
|
||||
|
||||
template <Ret (*detour)(Args...)>
|
||||
static Ret __vectorcall wrapper(
|
||||
/* It's wrapped to stop MSVC from giving me error messages with internal compiler
|
||||
* info. WTF.
|
||||
*/
|
||||
typename Tuple<Args..., float, int>::template type_at_wrap<to>... raw
|
||||
) {
|
||||
auto all = Tuple<>::make(raw...);
|
||||
return detour(all.template at<from>()...);
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
// Putting it all together: instantiating Impl with our filters.
|
||||
using MyImpl = Impl<typename Sequences::to, typename Sequences::from>;
|
||||
|
||||
public:
|
||||
// Just wrapping MyImpl.
|
||||
static Ret invoke(void* address, Args... all) {
|
||||
/* The extra float and int here are so that we can grab placeholders.
|
||||
* If we don't have anything in XMM0 - 5 or ECX / EDX, we will use
|
||||
* these placeholders instead. The values are 314 to avoid unintentional
|
||||
* bugs (since 0 may work coincidentally).
|
||||
*/
|
||||
return MyImpl::invoke(address, { all..., 314.0f, 314 });
|
||||
}
|
||||
|
||||
template <Ret (*detour)(Args...)>
|
||||
static auto get_wrapper() {
|
||||
return reinterpret_cast<void*>(&MyImpl::template wrapper<detour>);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Ret, class Class, class... Args>
|
||||
requires (MembercallStructReturn<Ret>)
|
||||
class Membercall<Ret, Class, Args...> {
|
||||
|
||||
protected:
|
||||
using Sequences = typename Membercall<Ret*, Class, Ret*, Args...>::Sequences;
|
||||
|
||||
// Where all the logic is actually implemented.
|
||||
template <class ImplClass, class>
|
||||
class Impl {
|
||||
static_assert(
|
||||
always_false<ImplClass>,
|
||||
"Please report a bug to the Geode developers! This should never be reached.\n"
|
||||
"SFINAE didn't reach the right overload!"
|
||||
);
|
||||
};
|
||||
|
||||
template <size_t... to, size_t... from>
|
||||
class Impl<std::index_sequence<to...>, std::index_sequence<from...>> {
|
||||
public:
|
||||
static Ret* invoke(void* address, Tuple<Class, Ret*, Args..., float, int> const& all) {
|
||||
return reinterpret_cast<Ret*(__vectorcall*)(
|
||||
typename Tuple<Class, Ret*, Args..., float, int>::template type_at<to>...
|
||||
)>(address)(all.template at<to>()...);
|
||||
}
|
||||
|
||||
template <Ret (*detour)(Class, Args...)>
|
||||
static Ret* __vectorcall wrapper(
|
||||
/* It's wrapped to stop MSVC from giving me error messages with internal compiler
|
||||
* info. WTF.
|
||||
*/
|
||||
typename Tuple<Class, Ret*, Args..., float, int>::template type_at_wrap<to>... raw
|
||||
) {
|
||||
auto all = Tuple<>::make(raw...);
|
||||
return reinterpret_cast<Ret*(*)(Class, Ret*, Args...)>(detour)(all.template at<from>()...);
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
// Putting it all together: instantiating Impl with our filters.
|
||||
using MyImpl = Impl<typename Sequences::to, typename Sequences::from>;
|
||||
|
||||
|
||||
public:
|
||||
static Ret invoke(void* address, Class inst, Args... all) {
|
||||
Ret ret;
|
||||
(void)MyImpl::invoke(address, { inst, &ret, all..., 314.0f, 314 });
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <Ret (*detour)(Class, Args...)>
|
||||
static auto get_wrapper() {
|
||||
return reinterpret_cast<void*>(&MyImpl::template wrapper<detour>);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_MEMBERCALL_HPP */
|
|
@ -1,22 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_META_HPP
|
||||
#define GEODE_CORE_META_META_HPP
|
||||
|
||||
#include "../platform/platform.hpp"
|
||||
#include "callconv.hpp"
|
||||
#include "common.hpp"
|
||||
#include "defaultconv.hpp"
|
||||
#include "function.hpp"
|
||||
|
||||
namespace geode::core::meta {}
|
||||
|
||||
namespace geode::core::meta::x86 {}
|
||||
|
||||
#if defined(GEODE_IS_WINDOWS)
|
||||
#include "cdecl.hpp"
|
||||
#include "membercall.hpp"
|
||||
#include "optcall.hpp"
|
||||
#include "thiscall.hpp"
|
||||
#include "stdcall.hpp"
|
||||
#endif
|
||||
|
||||
#endif /* GEODE_CORE_META_META_HPP */
|
|
@ -1,255 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_OPTCALL_HPP
|
||||
#define GEODE_CORE_META_OPTCALL_HPP
|
||||
|
||||
#include "callconv.hpp"
|
||||
#include "tuple.hpp"
|
||||
#include "x86.hpp"
|
||||
|
||||
namespace geode::core::meta::x86 {
|
||||
template <class Ret, class... Args>
|
||||
class Optcall : public CallConv<Ret, Args...> {
|
||||
private:
|
||||
// Metaprogramming / typedefs we need for the rest of the class.
|
||||
using MyConv = CallConv<Ret, Args...>;
|
||||
|
||||
private:
|
||||
// These go in a class to not pollute the namespace.
|
||||
class Sequences {
|
||||
private:
|
||||
// These are required for proper reordering.
|
||||
static constexpr size_t length = sizeof...(Args);
|
||||
|
||||
static constexpr size_t SSES = 4;
|
||||
static constexpr bool is_sse[length] = { sse_passable<Args>... };
|
||||
|
||||
static constexpr size_t GPRS = 2;
|
||||
static constexpr bool is_gpr[length] = { gpr_passable<Args>... };
|
||||
|
||||
static constexpr auto reordered_arr = reorder_pack<Args...>();
|
||||
|
||||
// Setup call from our caller, to "foreign" function
|
||||
static constexpr auto filter_to() {
|
||||
/* The size of our output may be longer than the input.
|
||||
* Also, annoyingly, we must make a lambda to calculate this.
|
||||
*/
|
||||
constexpr auto arr_size = []() -> size_t {
|
||||
// Magic constant 2 is to pad XMM4 and XMM5: can't be used.
|
||||
size_t size = length + SSES + 2;
|
||||
|
||||
/* We assume there are no SSES initially.
|
||||
* Any SSES we encounter, we have to remove a "duplicate".
|
||||
*/
|
||||
for (size_t i = 0; i < SSES; ++i) {
|
||||
if (i < length && is_sse[reordered_arr[i]]) {
|
||||
--size;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
};
|
||||
std::array<size_t, arr_size()> to = {};
|
||||
|
||||
// This is the index of the placeholder float, for clobbering SSEs.
|
||||
constexpr size_t CLOBBER_SSE = length;
|
||||
|
||||
// Put the SSEs into the right XMM registers, if they exist.
|
||||
for (size_t i = 0; i < SSES; ++i) {
|
||||
if (i < length && is_sse[reordered_arr[i]]) {
|
||||
to[i] = reordered_arr[i];
|
||||
}
|
||||
else {
|
||||
to[i] = CLOBBER_SSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Clobber XMM4 and XMM5.
|
||||
to[4] = CLOBBER_SSE;
|
||||
to[5] = CLOBBER_SSE;
|
||||
|
||||
for (size_t in = 0, out = SSES + 2; in < length; ++in) {
|
||||
// Put all the non SSEs in their correct places. Skip SSEs.
|
||||
size_t current = reordered_arr[in];
|
||||
if (!(is_sse[current] && in < SSES)) {
|
||||
to[out] = current;
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
return to;
|
||||
}
|
||||
|
||||
// Setup call from "foreign" function, to one of ours.
|
||||
static constexpr auto filter_from() {
|
||||
std::array<size_t, length> from = {};
|
||||
|
||||
for (size_t i = 0, gprs = 0, offset = 0; i < length; ++i) {
|
||||
size_t current = reordered_arr[i];
|
||||
if (is_sse[current] && i < SSES) {
|
||||
// If in SSE, retain index
|
||||
from[current] = i;
|
||||
}
|
||||
else if (is_gpr[current] && gprs < GPRS) {
|
||||
// If in GPR, offset by 4 (4 SSE registers available)
|
||||
from[current] = gprs + SSES;
|
||||
++gprs;
|
||||
}
|
||||
else {
|
||||
// If on stack, offset by 6 (4 SSE + 2 GPR registers available)
|
||||
from[current] = offset + SSES + GPRS;
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
|
||||
return from;
|
||||
}
|
||||
|
||||
// Grab only the stack values. For determining stack fixup.
|
||||
static constexpr auto filter_stack() {
|
||||
/* The size of our output may be shorter than the input.
|
||||
* Again, annoyingly, we must make a lambda to calculate this.
|
||||
*/
|
||||
constexpr auto arr_size = []() -> size_t {
|
||||
size_t size = length;
|
||||
|
||||
for (size_t i = 0, gprs = 0; i < length; ++i) {
|
||||
size_t current = reordered_arr[i];
|
||||
if (is_sse[current] && i < SSES) {
|
||||
--size;
|
||||
}
|
||||
else if (is_gpr[current] && gprs < GPRS) {
|
||||
--size;
|
||||
++gprs;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
};
|
||||
std::array<size_t, arr_size()> stack = {};
|
||||
|
||||
for (size_t in = 0, out = 0, gprs = 0; in < length; ++in) {
|
||||
size_t current = reordered_arr[in];
|
||||
if ((!is_sse[current] || in >= SSES) && (!is_gpr[current] || gprs >= GPRS)) {
|
||||
stack[out] = current;
|
||||
++out;
|
||||
}
|
||||
|
||||
if (is_gpr[current]) {
|
||||
++gprs;
|
||||
}
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
// Annoyingly, unless we're using C++20, we can't eliminate these intermediates. (afaik)
|
||||
static constexpr auto to_arr = filter_to();
|
||||
static constexpr auto from_arr = filter_from();
|
||||
static constexpr auto stack_arr = filter_stack();
|
||||
|
||||
public:
|
||||
using to = typename MyConv::template arr_to_seq<to_arr>;
|
||||
using from = typename MyConv::template arr_to_seq<from_arr>;
|
||||
using stack = typename MyConv::template arr_to_seq<stack_arr>;
|
||||
};
|
||||
|
||||
private:
|
||||
// Where all the logic is actually implemented.
|
||||
template <class Class, class, class>
|
||||
class Impl {
|
||||
static_assert(
|
||||
always_false<Class>,
|
||||
"Please report a bug to the Geode developers! This should never be reached.\n"
|
||||
"SFINAE didn't reach the right overload!"
|
||||
);
|
||||
};
|
||||
|
||||
template <size_t... to, size_t... from, size_t... stack>
|
||||
class Impl<
|
||||
std::index_sequence<to...>, std::index_sequence<from...>,
|
||||
std::index_sequence<stack...>> {
|
||||
private:
|
||||
static constexpr size_t fix = (std::is_class_v<Ret> ? stack_fix<Ret> : 0) +
|
||||
stack_fix<typename Tuple<Args...>::template type_at<stack>...>;
|
||||
|
||||
public:
|
||||
static Ret invoke(void* address, Tuple<Args..., float> const& all) {
|
||||
if constexpr (!std::is_same_v<Ret, void>) {
|
||||
Ret ret =
|
||||
reinterpret_cast<Ret(__vectorcall*)(decltype(all.template at<to>())...)>(
|
||||
address
|
||||
)(all.template at<to>()...);
|
||||
|
||||
if constexpr (fix != 0) {
|
||||
__asm add esp, [fix]
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
else {
|
||||
reinterpret_cast<Ret(__vectorcall*)(decltype(all.template at<to>())...)>(address
|
||||
)(all.template at<to>()...);
|
||||
|
||||
if constexpr (fix != 0) {
|
||||
__asm add esp, [fix]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <Ret (*detour)(Args...)>
|
||||
static Ret __cdecl wrapper(
|
||||
/* It's wrapped to stop MSVC from giving me error messages with internal compiler
|
||||
* info. WTF.
|
||||
*/
|
||||
typename Tuple<Args...>::template type_at_wrap<stack>... rest
|
||||
) {
|
||||
Register<double> f0, f1, f2, f3;
|
||||
Register<void*> i0, i1;
|
||||
|
||||
__asm {
|
||||
movlpd f0, xmm0
|
||||
movlpd f1, xmm1
|
||||
movlpd f2, xmm2
|
||||
movlpd f3, xmm3
|
||||
|
||||
mov i0, ecx
|
||||
mov i1, edx
|
||||
}
|
||||
|
||||
auto all = Tuple<>::make(f0, f1, f2, f3, i0, i1, rest...);
|
||||
|
||||
// Register<Type> has a explicit conversion operator we can take advantage of.
|
||||
return detour(static_cast<Args>(all.template at<from>())...);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// Putting it all together: instantiating Impl with our filters.
|
||||
using MyImpl =
|
||||
Impl<typename Sequences::to, typename Sequences::from, typename Sequences::stack>;
|
||||
|
||||
public:
|
||||
// Just wrapping MyImpl.
|
||||
static Ret invoke(void* address, Args... all) {
|
||||
/* The extra float here is so that we can grab placeholders.
|
||||
* If we don't have anything in XMM0 - 5, we will use
|
||||
* this placeholder instead. The value is 314 to avoid unintentional
|
||||
* bugs (since 0 may work coincidentally).
|
||||
*/
|
||||
return MyImpl::invoke(address, { all..., 314.0f });
|
||||
}
|
||||
|
||||
template <Ret (*detour)(Args...)>
|
||||
static auto get_wrapper() {
|
||||
return reinterpret_cast<void*>(&MyImpl::template wrapper<detour>);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Ret>
|
||||
class Optcall<Ret> : public Cdecl<Ret> {
|
||||
public:
|
||||
using Cdecl<Ret>::invoke;
|
||||
using Cdecl<Ret>::get_wrapper;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_OPTCALL_HPP */
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_STDCALL_HPP
|
||||
#define GEODE_CORE_META_STDCALL_HPP
|
||||
|
||||
namespace geode::core::meta::x86 {
|
||||
template <class Ret, class... Args>
|
||||
class Stdcall {
|
||||
private:
|
||||
template <Ret (*detour)(Args...)>
|
||||
static Ret __stdcall wrapper(Args... all) {
|
||||
return detour(all...);
|
||||
}
|
||||
|
||||
public:
|
||||
static Ret invoke(void* address, Args... all) {
|
||||
Ret(__stdcall * raw)(Args...) = reinterpret_cast<decltype(raw)>(address);
|
||||
return raw(all...);
|
||||
}
|
||||
|
||||
template <Ret (*detour)(Args...)>
|
||||
static auto get_wrapper() {
|
||||
return reinterpret_cast<void*>(&wrapper<detour>);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_STDCALL_HPP */
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_THISCALL_HPP
|
||||
#define GEODE_CORE_META_THISCALL_HPP
|
||||
|
||||
namespace geode::core::meta::x86 {
|
||||
template <class Ret, class... Args>
|
||||
class Thiscall {
|
||||
private:
|
||||
template <Ret (*detour)(Args...)>
|
||||
static Ret __thiscall wrapper(Args... all) {
|
||||
return detour(all...);
|
||||
}
|
||||
|
||||
public:
|
||||
static Ret invoke(void* address, Args... all) {
|
||||
Ret(__thiscall * raw)(Args...) = reinterpret_cast<decltype(raw)>(address);
|
||||
return raw(all...);
|
||||
}
|
||||
|
||||
template <Ret (*detour)(Args...)>
|
||||
static auto get_wrapper() {
|
||||
return reinterpret_cast<void*>(&wrapper<detour>);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_THISCALL_HPP */
|
|
@ -1,99 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_TUPLE_HPP
|
||||
#define GEODE_CORE_META_TUPLE_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace geode::core::meta {
|
||||
/* The Tuple class presents a nicer way to interact with parameter packs.
|
||||
* While this container is technically usable in other code, this is mostly
|
||||
* used for static reflection in function wrapping. Other applications will
|
||||
* usually be better covered by std::tuple.
|
||||
*/
|
||||
template <class...>
|
||||
class Tuple;
|
||||
|
||||
template <>
|
||||
class Tuple<> {
|
||||
private:
|
||||
template <class Type, size_t i>
|
||||
class Element {
|
||||
private:
|
||||
Type value;
|
||||
|
||||
protected:
|
||||
constexpr Type at(std::integral_constant<size_t, i>&&) const {
|
||||
return this->value;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr Element(Type value) : value(value) {}
|
||||
};
|
||||
|
||||
template <class... Parents>
|
||||
class Elements : public Parents... {
|
||||
private:
|
||||
using Parents::at...;
|
||||
|
||||
public:
|
||||
static constexpr size_t size = sizeof...(Parents);
|
||||
|
||||
template <size_t i>
|
||||
constexpr decltype(auto) at() const {
|
||||
static_assert(i < size, "Out of range access!");
|
||||
return this->at(std::integral_constant<size_t, i>());
|
||||
}
|
||||
};
|
||||
|
||||
template <class, class...>
|
||||
class elements_for_impl;
|
||||
|
||||
template <size_t... indices, class... Classes>
|
||||
class elements_for_impl<std::index_sequence<indices...>, Classes...> {
|
||||
public:
|
||||
using result = Elements<Element<Classes, indices>...>;
|
||||
};
|
||||
|
||||
public:
|
||||
template <class... Classes>
|
||||
using elements_for = typename elements_for_impl<
|
||||
std::make_index_sequence<sizeof...(Classes)>, Classes...>::result;
|
||||
|
||||
template <class... Classes>
|
||||
static auto make(Classes&&... values) {
|
||||
return Tuple<Classes...> { values... };
|
||||
}
|
||||
};
|
||||
|
||||
template <class Current, class... Rest>
|
||||
class Tuple<Current, Rest...> : public Tuple<>::elements_for<Current, Rest...> {
|
||||
private:
|
||||
using MyElements = Tuple<>::elements_for<Current, Rest...>;
|
||||
|
||||
public:
|
||||
template <size_t i>
|
||||
using type_at = decltype(std::declval<MyElements>().template at<i>());
|
||||
|
||||
private:
|
||||
template <size_t i, bool>
|
||||
class type_at_wrap_impl {
|
||||
public:
|
||||
using result = void;
|
||||
};
|
||||
|
||||
template <size_t i>
|
||||
class type_at_wrap_impl<i, true> {
|
||||
public:
|
||||
using result = type_at<i>;
|
||||
};
|
||||
|
||||
public:
|
||||
// MSVC literally shows internal compiler structures if I don't wrap this sometimes.
|
||||
template <size_t i>
|
||||
using type_at_wrap = typename type_at_wrap_impl<i, (i < sizeof...(Rest) + 1)>::result;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_TUPLE_HPP */
|
|
@ -1,114 +0,0 @@
|
|||
#ifndef GEODE_CORE_META_X86_HPP
|
||||
#define GEODE_CORE_META_X86_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace geode::core::meta::x86 {
|
||||
// Logic needed by x86 calling conventions for stack fixing / filtering.
|
||||
template <class Class>
|
||||
static constexpr bool sse_passable = any_of<std::remove_cv_t<Class>, float, double>;
|
||||
|
||||
template <class Class>
|
||||
static constexpr bool gpr_passable =
|
||||
((sizeof(Class) <= sizeof(void*) && !std::is_class_v<Class> && !sse_passable<Class>) ||
|
||||
std::is_member_function_pointer_v<Class>);
|
||||
|
||||
template <class... Classes>
|
||||
static inline constexpr std::array<size_t, sizeof...(Classes)> reorder_pack() {
|
||||
constexpr size_t length = sizeof...(Classes);
|
||||
constexpr bool should_reorder[] = { std::is_class_v<Classes> &&
|
||||
sizeof(Classes) > sizeof(void*)... };
|
||||
|
||||
std::array<size_t, length> reordered = {};
|
||||
size_t out = 0;
|
||||
// Non-reordered go first.
|
||||
for (size_t in = 0; in < length; ++in) {
|
||||
if (!should_reorder[in]) {
|
||||
reordered[out] = in;
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
// Reordered go last.
|
||||
for (size_t in = 0; in < length; ++in) {
|
||||
if (should_reorder[in]) {
|
||||
reordered[out] = in;
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
return reordered;
|
||||
}
|
||||
|
||||
template <class... Stack>
|
||||
static constexpr size_t stack_fix =
|
||||
(((sizeof(Stack) % sizeof(void*) == 0)
|
||||
? sizeof(Stack)
|
||||
: sizeof(Stack) - (sizeof(Stack) % sizeof(void*)) + sizeof(void*)) +
|
||||
...);
|
||||
|
||||
template <>
|
||||
static constexpr size_t stack_fix<> = 0;
|
||||
|
||||
template <>
|
||||
static constexpr size_t stack_fix<void> = 0;
|
||||
|
||||
template <class From>
|
||||
class Register {
|
||||
public:
|
||||
From raw;
|
||||
|
||||
public:
|
||||
template <class To>
|
||||
explicit operator To() {
|
||||
static_assert(
|
||||
sizeof(From) >= sizeof(To),
|
||||
"Please report a bug to the Geode developers! This should never be reached.\n"
|
||||
"Size of Register is smaller than the size of the destination type!"
|
||||
);
|
||||
|
||||
union {
|
||||
From from;
|
||||
To to;
|
||||
} u;
|
||||
|
||||
u.from = raw;
|
||||
return u.to;
|
||||
}
|
||||
};
|
||||
|
||||
// Ignore this for now, it's for discarding calling convention qualifier later.
|
||||
#if 0
|
||||
template <class Func>
|
||||
class remove_conv {
|
||||
public:
|
||||
using result = Func;
|
||||
};
|
||||
|
||||
// We all hate macros but I'd say this is a good use case.
|
||||
#define REMOVE_FOR(CC) \
|
||||
template <class Ret, class... Args> \
|
||||
class remove_conv<Ret(CC*)(Args...)> { \
|
||||
public: \
|
||||
using result = Ret (*)(Args...); \
|
||||
}; \
|
||||
\
|
||||
template <class Ret, class Parent, class... Args> \
|
||||
class remove_conv<Ret (CC Parent::*)(Args...)> { \
|
||||
public: \
|
||||
using result = Ret (Parent::*)(Args...); \
|
||||
}
|
||||
|
||||
REMOVE_FOR(__cdecl);
|
||||
REMOVE_FOR(__stdcall);
|
||||
REMOVE_FOR(__thiscall);
|
||||
REMOVE_FOR(__fastcall);
|
||||
REMOVE_FOR(__vectorcall);
|
||||
|
||||
#undef REMOVE_FOR
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* GEODE_CORE_META_X86_HPP */
|
|
@ -1,5 +1,4 @@
|
|||
#pragma once
|
||||
#include "../meta/meta.hpp"
|
||||
#include "AsStaticFunction.hpp"
|
||||
#include "Field.hpp"
|
||||
#include "IDManager.hpp"
|
||||
|
@ -96,7 +95,7 @@ namespace geode::modifier {
|
|||
class ModifyDerive {
|
||||
public:
|
||||
ModifyDerive() {
|
||||
static_assert(core::meta::always_false<Derived>, "Custom Modify not implemented.");
|
||||
static_assert(alwaysFalse<Derived>, "Custom Modify not implemented.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -27,6 +27,9 @@ namespace geode::modifier {
|
|||
using type = FunctionType*;
|
||||
};
|
||||
|
||||
template <class...>
|
||||
static constexpr bool alwaysFalse = false;
|
||||
|
||||
/**
|
||||
* The ~unevaluated~ function that gets the appropriate
|
||||
* version of a function type from its return, parameters, and classes.
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
#pragma warning(disable : 4251) // dll-interface
|
||||
#pragma warning(disable : 4244) // narrowing conversion
|
||||
|
||||
#include <Geode/meta/cdecl.hpp>
|
||||
#include <Geode/meta/function.hpp>
|
||||
#include <Geode/meta/membercall.hpp>
|
||||
#include <Geode/meta/optcall.hpp>
|
||||
#include <Geode/meta/thiscall.hpp>
|
||||
#include <Windows.h>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
|
|
@ -1,189 +0,0 @@
|
|||
#include "Core.hpp"
|
||||
|
||||
#include <map>
|
||||
|
||||
#ifndef GEODE_IS_WINDOWS
|
||||
|
||||
#if defined(GEODE_IS_MACOS)
|
||||
#include "../platform/mac/Core.hpp"
|
||||
#elif defined(GEODE_IS_IOS)
|
||||
// #include "iOS.hpp"
|
||||
#endif
|
||||
|
||||
namespace geode::core::impl {
|
||||
namespace {
|
||||
auto& originalBytes() {
|
||||
static std::map<void*, std::vector<std::byte>*> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto& unhandledTrampolines() {
|
||||
static std::map<void*, void*> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto& unhandledHandlers() {
|
||||
static std::map<void*, void*> ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void* generateRawTrampoline(void* address) {
|
||||
static constexpr size_t MAX_TRAMPOLINE_SIZE = 0x40;
|
||||
auto trampoline = TargetPlatform::allocateVM(MAX_TRAMPOLINE_SIZE);
|
||||
unhandledTrampolines()[address] = trampoline;
|
||||
|
||||
// we need to add a trap instruction to the trampoline
|
||||
// so that it will go to handleContext
|
||||
const size_t trapSize = TargetPlatform::getTrapSize();
|
||||
TargetPlatform::writeMemory(trampoline, (void*)TargetPlatform::getTrap().data(), trapSize);
|
||||
return trampoline;
|
||||
}
|
||||
|
||||
void addJump(void* at, void* to) {
|
||||
if (unhandledTrampolines().find(at) != unhandledTrampolines().end()) {
|
||||
// we havent set up the trampoline yet, so this means
|
||||
// we need to preserve the original bytes to use when
|
||||
// populating the trampoline. we need to save the handler
|
||||
// to set a jump to it when we finish the trampoline too.
|
||||
|
||||
const size_t jumpSize = TargetPlatform::getJumpSize(at, to);
|
||||
|
||||
originalBytes().insert({ at, new std::vector<std::byte>(jumpSize, std::byte(0x00)) });
|
||||
TargetPlatform::writeMemory((void*)originalBytes()[at]->data(), at, jumpSize);
|
||||
|
||||
unhandledHandlers()[at] = to;
|
||||
}
|
||||
TargetPlatform::writeMemory(
|
||||
at, (void*)TargetPlatform::getJump(at, to).data(), TargetPlatform::getJumpSize(at, to)
|
||||
);
|
||||
}
|
||||
|
||||
void handleContext(void* context, void* current) {
|
||||
static thread_local void* original = nullptr;
|
||||
static thread_local bool originalFlag = false;
|
||||
|
||||
for (auto& [k, v] : unhandledTrampolines()) {
|
||||
if (v == current) {
|
||||
// we are inside a trampoline, which means we need
|
||||
// to jump to the original function and add a trap
|
||||
// instruction to the original function to mark the
|
||||
// beginning of the instruction measuring
|
||||
|
||||
const size_t jumpSize = TargetPlatform::getJumpSize(current, k);
|
||||
TargetPlatform::writeMemory(
|
||||
current, (void*)TargetPlatform::getJump(current, k).data(), jumpSize
|
||||
);
|
||||
|
||||
const size_t trapSize = TargetPlatform::getTrapSize();
|
||||
TargetPlatform::writeMemory(k, (void*)TargetPlatform::getTrap().data(), trapSize);
|
||||
|
||||
originalFlag = true;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (originalFlag) {
|
||||
// we are at the beginning of the original function,
|
||||
// which means we need to put back the original
|
||||
// instructions and enable single stepping to
|
||||
// come back to here every instruction
|
||||
//
|
||||
// we can get rid of the original bytes vector since
|
||||
// we already recovered it
|
||||
|
||||
original = current;
|
||||
originalFlag = false;
|
||||
|
||||
auto origBytes = originalBytes()[original];
|
||||
TargetPlatform::writeMemory(original, (void*)origBytes->data(), origBytes->size());
|
||||
delete origBytes;
|
||||
originalBytes().erase(original);
|
||||
|
||||
TargetPlatform::enableSingleStep(context);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
// we are at an instruction somewhere after the
|
||||
// beginning of the original function, which
|
||||
// means we need to check if we can fit a jump
|
||||
// instruction
|
||||
auto trampoline = unhandledTrampolines()[original];
|
||||
|
||||
const size_t jumpSize =
|
||||
TargetPlatform::getJumpSize(trampoline, unhandledHandlers()[original]);
|
||||
const size_t difference = (size_t)current - (size_t)original;
|
||||
|
||||
if (difference >= jumpSize) {
|
||||
// we have enough space to fit a jump from the
|
||||
// beginning of the function to the handler
|
||||
// now we can copy the instructions we have
|
||||
// measured to the trampoline, add a jump back
|
||||
// at the end of it. and also put the jump from
|
||||
// the original function to the handler.
|
||||
// now that we are done, we can disable single
|
||||
// stepping
|
||||
|
||||
TargetPlatform::writeMemory(trampoline, original, difference);
|
||||
TargetPlatform::writeMemory(
|
||||
(void*)((size_t)trampoline + difference),
|
||||
(void*)TargetPlatform::getJump(
|
||||
(void*)((size_t)trampoline + difference),
|
||||
(void*)((size_t)original + difference)
|
||||
)
|
||||
.data(),
|
||||
difference
|
||||
);
|
||||
|
||||
TargetPlatform::writeMemory(
|
||||
original,
|
||||
(void*)TargetPlatform::getJump(original, unhandledHandlers()[original]).data(),
|
||||
TargetPlatform::getJumpSize(original, unhandledHandlers()[original])
|
||||
);
|
||||
|
||||
TargetPlatform::disableSingleStep(context);
|
||||
|
||||
unhandledTrampolines().erase(original);
|
||||
unhandledHandlers().erase(original);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool geode::core::hook::initialize() {
|
||||
return geode::core::impl::TargetPlatform::initialize();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// #include <dobby.h>
|
||||
#include <MinHook.h>
|
||||
|
||||
namespace geode::core::impl {
|
||||
namespace {
|
||||
auto& trampolines() {
|
||||
static std::map<void*, void*> ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void* generateRawTrampoline(void* address) {
|
||||
return trampolines()[address];
|
||||
}
|
||||
|
||||
void addJump(void* at, void* to) {
|
||||
// DobbyDestroy(at);
|
||||
// DobbyHook(at, to, &trampolines()[at]);
|
||||
|
||||
MH_RemoveHook(at);
|
||||
MH_CreateHook(at, to, &trampolines()[at]);
|
||||
MH_EnableHook(at);
|
||||
}
|
||||
}
|
||||
|
||||
bool geode::core::hook::initialize() {
|
||||
return MH_Initialize() == MH_OK;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,19 +0,0 @@
|
|||
#pragma once
|
||||
#include "Platform.hpp"
|
||||
|
||||
/*
|
||||
Internal use functions
|
||||
*/
|
||||
namespace geode::core {
|
||||
namespace impl {
|
||||
void* generateRawTrampoline(void* address);
|
||||
|
||||
void addJump(void* at, void* to);
|
||||
|
||||
void handleContext(void* context, void* current);
|
||||
}
|
||||
|
||||
namespace hook {
|
||||
bool initialize();
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
#include "Core.hpp"
|
||||
|
||||
#include <Geode/hook-core/Hook.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace geode::core::impl {
|
||||
namespace {
|
||||
inline auto& generatedTrampolines() {
|
||||
static std::unordered_map<void*, void*> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline auto& currentHandlers() {
|
||||
static std::unordered_map<void*, void*> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline auto& mappedTrampolines() {
|
||||
static std::unordered_map<void*, VectorPointer> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline auto& mappedHandlers() {
|
||||
static std::unordered_map<void*, VectorPointer> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline auto& mappedDetours() {
|
||||
static std::unordered_map<void*, VectorPointer> ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void addHook(
|
||||
void* address, void* detour, VectorPointer* detourVectorAddress, void* generatedHandler,
|
||||
void** originalTrampolineAddress, void* generatedTrampoline
|
||||
) {
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
if (mappedHandlers().find(address) == mappedHandlers().end()) {
|
||||
// std::cout << "allocate handler vector for " << address << std::endl;
|
||||
mappedHandlers().insert({ address, new std::vector<void*> });
|
||||
currentHandlers()[address] = generatedHandler;
|
||||
addJump(address, generatedHandler);
|
||||
}
|
||||
mappedHandlers()[address]->push_back(generatedHandler);
|
||||
#endif
|
||||
if (mappedTrampolines().find(address) == mappedTrampolines().end()) {
|
||||
// std::cout << "allocate trampoline vector for " << address << std::endl;
|
||||
mappedTrampolines().insert({ address, new std::vector<void*> });
|
||||
if (generatedTrampolines().find(address) == generatedTrampolines().end()) {
|
||||
generatedTrampolines()[address] = generateRawTrampoline(address);
|
||||
}
|
||||
}
|
||||
auto puretramp = generatedTrampolines()[address];
|
||||
mappedTrampolines()[address]->push_back(generatedTrampoline);
|
||||
*originalTrampolineAddress = puretramp;
|
||||
|
||||
#ifndef GEODE_IS_WINDOWS
|
||||
if (mappedHandlers().find(address) == mappedHandlers().end()) {
|
||||
// std::cout << "allocate handler vector for " << address << std::endl;
|
||||
mappedHandlers().insert({ address, new std::vector<void*> });
|
||||
currentHandlers()[address] = generatedHandler;
|
||||
addJump(address, generatedHandler);
|
||||
}
|
||||
mappedHandlers()[address]->push_back(generatedHandler);
|
||||
#endif
|
||||
|
||||
if (mappedDetours().find(address) == mappedDetours().end()) {
|
||||
// std::cout << "allocate detour vector for " << address << std::endl;
|
||||
mappedDetours().insert({ address, new std::vector<void*> });
|
||||
}
|
||||
auto detours = mappedDetours().at(address);
|
||||
*detourVectorAddress = detours;
|
||||
|
||||
if (detours->size() == 0) {
|
||||
// std::cout << "add trampoline to the detour vector (it will not get deallocated now)"
|
||||
// << std::endl;
|
||||
detours->push_back(mappedTrampolines()[address]->front());
|
||||
}
|
||||
detours->insert(detours->end() - 1, detour);
|
||||
}
|
||||
|
||||
void removeHook(HookHandle const& handle) {
|
||||
auto [handler, address, detour, trampoline] = handle;
|
||||
|
||||
auto detours = mappedDetours().at(address);
|
||||
detours->erase(std::remove(detours->begin(), detours->end(), detour), detours->end());
|
||||
|
||||
auto handlers = mappedHandlers().at(address);
|
||||
handlers->erase(std::remove(handlers->begin(), handlers->end(), handler), handlers->end());
|
||||
|
||||
auto trampolines = mappedTrampolines().at(address);
|
||||
trampolines->erase(
|
||||
std::remove(trampolines->begin(), trampolines->end(), trampoline), trampolines->end()
|
||||
);
|
||||
|
||||
if (handlers->size() == 0 /* trampolines size should be 0 now */) {
|
||||
// std::cout << "deallocate handler vector for " << address << std::endl;
|
||||
delete handlers;
|
||||
mappedHandlers().erase(address);
|
||||
|
||||
// std::cout << "deallocate trampoline vector for " << address << std::endl;
|
||||
delete trampolines;
|
||||
mappedTrampolines().erase(address);
|
||||
|
||||
// TODO: bandaid
|
||||
// addJump(address, generatedTrampolines()[address]);
|
||||
// afterHookHander = (decltype(afterHookHander))generatedTrampoline[address]; // switch
|
||||
// the trampoline (inline)
|
||||
}
|
||||
else if (currentHandlers()[address] == handler) {
|
||||
// std::cout << "using a different trampoline and handler for " << address << std::endl;
|
||||
detours->back() = trampolines->front();
|
||||
|
||||
currentHandlers()[address] = handlers->front();
|
||||
addJump(address, handlers->front());
|
||||
// afterHookHander = (decltype(afterHookHander))(handlers->front()); // switch the
|
||||
// handler (inline)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/platform/platform.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace geode::core::impl {
|
||||
template <typename T>
|
||||
class Platform {
|
||||
public:
|
||||
static const std::vector<std::byte> getTrap() {
|
||||
return T::trap;
|
||||
}
|
||||
|
||||
static const size_t getTrapSize() {
|
||||
return T::trap.size();
|
||||
}
|
||||
|
||||
static const std::vector<std::byte> getJump(void* from, void* to) {
|
||||
// static_assert(&Platform<T>::jump != T::jump, "implement jump");
|
||||
return T::jump(from, to);
|
||||
}
|
||||
|
||||
static const size_t getJumpSize(void* from, void* to) {
|
||||
// static_assert(&Platform<T>::jump != T::jump, "implement trap");
|
||||
return T::jump(from, to).size();
|
||||
}
|
||||
|
||||
static void* allocateVM(size_t size) {
|
||||
// static_assert(&Platform<T>::allocateVM != T::allocateVM, "implement allocateVM");
|
||||
return T::allocateVM(size);
|
||||
}
|
||||
|
||||
static bool writeMemory(void* to, void* from, size_t size) {
|
||||
static_assert(&Platform<T>::writeMemory != &T::writeMemory, "implement writeMemory");
|
||||
return T::writeMemory(to, from, size);
|
||||
}
|
||||
|
||||
// static bool readMemory(const void* addr, const void* to, const size_t size) {
|
||||
// static_assert(&Platform<T>::read_memory != &T::read_memory, "implement readMemory");
|
||||
// return T::readMemory(addr, to, size);
|
||||
// }
|
||||
|
||||
static bool initialize() {
|
||||
static_assert(&Platform<T>::initialize != &T::initialize, "implement initialize");
|
||||
return T::initialize();
|
||||
}
|
||||
|
||||
static bool enableSingleStep(void* context) {
|
||||
static_assert(
|
||||
&Platform<T>::enableSingleStep != &T::enableSingleStep, "implement enableSingleStep"
|
||||
);
|
||||
return T::enableSingleStep(context);
|
||||
}
|
||||
|
||||
static bool disableSingleStep(void* context) {
|
||||
static_assert(
|
||||
&Platform<T>::disableSingleStep != &T::disableSingleStep,
|
||||
"implement disableSingleStep"
|
||||
);
|
||||
return T::disableSingleStep(context);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -4,10 +4,7 @@
|
|||
#include <Geode/utils/casts.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
#include <vector>
|
||||
// #include <hook/hook.hpp>
|
||||
#include "ModImpl.hpp"
|
||||
|
||||
#include <Geode/hook-core/Hook.hpp>
|
||||
#include "HookImpl.hpp"
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
|
|
@ -4,11 +4,8 @@
|
|||
#include <Geode/utils/casts.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
#include <vector>
|
||||
// #include <hook/hook.hpp>
|
||||
#include "ModImpl.hpp"
|
||||
|
||||
#include <Geode/hook-core/Hook.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
class Hook::Impl {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#include "../core/Core.hpp"
|
||||
#include "loader/LoaderImpl.hpp"
|
||||
|
||||
#include <Geode/loader/IPC.hpp>
|
||||
|
@ -7,7 +6,6 @@
|
|||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/loader/Setting.hpp>
|
||||
#include <Geode/loader/SettingEvent.hpp>
|
||||
#include <loader/ModImpl.hpp>
|
||||
#include <Geode/loader/ModJsonTest.hpp>
|
||||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <array>
|
||||
|
@ -152,17 +150,6 @@ $execute {
|
|||
}
|
||||
|
||||
int geodeEntry(void* platformData) {
|
||||
// setup internals
|
||||
|
||||
if (!geode::core::hook::initialize()) {
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
"Unable to load Geode!",
|
||||
"There was an unknown fatal error setting up "
|
||||
"internal tools and Geode can not be loaded. "
|
||||
"(Unable to set up hook manager)"
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// set up internal mod, settings and data
|
||||
auto internalSetupRes = LoaderImpl::get()->setupInternalMod();
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
|
||||
#include "Core.hpp"
|
||||
#include "../../core/Core.hpp"
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_MACOS
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_init.h> /* mach_task_self() */
|
||||
#include <mach/mach_port.h>
|
||||
#include <mach/mach_vm.h> /* mach_vm_* */
|
||||
#include <mach/task.h>
|
||||
#include <signal.h> /* sigaction */
|
||||
#include <sys/ucontext.h> /* ucontext_t */
|
||||
|
||||
using namespace geode::core::hook;
|
||||
using namespace geode::core::impl;
|
||||
|
||||
namespace {
|
||||
void signalHandler(int signal, siginfo_t* signal_info, void* vcontext) {
|
||||
auto context = reinterpret_cast<ucontext_t*>(vcontext);
|
||||
|
||||
auto current = reinterpret_cast<void*>(context->uc_mcontext->__ss.__rip);
|
||||
|
||||
handleContext(vcontext, current);
|
||||
}
|
||||
}
|
||||
|
||||
bool MacOSX::enableSingleStep(void* vcontext) {
|
||||
auto context = reinterpret_cast<ucontext_t*>(vcontext);
|
||||
context->uc_mcontext->__ss.__rflags |= ((unsigned long)0x100);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacOSX::disableSingleStep(void* vcontext) {
|
||||
auto context = reinterpret_cast<ucontext_t*>(vcontext);
|
||||
context->uc_mcontext->__ss.__rflags &= ~((unsigned long)0x100);
|
||||
return true;
|
||||
}
|
||||
|
||||
void* MacOSX::allocateVM(size_t size) {
|
||||
mach_vm_address_t ret;
|
||||
|
||||
kern_return_t status; // return status
|
||||
|
||||
status = mach_vm_allocate(mach_task_self(), &ret, (mach_vm_size_t)size, VM_FLAGS_ANYWHERE);
|
||||
|
||||
return (void*)ret;
|
||||
}
|
||||
|
||||
std::vector<std::byte> MacOSX::jump(void* from, void* to) {
|
||||
constexpr size_t size = sizeof(int) + 1;
|
||||
std::vector<std::byte> ret(size);
|
||||
ret[0] = { 0xe9 };
|
||||
|
||||
int offset = (int)((size_t)to - (size_t)from - size);
|
||||
// im too lazy
|
||||
((int*)((size_t)ret.data() + 1))[0] = offset;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MacOSX::writeMemory(void* to, void* from, size_t size) {
|
||||
kern_return_t status; // return status
|
||||
|
||||
mach_vm_size_t vmsize;
|
||||
mach_vm_address_t address = (mach_vm_address_t)to;
|
||||
vm_region_basic_info_data_t info;
|
||||
mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
|
||||
mach_port_t object;
|
||||
|
||||
// std::cout << "get memory protection" << std::endl;
|
||||
// // get memory protection
|
||||
// status = mach_vm_region(mach_task_self(), &address, &vmsize, VM_REGION_BASIC_INFO,
|
||||
// (vm_region_info_t)&info, &info_count, &object); std::cout << status << std::endl; if (status
|
||||
// != KERN_SUCCESS) return false;
|
||||
|
||||
// std::cout << "set to write protection" << std::endl;
|
||||
// set to write protection
|
||||
status = mach_vm_protect(
|
||||
mach_task_self(), (mach_vm_address_t)to, size, FALSE,
|
||||
VM_PROT_COPY | VM_PROT_EXECUTE | VM_PROT_WRITE | VM_PROT_READ
|
||||
);
|
||||
if (status != KERN_SUCCESS) return false;
|
||||
|
||||
// std::cout << "write to memory" << std::endl;
|
||||
// write to memory
|
||||
status = mach_vm_write(
|
||||
mach_task_self(), (mach_vm_address_t)to, (vm_offset_t)from, (mach_msg_type_number_t)size
|
||||
);
|
||||
if (status != KERN_SUCCESS) return false;
|
||||
|
||||
// std::cout << "revert to old protection" << std::endl;
|
||||
// // revert to old protection
|
||||
// status = mach_vm_protect(mach_task_self(), (mach_vm_address_t)to, size, FALSE,
|
||||
// info.protection); if (status != KERN_SUCCESS) return false;
|
||||
|
||||
return status == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
bool MacOSX::initialize() {
|
||||
task_set_exception_ports(
|
||||
mach_task_self(), EXC_MASK_BAD_INSTRUCTION,
|
||||
MACH_PORT_NULL, // m_exception_port,
|
||||
EXCEPTION_DEFAULT, 0
|
||||
);
|
||||
// first reached here
|
||||
struct sigaction action = {};
|
||||
action.sa_sigaction = &signalHandler;
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
|
||||
return sigaction(SIGILL, &action, NULL) == 0 && sigaction(SIGTRAP, &action, NULL) == 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,21 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../core/Platform.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace geode::core::impl {
|
||||
class MacOSX : public Platform<MacOSX> {
|
||||
public:
|
||||
static inline auto trap = { std::byte(0x0f), std::byte(0x0b) };
|
||||
|
||||
static bool writeMemory(void* to, void* from, size_t size);
|
||||
static std::vector<std::byte> jump(void* from, void* to);
|
||||
static bool initialize();
|
||||
static bool enableSingleStep(void* context);
|
||||
static bool disableSingleStep(void* context);
|
||||
static void* allocateVM(size_t size);
|
||||
};
|
||||
|
||||
using TargetPlatform = Platform<MacOSX>;
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
|
||||
#include "Core.hpp"
|
||||
#include "../../core/Core.hpp"
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
using namespace geode::core::hook;
|
||||
using namespace geode::core::impl;
|
||||
|
||||
namespace {
|
||||
LONG WINAPI signalHandler(EXCEPTION_POINTERS* info) {
|
||||
#if defined(_WIN64)
|
||||
auto current = reinterpret_cast<void*>(info->ContextRecord->Rip);
|
||||
#elif defined(_WIN32)
|
||||
auto current = reinterpret_cast<void*>(info->ContextRecord->Eip);
|
||||
#endif
|
||||
|
||||
// handleContext(reinterpret_cast<void*>(info), current);
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
}
|
||||
|
||||
void* Windows::allocateVM(size_t size) {
|
||||
return VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||
}
|
||||
|
||||
std::vector<std::byte> Windows::jump(void* from, void* to) {
|
||||
std::vector<std::byte> ret;
|
||||
ret.resize(5, std::byte(0u));
|
||||
ret[0] = std::byte(0xe9);
|
||||
|
||||
// target - src - 5
|
||||
|
||||
intptr_t offset = reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from) - 5;
|
||||
std::byte data[4];
|
||||
std::memcpy(data, &offset, 4);
|
||||
ret[1] = data[0];
|
||||
ret[2] = data[1];
|
||||
ret[3] = data[2];
|
||||
ret[4] = data[3];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Windows::enableSingleStep(void* vcontext) {
|
||||
auto info = reinterpret_cast<EXCEPTION_POINTERS*>(vcontext);
|
||||
#if defined(_WIN64)
|
||||
info->ContextRecord->RFlags |= ((QWORD)0x100);
|
||||
#elif defined(_WIN32)
|
||||
info->ContextRecord->EFlags |= ((DWORD)0x100);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Windows::disableSingleStep(void* vcontext) {
|
||||
auto info = reinterpret_cast<EXCEPTION_POINTERS*>(vcontext);
|
||||
#if defined(_WIN64)
|
||||
info->ContextRecord->RFlags &= ~((QWORD)0x100);
|
||||
#elif defined(_WIN32)
|
||||
info->ContextRecord->EFlags &= ~((DWORD)0x100);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Windows::writeMemory(void* to, void* from, size_t size) {
|
||||
DWORD old;
|
||||
VirtualProtect(to, size, PAGE_EXECUTE_READWRITE, &old);
|
||||
auto res = WriteProcessMemory(GetCurrentProcess(), to, from, size, nullptr);
|
||||
VirtualProtect(to, size, old, &old);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Windows::initialize() {
|
||||
// return AddVectoredExceptionHandler(true, signalHandler);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,26 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../core/Platform.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace geode::core::impl {
|
||||
class Windows : public Platform<Windows> {
|
||||
public:
|
||||
#if defined(NDEBUG)
|
||||
static inline auto trap = { std::byte(0xcc) };
|
||||
#else
|
||||
static inline auto trap = { std::byte(0x0f), std::byte(0x0b) };
|
||||
#endif
|
||||
|
||||
public:
|
||||
static bool writeMemory(void* to, void* from, size_t size);
|
||||
static std::vector<std::byte> jump(void* from, void* to);
|
||||
static bool initialize();
|
||||
static bool enableSingleStep(void* context);
|
||||
static bool disableSingleStep(void* context);
|
||||
static void* allocateVM(size_t size);
|
||||
};
|
||||
|
||||
using TargetPlatform = Platform<Windows>;
|
||||
}
|
Loading…
Reference in a new issue