mirror of
https://github.com/Mr-X-GTA/YimMenu.git
synced 2024-12-22 20:17:24 +08:00
feat: dynamicly calling x64 functions from lua at runtime with arbitrary signatures. (#3311)
This commit is contained in:
parent
80b7c90efd
commit
0b67accd30
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Table containing helper functions related to process memory.
|
Table containing helper functions related to process memory.
|
||||||
|
|
||||||
## Functions (6)
|
## Functions (7)
|
||||||
|
|
||||||
### `scan_pattern(pattern)`
|
### `scan_pattern(pattern)`
|
||||||
|
|
||||||
@ -73,6 +73,7 @@ memory.free(ptr)
|
|||||||
**Example Usage:**
|
**Example Usage:**
|
||||||
```lua
|
```lua
|
||||||
local ptr = memory.scan_pattern("some ida sig")
|
local ptr = memory.scan_pattern("some ida sig")
|
||||||
|
-- Check the implementation of the asmjit::TypeId get_type_id function if you are unsure what to use for return type / parameters types
|
||||||
memory.dynamic_hook("test_hook", "float", {"const char*"}, ptr,
|
memory.dynamic_hook("test_hook", "float", {"const char*"}, ptr,
|
||||||
function(ret_val, str)
|
function(ret_val, str)
|
||||||
|
|
||||||
@ -103,4 +104,43 @@ end)
|
|||||||
memory.dynamic_hook(hook_name, return_type, param_types, target_func_ptr, pre_callback, post_callback)
|
memory.dynamic_hook(hook_name, return_type, param_types, target_func_ptr, pre_callback, post_callback)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `dynamic_call(return_type, param_types, target_func_ptr)`
|
||||||
|
|
||||||
|
**Example Usage:**
|
||||||
|
```lua
|
||||||
|
-- the sig in this example leads to an implementation of memcpy_s
|
||||||
|
local ptr = memory.scan_pattern("48 89 5C 24 08 48 89 74 24 10 57 48 83 EC 20 49 8B D9 49 8B F0 48 8B FA")
|
||||||
|
if ptr:is_valid() then
|
||||||
|
local dest_size = 8
|
||||||
|
local dest_ptr = memory.allocate(dest_size)
|
||||||
|
dest_ptr:set_qword(0)
|
||||||
|
|
||||||
|
local src_size = 8
|
||||||
|
local src_ptr = memory.allocate(src_size)
|
||||||
|
src_ptr:set_qword(123)
|
||||||
|
|
||||||
|
-- Check the implementation of the asmjit::TypeId get_type_id function if you are unsure what to use for return type / parameters types
|
||||||
|
local func_to_call_test_global_name = memory.dynamic_call("int", {"void*", "uint64_t", "void*", "uint64_t"}, ptr)
|
||||||
|
-- print zero.
|
||||||
|
log.info(dest_ptr:get_qword())
|
||||||
|
-- note: don't pass memory.pointer objects directly when you call the function, but use get_address() instead.
|
||||||
|
local call_res_test = _G[func_to_call_test_global_name](dest_ptr:get_address(), dest_size, src_ptr:get_address(), src_size)
|
||||||
|
-- print 123.
|
||||||
|
log.info(dest_ptr:get_qword())
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Parameters:**
|
||||||
|
- `return_type` (string): Type of the return value of the function to call.
|
||||||
|
- `param_types` (table<string>): Types of the parameters of the function to call.
|
||||||
|
- `target_func_ptr` (memory.pointer): The pointer to the function to call.
|
||||||
|
|
||||||
|
- **Returns:**
|
||||||
|
- `string`: Key name of the function that you can now call from lua.
|
||||||
|
|
||||||
|
**Example Usage:**
|
||||||
|
```lua
|
||||||
|
string = memory.dynamic_call(return_type, param_types, target_func_ptr)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -216,6 +216,7 @@ namespace lua::memory
|
|||||||
// **Example Usage:**
|
// **Example Usage:**
|
||||||
// ```lua
|
// ```lua
|
||||||
// local ptr = memory.scan_pattern("some ida sig")
|
// local ptr = memory.scan_pattern("some ida sig")
|
||||||
|
// -- Check the implementation of the asmjit::TypeId get_type_id function if you are unsure what to use for return type / parameters types
|
||||||
// memory.dynamic_hook("test_hook", "float", {"const char*"}, ptr,
|
// memory.dynamic_hook("test_hook", "float", {"const char*"}, ptr,
|
||||||
// function(ret_val, str)
|
// function(ret_val, str)
|
||||||
//
|
//
|
||||||
@ -270,6 +271,351 @@ namespace lua::memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::unordered_map<uintptr_t, std::vector<uint8_t>> jitted_binded_funcs;
|
||||||
|
|
||||||
|
static std::string get_jitted_lua_func_global_name(uintptr_t function_to_call_ptr)
|
||||||
|
{
|
||||||
|
return std::format("__dynamic_call_{}", function_to_call_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
class asmjit_error_handler_t : public asmjit::ErrorHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void handleError(asmjit::Error err, const char* message, asmjit::BaseEmitter* origin) override
|
||||||
|
{
|
||||||
|
LOG(FATAL) << "asmjit error: " << message;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void jit_lua_binded_func(uintptr_t function_to_call_ptr, const asmjit::FuncSignature& function_to_call_sig, const asmjit::Arch& arch, std::vector<type_info_t> param_types, type_info_t return_type, lua_State* lua_state, const std::string& jitted_lua_func_global_name)
|
||||||
|
{
|
||||||
|
const auto it = jitted_binded_funcs.find(function_to_call_ptr);
|
||||||
|
if (it != jitted_binded_funcs.end())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
asmjit::CodeHolder code;
|
||||||
|
auto env = asmjit::Environment::host();
|
||||||
|
env.setArch(arch);
|
||||||
|
auto asmjit_error = code.init(env);
|
||||||
|
|
||||||
|
// initialize function
|
||||||
|
asmjit::x86::Compiler cc(&code);
|
||||||
|
// clang-format off
|
||||||
|
asmjit::FuncNode* func = cc.addFunc(asmjit::FuncSignature(asmjit::CallConvId::kFastCall, asmjit::FuncSignature::kNoVarArgs,
|
||||||
|
asmjit::TypeId::kInt32,
|
||||||
|
asmjit::TypeId::kUIntPtr));
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
asmjit::StringLogger log;
|
||||||
|
// clang-format off
|
||||||
|
const auto format_flags =
|
||||||
|
asmjit::FormatFlags::kMachineCode | asmjit::FormatFlags::kExplainImms | asmjit::FormatFlags::kRegCasts |
|
||||||
|
asmjit::FormatFlags::kHexImms | asmjit::FormatFlags::kHexOffsets | asmjit::FormatFlags::kPositions;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
log.addFlags(format_flags);
|
||||||
|
code.setLogger(&log);
|
||||||
|
asmjit_error_handler_t asmjit_error_handler;
|
||||||
|
code.setErrorHandler(&asmjit_error_handler);
|
||||||
|
|
||||||
|
// too small to really need it
|
||||||
|
func->frame().resetPreservedFP();
|
||||||
|
|
||||||
|
// map argument slots to registers, following abi.
|
||||||
|
std::vector<asmjit::x86::Reg> arg_registers;
|
||||||
|
for (uint8_t arg_index = 0; arg_index < function_to_call_sig.argCount(); arg_index++)
|
||||||
|
{
|
||||||
|
const auto arg_type = function_to_call_sig.args()[arg_index];
|
||||||
|
|
||||||
|
// for each "function to call" parameter
|
||||||
|
// InvokeNode for the corresponding lua_toXXX func
|
||||||
|
// result of the invokenode setRet goes to the arg Reg.
|
||||||
|
// those Reg will then be setArg of the function_to_call final InvokeNode.
|
||||||
|
|
||||||
|
asmjit::x86::Reg arg;
|
||||||
|
|
||||||
|
const auto arg_type_info = param_types[arg_index];
|
||||||
|
if (arg_type_info == type_info_t::integer_ || arg_type_info == type_info_t::float_ || arg_type_info == type_info_t::double_)
|
||||||
|
{
|
||||||
|
asmjit::InvokeNode* lua_tofunc;
|
||||||
|
// clang-format off
|
||||||
|
cc.invoke(&lua_tofunc, lua_tonumberx, asmjit::FuncSignature(asmjit::CallConvId::kCDecl, asmjit::FuncSignature::kNoVarArgs,
|
||||||
|
asmjit::TypeId::kFloat64,
|
||||||
|
asmjit::TypeId::kUIntPtr, asmjit::TypeId::kInt32, asmjit::TypeId::kUIntPtr));
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
lua_tofunc->setArg(0, lua_state);
|
||||||
|
lua_tofunc->setArg(1, arg_index + 1);
|
||||||
|
lua_tofunc->setArg(2, NULL);
|
||||||
|
|
||||||
|
const auto tmp = cc.newXmm();
|
||||||
|
lua_tofunc->setRet(0, tmp);
|
||||||
|
if (arg_type_info == type_info_t::integer_)
|
||||||
|
{
|
||||||
|
arg = cc.newUIntPtr();
|
||||||
|
cc.cvttsd2si(arg.as<asmjit::x86::Gp>(), tmp);
|
||||||
|
}
|
||||||
|
else if (arg_type_info == type_info_t::float_)
|
||||||
|
{
|
||||||
|
arg = cc.newXmm();
|
||||||
|
cc.cvtsd2ss(arg.as<asmjit::x86::Xmm>(), tmp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arg = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (arg_type_info == type_info_t::boolean_)
|
||||||
|
{
|
||||||
|
asmjit::InvokeNode* lua_tofunc;
|
||||||
|
// clang-format off
|
||||||
|
cc.invoke(&lua_tofunc, lua_toboolean, asmjit::FuncSignature(asmjit::CallConvId::kCDecl, asmjit::FuncSignature::kNoVarArgs,
|
||||||
|
asmjit::TypeId::kInt32,
|
||||||
|
asmjit::TypeId::kUIntPtr, asmjit::TypeId::kInt32));
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
lua_tofunc->setArg(0, lua_state);
|
||||||
|
lua_tofunc->setArg(1, arg_index + 1);
|
||||||
|
|
||||||
|
arg = cc.newUIntPtr();
|
||||||
|
lua_tofunc->setRet(0, arg);
|
||||||
|
}
|
||||||
|
else if (arg_type_info == type_info_t::string_)
|
||||||
|
{
|
||||||
|
asmjit::InvokeNode* lua_tofunc;
|
||||||
|
// clang-format off
|
||||||
|
cc.invoke(&lua_tofunc, lua_tolstring, asmjit::FuncSignature(asmjit::CallConvId::kCDecl, asmjit::FuncSignature::kNoVarArgs,
|
||||||
|
asmjit::TypeId::kUIntPtr,
|
||||||
|
asmjit::TypeId::kUIntPtr, asmjit::TypeId::kInt32, asmjit::TypeId::kUIntPtr));
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
lua_tofunc->setArg(0, lua_state);
|
||||||
|
lua_tofunc->setArg(1, arg_index + 1);
|
||||||
|
lua_tofunc->setArg(2, NULL);
|
||||||
|
|
||||||
|
arg = cc.newUIntPtr();
|
||||||
|
lua_tofunc->setRet(0, arg);
|
||||||
|
}
|
||||||
|
else if (arg_type_info == type_info_t::ptr_)
|
||||||
|
{
|
||||||
|
asmjit::InvokeNode* lua_tofunc;
|
||||||
|
// clang-format off
|
||||||
|
cc.invoke(&lua_tofunc, lua_tonumberx, asmjit::FuncSignature(asmjit::CallConvId::kCDecl, asmjit::FuncSignature::kNoVarArgs,
|
||||||
|
asmjit::TypeId::kFloat64,
|
||||||
|
asmjit::TypeId::kUIntPtr, asmjit::TypeId::kInt32, asmjit::TypeId::kUIntPtr));
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
lua_tofunc->setArg(0, lua_state);
|
||||||
|
lua_tofunc->setArg(1, arg_index + 1);
|
||||||
|
lua_tofunc->setArg(2, NULL);
|
||||||
|
|
||||||
|
// lua_Number (double) to integer type
|
||||||
|
const auto tmp = cc.newXmm();
|
||||||
|
lua_tofunc->setRet(0, tmp);
|
||||||
|
arg = cc.newUIntPtr();
|
||||||
|
cc.cvttsd2si(arg.as<asmjit::x86::Gp>(), tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_registers.push_back(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
asmjit::InvokeNode* function_to_call_invoke_node;
|
||||||
|
cc.invoke(&function_to_call_invoke_node, function_to_call_ptr, function_to_call_sig);
|
||||||
|
for (uint8_t arg_index = 0; arg_index < function_to_call_sig.argCount(); arg_index++)
|
||||||
|
{
|
||||||
|
function_to_call_invoke_node->setArg(arg_index, arg_registers.at(arg_index));
|
||||||
|
}
|
||||||
|
asmjit::x86::Reg function_to_call_return_val_reg;
|
||||||
|
if (is_general_register(function_to_call_sig.ret()))
|
||||||
|
{
|
||||||
|
function_to_call_return_val_reg = cc.newUIntPtr();
|
||||||
|
}
|
||||||
|
else if (is_XMM_register(function_to_call_sig.ret()))
|
||||||
|
{
|
||||||
|
function_to_call_return_val_reg = cc.newXmm();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG(FATAL) << "Return val wider than 64bits not supported";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
function_to_call_invoke_node->setRet(0, function_to_call_return_val_reg);
|
||||||
|
|
||||||
|
if (return_type == type_info_t::integer_ || return_type == type_info_t::float_ || return_type == type_info_t::double_)
|
||||||
|
{
|
||||||
|
if (function_to_call_sig.ret() >= asmjit::TypeId::_kIntStart && function_to_call_sig.ret() <= asmjit::TypeId::_kIntEnd)
|
||||||
|
{
|
||||||
|
// the function returned to a Gp register, need to convert it to a lua_Number compatible register.
|
||||||
|
const auto tmp = cc.newXmm();
|
||||||
|
cc.cvtsi2sd(tmp, function_to_call_return_val_reg.as<asmjit::x86::Gp>());
|
||||||
|
function_to_call_return_val_reg = tmp;
|
||||||
|
}
|
||||||
|
else if (function_to_call_sig.ret() == asmjit::TypeId::kFloat32)
|
||||||
|
{
|
||||||
|
// m128_f32 -> m128_f64 (lua_Number)
|
||||||
|
cc.cvtss2sd(function_to_call_return_val_reg.as<asmjit::x86::Xmm>(), function_to_call_return_val_reg.as<asmjit::x86::Xmm>());
|
||||||
|
}
|
||||||
|
|
||||||
|
asmjit::InvokeNode* lua_pushfunc;
|
||||||
|
// clang-format off
|
||||||
|
cc.invoke(&lua_pushfunc, lua_pushnumber, asmjit::FuncSignature(asmjit::CallConvId::kCDecl, asmjit::FuncSignature::kNoVarArgs,
|
||||||
|
asmjit::TypeId::kVoid,
|
||||||
|
asmjit::TypeId::kUIntPtr, asmjit::TypeId::kFloat64));
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
lua_pushfunc->setArg(0, lua_state);
|
||||||
|
lua_pushfunc->setArg(1, function_to_call_return_val_reg);
|
||||||
|
}
|
||||||
|
else if (return_type == type_info_t::boolean_)
|
||||||
|
{
|
||||||
|
asmjit::InvokeNode* lua_pushfunc;
|
||||||
|
// clang-format off
|
||||||
|
cc.invoke(&lua_pushfunc, lua_pushboolean, asmjit::FuncSignature(asmjit::CallConvId::kCDecl, asmjit::FuncSignature::kNoVarArgs,
|
||||||
|
asmjit::TypeId::kVoid,
|
||||||
|
asmjit::TypeId::kUIntPtr, asmjit::TypeId::kInt8));
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
lua_pushfunc->setArg(0, lua_state);
|
||||||
|
lua_pushfunc->setArg(1, function_to_call_return_val_reg);
|
||||||
|
}
|
||||||
|
else if (return_type == type_info_t::string_)
|
||||||
|
{
|
||||||
|
asmjit::InvokeNode* lua_pushfunc;
|
||||||
|
// clang-format off
|
||||||
|
cc.invoke(&lua_pushfunc, lua_pushstring, asmjit::FuncSignature(asmjit::CallConvId::kCDecl, asmjit::FuncSignature::kNoVarArgs,
|
||||||
|
asmjit::TypeId::kVoid,
|
||||||
|
asmjit::TypeId::kUIntPtr, asmjit::TypeId::kUIntPtr));
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
lua_pushfunc->setArg(0, lua_state);
|
||||||
|
lua_pushfunc->setArg(1, function_to_call_return_val_reg);
|
||||||
|
}
|
||||||
|
else if (return_type == type_info_t::ptr_)
|
||||||
|
{
|
||||||
|
// integer type to lua_Number (double)
|
||||||
|
asmjit::x86::Xmm tmp = cc.newXmm();
|
||||||
|
cc.cvtsi2sd(tmp, function_to_call_return_val_reg.as<asmjit::x86::Gp>());
|
||||||
|
|
||||||
|
asmjit::InvokeNode* lua_pushfunc;
|
||||||
|
// clang-format off
|
||||||
|
cc.invoke(&lua_pushfunc, lua_pushnumber, asmjit::FuncSignature(asmjit::CallConvId::kCDecl, asmjit::FuncSignature::kNoVarArgs,
|
||||||
|
asmjit::TypeId::kVoid,
|
||||||
|
asmjit::TypeId::kUIntPtr, asmjit::TypeId::kFloat64));
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
lua_pushfunc->setArg(0, lua_state);
|
||||||
|
lua_pushfunc->setArg(1, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a lua binded c func always return an int which hold the number of returned vars.
|
||||||
|
asmjit::x86::Gp retReg = cc.newUIntPtr();
|
||||||
|
cc.mov(retReg, function_to_call_sig.hasRet() ? 1 : 0);
|
||||||
|
cc.ret(retReg);
|
||||||
|
|
||||||
|
cc.endFunc();
|
||||||
|
|
||||||
|
// write to buffer
|
||||||
|
cc.finalize();
|
||||||
|
|
||||||
|
// worst case, overestimates for case trampolines needed
|
||||||
|
code.flatten();
|
||||||
|
size_t size = code.codeSize();
|
||||||
|
|
||||||
|
// Allocate a virtual memory (executable).
|
||||||
|
static std::vector<uint8_t> jit_function_buffer(size);
|
||||||
|
DWORD old_protect;
|
||||||
|
VirtualProtect(jit_function_buffer.data(), size, PAGE_EXECUTE_READWRITE, &old_protect);
|
||||||
|
|
||||||
|
// if multiple sections, resolve linkage (1 atm)
|
||||||
|
if (code.hasUnresolvedLinks())
|
||||||
|
{
|
||||||
|
code.resolveUnresolvedLinks();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relocate to the base-address of the allocated memory.
|
||||||
|
code.relocateToBase((uintptr_t)jit_function_buffer.data());
|
||||||
|
code.copyFlattenedData(jit_function_buffer.data(), size);
|
||||||
|
|
||||||
|
LOG(VERBOSE) << "JIT Stub: " << log.data();
|
||||||
|
|
||||||
|
lua_pushcfunction(lua_state, (lua_CFunction)jit_function_buffer.data());
|
||||||
|
lua_setglobal(lua_state, jitted_lua_func_global_name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lua API: Function
|
||||||
|
// Table: memory
|
||||||
|
// Name: dynamic_call
|
||||||
|
// Param: return_type: string: Type of the return value of the function to call.
|
||||||
|
// Param: param_types: table<string>: Types of the parameters of the function to call.
|
||||||
|
// Param: target_func_ptr: memory.pointer: The pointer to the function to call.
|
||||||
|
// Returns: string: Key name of the function that you can now call from lua.
|
||||||
|
// **Example Usage:**
|
||||||
|
// ```lua
|
||||||
|
// -- the sig in this example leads to an implementation of memcpy_s
|
||||||
|
// local ptr = memory.scan_pattern("48 89 5C 24 08 48 89 74 24 10 57 48 83 EC 20 49 8B D9 49 8B F0 48 8B FA")
|
||||||
|
// if ptr:is_valid() then
|
||||||
|
// local dest_size = 8
|
||||||
|
// local dest_ptr = memory.allocate(dest_size)
|
||||||
|
// dest_ptr:set_qword(0)
|
||||||
|
//
|
||||||
|
// local src_size = 8
|
||||||
|
// local src_ptr = memory.allocate(src_size)
|
||||||
|
// src_ptr:set_qword(123)
|
||||||
|
//
|
||||||
|
// -- Check the implementation of the asmjit::TypeId get_type_id function if you are unsure what to use for return type / parameters types
|
||||||
|
// local func_to_call_test_global_name = memory.dynamic_call("int", {"void*", "uint64_t", "void*", "uint64_t"}, ptr)
|
||||||
|
// -- print zero.
|
||||||
|
// log.info(dest_ptr:get_qword())
|
||||||
|
// -- note: don't pass memory.pointer objects directly when you call the function, but use get_address() instead.
|
||||||
|
// local call_res_test = _G[func_to_call_test_global_name](dest_ptr:get_address(), dest_size, src_ptr:get_address(), src_size)
|
||||||
|
// -- print 123.
|
||||||
|
// log.info(dest_ptr:get_qword())
|
||||||
|
// end
|
||||||
|
// ```
|
||||||
|
static std::string dynamic_call(const std::string& return_type, sol::table param_types_table, lua::memory::pointer& target_func_ptr_obj, sol::this_state state_)
|
||||||
|
{
|
||||||
|
const auto target_func_ptr = target_func_ptr_obj.get_address();
|
||||||
|
|
||||||
|
const auto jitted_lua_func_global_name = get_jitted_lua_func_global_name(target_func_ptr);
|
||||||
|
|
||||||
|
const auto already_jitted_func = sol::state_view(state_)[jitted_lua_func_global_name];
|
||||||
|
if (already_jitted_func.is<sol::protected_function>())
|
||||||
|
{
|
||||||
|
return already_jitted_func;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> param_types_strings;
|
||||||
|
for (const auto& [k, v] : param_types_table)
|
||||||
|
{
|
||||||
|
if (v.is<const char*>())
|
||||||
|
{
|
||||||
|
param_types_strings.push_back(v.as<const char*>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string call_convention = "";
|
||||||
|
asmjit::FuncSignature sig(get_call_convention(call_convention), asmjit::FuncSignature::kNoVarArgs, get_type_id(return_type));
|
||||||
|
|
||||||
|
std::vector<type_info_t> param_types;
|
||||||
|
for (const std::string& s : param_types_strings)
|
||||||
|
{
|
||||||
|
sig.addArg(get_type_id(s));
|
||||||
|
param_types.push_back(get_type_info_from_string(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
jit_lua_binded_func(target_func_ptr,
|
||||||
|
sig,
|
||||||
|
asmjit::Arch::kHost,
|
||||||
|
param_types,
|
||||||
|
get_type_info_from_string(return_type),
|
||||||
|
state_.L,
|
||||||
|
jitted_lua_func_global_name);
|
||||||
|
|
||||||
|
return jitted_lua_func_global_name;
|
||||||
|
}
|
||||||
|
|
||||||
void bind(sol::state& state)
|
void bind(sol::state& state)
|
||||||
{
|
{
|
||||||
auto ns = state["memory"].get_or_create<sol::table>();
|
auto ns = state["memory"].get_or_create<sol::table>();
|
||||||
@ -312,5 +658,7 @@ namespace lua::memory
|
|||||||
|
|
||||||
ns.new_usertype<value_wrapper_t>("value_wrapper", "get", &value_wrapper_t::get, "set", &value_wrapper_t::set);
|
ns.new_usertype<value_wrapper_t>("value_wrapper", "get", &value_wrapper_t::get, "set", &value_wrapper_t::set);
|
||||||
ns["dynamic_hook"] = dynamic_hook;
|
ns["dynamic_hook"] = dynamic_hook;
|
||||||
|
|
||||||
|
ns["dynamic_call"] = dynamic_call;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,143 @@
|
|||||||
|
|
||||||
namespace lua::memory
|
namespace lua::memory
|
||||||
{
|
{
|
||||||
|
// does a given type fit in a general purpose register (i.e. is it integer type)
|
||||||
|
inline bool is_general_register(const asmjit::TypeId type_id)
|
||||||
|
{
|
||||||
|
switch (type_id)
|
||||||
|
{
|
||||||
|
case asmjit::TypeId::kInt8:
|
||||||
|
case asmjit::TypeId::kUInt8:
|
||||||
|
case asmjit::TypeId::kInt16:
|
||||||
|
case asmjit::TypeId::kUInt16:
|
||||||
|
case asmjit::TypeId::kInt32:
|
||||||
|
case asmjit::TypeId::kUInt32:
|
||||||
|
case asmjit::TypeId::kInt64:
|
||||||
|
case asmjit::TypeId::kUInt64:
|
||||||
|
case asmjit::TypeId::kIntPtr:
|
||||||
|
case asmjit::TypeId::kUIntPtr: return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// float, double, simd128
|
||||||
|
inline bool is_XMM_register(const asmjit::TypeId type_id)
|
||||||
|
{
|
||||||
|
switch (type_id)
|
||||||
|
{
|
||||||
|
case asmjit::TypeId::kFloat32:
|
||||||
|
case asmjit::TypeId::kFloat64: return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline asmjit::CallConvId get_call_convention(const std::string& conv)
|
||||||
|
{
|
||||||
|
if (conv == "cdecl")
|
||||||
|
{
|
||||||
|
return asmjit::CallConvId::kCDecl;
|
||||||
|
}
|
||||||
|
else if (conv == "stdcall")
|
||||||
|
{
|
||||||
|
return asmjit::CallConvId::kStdCall;
|
||||||
|
}
|
||||||
|
else if (conv == "fastcall")
|
||||||
|
{
|
||||||
|
return asmjit::CallConvId::kFastCall;
|
||||||
|
}
|
||||||
|
|
||||||
|
return asmjit::CallConvId::kHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline asmjit::TypeId get_type_id(const std::string& type)
|
||||||
|
{
|
||||||
|
if (type.find('*') != std::string::npos)
|
||||||
|
{
|
||||||
|
return asmjit::TypeId::kUIntPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TYPEID_MATCH_STR_IF(var, T) \
|
||||||
|
if (var == #T) \
|
||||||
|
{ \
|
||||||
|
return asmjit::TypeId(asmjit::TypeUtils::TypeIdOfT<T>::kTypeId); \
|
||||||
|
}
|
||||||
|
#define TYPEID_MATCH_STR_ELSEIF(var, T) \
|
||||||
|
else if (var == #T) \
|
||||||
|
{ \
|
||||||
|
return asmjit::TypeId(asmjit::TypeUtils::TypeIdOfT<T>::kTypeId); \
|
||||||
|
}
|
||||||
|
|
||||||
|
TYPEID_MATCH_STR_IF(type, signed char)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, unsigned char)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, short)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, unsigned short)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, int)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, unsigned int)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, long)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, unsigned long)
|
||||||
|
#ifdef POLYHOOK2_OS_WINDOWS
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, __int64)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, unsigned __int64)
|
||||||
|
#endif
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, long long)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, unsigned long long)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, char)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, char16_t)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, char32_t)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, wchar_t)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, uint8_t)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, int8_t)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, uint16_t)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, int16_t)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, int32_t)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, uint32_t)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, uint64_t)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, int64_t)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, float)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, double)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, bool)
|
||||||
|
TYPEID_MATCH_STR_ELSEIF(type, void)
|
||||||
|
else if (type == "intptr_t")
|
||||||
|
{
|
||||||
|
return asmjit::TypeId::kIntPtr;
|
||||||
|
}
|
||||||
|
else if (type == "uintptr_t")
|
||||||
|
{
|
||||||
|
return asmjit::TypeId::kUIntPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return asmjit::TypeId::kVoid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static type_info_t get_type_info_from_string(const std::string& s)
|
||||||
|
{
|
||||||
|
if ((s.contains("const") && s.contains("char") && s.contains("*")) || s.contains("string"))
|
||||||
|
{
|
||||||
|
return type_info_t::string_;
|
||||||
|
}
|
||||||
|
else if (s.contains("bool"))
|
||||||
|
{
|
||||||
|
return type_info_t::boolean_;
|
||||||
|
}
|
||||||
|
else if (s.contains("ptr") || s.contains("pointer") || s.contains("*"))
|
||||||
|
{
|
||||||
|
// passing lua::memory::pointer
|
||||||
|
return type_info_t::ptr_;
|
||||||
|
}
|
||||||
|
else if (s.contains("float"))
|
||||||
|
{
|
||||||
|
return type_info_t::float_;
|
||||||
|
}
|
||||||
|
else if (s.contains("double"))
|
||||||
|
{
|
||||||
|
return type_info_t::double_;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return type_info_t::integer_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class runtime_func_t
|
class runtime_func_t
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> m_jit_function_buffer;
|
std::vector<uint8_t> m_jit_function_buffer;
|
||||||
@ -317,34 +454,6 @@ namespace lua::memory
|
|||||||
// stdcall, fastcall, or cdecl (cdecl is default on x86). On x64 those map to the same thing.
|
// stdcall, fastcall, or cdecl (cdecl is default on x86). On x64 those map to the same thing.
|
||||||
uintptr_t make_jit_func(const std::string& return_type, const std::vector<std::string>& param_types, const asmjit::Arch arch, const user_pre_callback_t pre_callback, const user_post_callback_t post_callback, const uintptr_t target_func_ptr, std::string call_convention = "")
|
uintptr_t make_jit_func(const std::string& return_type, const std::vector<std::string>& param_types, const asmjit::Arch arch, const user_pre_callback_t pre_callback, const user_post_callback_t post_callback, const uintptr_t target_func_ptr, std::string call_convention = "")
|
||||||
{
|
{
|
||||||
auto get_type_info_from_string = [](const std::string& s) {
|
|
||||||
if ((s.contains("const") && s.contains("char") && s.contains("*")) || s.contains("string"))
|
|
||||||
{
|
|
||||||
return type_info_t::string_;
|
|
||||||
}
|
|
||||||
else if (s.contains("bool"))
|
|
||||||
{
|
|
||||||
return type_info_t::boolean_;
|
|
||||||
}
|
|
||||||
else if (s.contains("ptr") || s.contains("pointer") || s.contains("*"))
|
|
||||||
{
|
|
||||||
// passing lua::memory::pointer
|
|
||||||
return type_info_t::ptr_;
|
|
||||||
}
|
|
||||||
else if (s.contains("float"))
|
|
||||||
{
|
|
||||||
return type_info_t::float_;
|
|
||||||
}
|
|
||||||
else if (s.contains("double"))
|
|
||||||
{
|
|
||||||
return type_info_t::double_;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return type_info_t::integer_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
m_return_type = get_type_info_from_string(return_type);
|
m_return_type = get_type_info_from_string(return_type);
|
||||||
|
|
||||||
asmjit::FuncSignature sig(get_call_convention(call_convention), asmjit::FuncSignature::kNoVarArgs, get_type_id(return_type));
|
asmjit::FuncSignature sig(get_call_convention(call_convention), asmjit::FuncSignature::kNoVarArgs, get_type_id(return_type));
|
||||||
@ -364,114 +473,5 @@ namespace lua::memory
|
|||||||
|
|
||||||
m_detour->enable();
|
m_detour->enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
// does a given type fit in a general purpose register (i.e. is it integer type)
|
|
||||||
bool is_general_register(const asmjit::TypeId type_id) const
|
|
||||||
{
|
|
||||||
switch (type_id)
|
|
||||||
{
|
|
||||||
case asmjit::TypeId::kInt8:
|
|
||||||
case asmjit::TypeId::kUInt8:
|
|
||||||
case asmjit::TypeId::kInt16:
|
|
||||||
case asmjit::TypeId::kUInt16:
|
|
||||||
case asmjit::TypeId::kInt32:
|
|
||||||
case asmjit::TypeId::kUInt32:
|
|
||||||
case asmjit::TypeId::kInt64:
|
|
||||||
case asmjit::TypeId::kUInt64:
|
|
||||||
case asmjit::TypeId::kIntPtr:
|
|
||||||
case asmjit::TypeId::kUIntPtr: return true;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// float, double, simd128
|
|
||||||
bool is_XMM_register(const asmjit::TypeId type_id) const
|
|
||||||
{
|
|
||||||
switch (type_id)
|
|
||||||
{
|
|
||||||
case asmjit::TypeId::kFloat32:
|
|
||||||
case asmjit::TypeId::kFloat64: return true;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
asmjit::CallConvId get_call_convention(const std::string& conv)
|
|
||||||
{
|
|
||||||
if (conv == "cdecl")
|
|
||||||
{
|
|
||||||
return asmjit::CallConvId::kCDecl;
|
|
||||||
}
|
|
||||||
else if (conv == "stdcall")
|
|
||||||
{
|
|
||||||
return asmjit::CallConvId::kStdCall;
|
|
||||||
}
|
|
||||||
else if (conv == "fastcall")
|
|
||||||
{
|
|
||||||
return asmjit::CallConvId::kFastCall;
|
|
||||||
}
|
|
||||||
|
|
||||||
return asmjit::CallConvId::kHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
asmjit::TypeId get_type_id(const std::string& type)
|
|
||||||
{
|
|
||||||
if (type.find('*') != std::string::npos)
|
|
||||||
{
|
|
||||||
return asmjit::TypeId::kUIntPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TYPEID_MATCH_STR_IF(var, T) \
|
|
||||||
if (var == #T) \
|
|
||||||
{ \
|
|
||||||
return asmjit::TypeId(asmjit::TypeUtils::TypeIdOfT<T>::kTypeId); \
|
|
||||||
}
|
|
||||||
#define TYPEID_MATCH_STR_ELSEIF(var, T) \
|
|
||||||
else if (var == #T) \
|
|
||||||
{ \
|
|
||||||
return asmjit::TypeId(asmjit::TypeUtils::TypeIdOfT<T>::kTypeId); \
|
|
||||||
}
|
|
||||||
|
|
||||||
TYPEID_MATCH_STR_IF(type, signed char)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, unsigned char)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, short)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, unsigned short)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, int)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, unsigned int)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, long)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, unsigned long)
|
|
||||||
#ifdef POLYHOOK2_OS_WINDOWS
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, __int64)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, unsigned __int64)
|
|
||||||
#endif
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, long long)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, unsigned long long)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, char)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, char16_t)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, char32_t)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, wchar_t)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, uint8_t)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, int8_t)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, uint16_t)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, int16_t)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, int32_t)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, uint32_t)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, uint64_t)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, int64_t)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, float)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, double)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, bool)
|
|
||||||
TYPEID_MATCH_STR_ELSEIF(type, void)
|
|
||||||
else if (type == "intptr_t")
|
|
||||||
{
|
|
||||||
return asmjit::TypeId::kIntPtr;
|
|
||||||
}
|
|
||||||
else if (type == "uintptr_t")
|
|
||||||
{
|
|
||||||
return asmjit::TypeId::kUIntPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return asmjit::TypeId::kVoid;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user