2
0
mirror of https://github.com/rehlds/metamod-r.git synced 2025-01-16 00:28:07 +03:00
metamod-r/metamod/src/callback_jit.cpp

393 lines
9.2 KiB
C++
Raw Normal View History

2016-07-30 02:03:01 +03:00
#include "precompiled.h"
CJit g_jit;
2017-05-09 19:31:09 +03:00
class CUniqueLabel : public std::string
2016-07-30 02:03:01 +03:00
{
public:
2017-05-09 19:31:09 +03:00
CUniqueLabel(const char* name) : std::string(std::string(name) + std::to_string(m_unique_index++))
2016-07-30 02:03:01 +03:00
{
}
private:
static size_t m_unique_index;
};
2017-05-09 19:31:09 +03:00
2016-07-30 02:03:01 +03:00
size_t CUniqueLabel::m_unique_index;
2017-06-26 15:44:12 +03:00
class CForwardCallbackJIT : public jitasm::function<void, CForwardCallbackJIT>
2016-07-30 02:03:01 +03:00
{
public:
2017-05-09 19:31:09 +03:00
CForwardCallbackJIT(jitdata_t* jitdata);
2017-01-06 22:55:16 +03:00
void naked_main();
2017-05-09 19:31:09 +03:00
void call_func(Reg32 addr);
2017-06-26 15:44:12 +03:00
void jit_debug(const char* format, ...);
2016-07-30 02:03:01 +03:00
private:
jitdata_t* m_jitdata;
enum
{
mg_mres = 0,
mg_prev_mres = 4,
mg_status = 8,
mg_orig_ret = 12,
mg_over_ret = 16,
};
2017-02-14 01:20:27 +03:00
enum
{
first_arg_offset = 12,
xmmreg_size = 16
};
static size_t align(size_t v, size_t a)
{
return (v + a - 1) & ~(a - 1);
}
2016-07-30 02:03:01 +03:00
};
CForwardCallbackJIT::CForwardCallbackJIT(jitdata_t* jitdata) : m_jitdata(jitdata)
{
}
void CForwardCallbackJIT::naked_main()
{
2017-06-26 15:44:12 +03:00
jit_debug("Enter %s\n", m_jitdata->name);
2016-07-30 02:03:01 +03:00
// prologue
push(ebx);
2016-07-30 02:03:01 +03:00
push(ebp);
mov(ebp, esp);
2017-02-14 01:20:27 +03:00
and_(esp, 0xFFFFFFF0); // stack must be 16-aligned when we calling subroutines
2016-07-30 02:03:01 +03:00
enum // stack map
{
/* META GLOBALS BACKUP */
/* STRING BUFFER */
over_ret = sizeof(int),
orig_ret = 0
2016-07-30 02:03:01 +03:00
};
auto globals = ebx;
auto locals_size = m_jitdata->has_ret ? sizeof(int) * 2 /* orig + over */ : 0;
auto framesize = align(locals_size + sizeof(meta_globals_t) + /* for align */m_jitdata->args_count * sizeof(int), xmmreg_size) - m_jitdata->args_count * sizeof(int);
2016-07-30 02:03:01 +03:00
if (m_jitdata->has_varargs) {
size_t strbuf_offset = locals_size;
2017-01-06 22:11:28 +03:00
sub(esp, framesize += align(MAX_STRBUF_LEN, xmmreg_size));
2016-07-30 02:03:01 +03:00
// format varargs
lea(edx, dword_ptr[ebp + first_arg_offset + m_jitdata->args_count * sizeof(int)]); // varargs ptr
if (strbuf_offset)
lea(eax, dword_ptr[esp + strbuf_offset]); // buf ptr
else
mov(eax, esp);
2017-05-08 23:52:22 +03:00
mov(ecx, size_t(Q_vsnprintf));
2016-07-30 02:03:01 +03:00
push(edx);
push(dword_ptr[ebp + first_arg_offset + (m_jitdata->args_count - 1) * sizeof(int)]); // last arg of pfn (format string)
2016-07-30 02:03:01 +03:00
push(MAX_STRBUF_LEN);
push(eax);
call(ecx);
add(esp, 4 * sizeof(int));
}
else
sub(esp, framesize);
size_t mg_backup = framesize - xmmreg_size - sizeof(int);
2017-01-06 22:11:28 +03:00
// setup globals ptr and backup old data
2016-07-30 02:03:01 +03:00
mov(globals, size_t(&g_metaGlobals));
movaps(xmm0, xmmword_ptr[globals]);
mov(eax, dword_ptr[globals + xmmreg_size]);
movaps(xmmword_ptr[esp + mg_backup + sizeof(int)], xmm0);
mov(dword_ptr[esp + mg_backup], eax);
2016-07-30 02:03:01 +03:00
// call metamod's pre hook if present
if (m_jitdata->mm_hook && m_jitdata->mm_hook_time == P_PRE) {
mov(ecx, m_jitdata->mm_hook);
call_func(ecx);
}
// setup meta globals
mov(dword_ptr[globals + mg_mres], MRES_UNSET);
// setup retval pointers
if (m_jitdata->has_ret) {
lea(eax, dword_ptr[esp + over_ret]);
mov(dword_ptr[globals + mg_orig_ret], esp);
mov(dword_ptr[globals + mg_over_ret], eax);
}
// call pre
2017-06-26 18:10:34 +03:00
for (auto plug : *m_jitdata->plugins) {
2017-05-09 18:34:55 +03:00
if (plug->status() < PL_RUNNING) // allow only running and paused
2017-01-13 02:12:09 +03:00
continue;
2016-07-30 02:03:01 +03:00
size_t fn_table = *(size_t *)(size_t(plug) + m_jitdata->table_offset);
// plugin don't want any hooks from that table
if (!fn_table)
continue;
CUniqueLabel go_next_plugin("go_next_plugin");
// check status and handler set
mov(ecx, dword_ptr[fn_table + m_jitdata->pfn_offset]);
2017-05-09 18:34:55 +03:00
cmp(byte_ptr[plug->status_ptr()], PL_RUNNING);
2016-07-30 02:03:01 +03:00
jecxz(go_next_plugin);
2017-02-14 01:20:27 +03:00
jnz(go_next_plugin);
2016-07-30 02:03:01 +03:00
2017-06-26 18:10:34 +03:00
if (&plug == &m_jitdata->plugins->front()) { // init
2016-07-30 02:03:01 +03:00
xor_(eax, eax);
mov(dword_ptr[globals + mg_mres], MRES_IGNORED);
mov(dword_ptr[globals + mg_prev_mres], eax); // MRES_UNSET
mov(dword_ptr[globals + mg_status], eax); // NULL
}
2017-06-26 18:10:34 +03:00
else {
mov(eax, dword_ptr[globals + mg_mres]);
mov(dword_ptr[globals + mg_mres], MRES_IGNORED);
mov(dword_ptr[globals + mg_prev_mres], eax);
}
2016-07-30 02:03:01 +03:00
2017-06-26 15:44:12 +03:00
jit_debug("Calling pre [%s] for plug [%s]\n", m_jitdata->name, plug->description());
2016-07-30 02:03:01 +03:00
call_func(ecx);
mov(edx, dword_ptr[globals + mg_mres]);
mov(ecx, dword_ptr[globals + mg_status]);
cmp(edx, ecx);
cmovg(ecx, edx);
mov(dword_ptr[globals + mg_status], ecx);
if (m_jitdata->has_ret) {
mov(ecx, dword_ptr[esp + over_ret]);
cmp(edx, MRES_SUPERCEDE);
cmovz(ecx, eax);
mov(dword_ptr[esp + over_ret], ecx);
}
L(go_next_plugin);
}
// call original if need
cmp(dword_ptr[globals + mg_status], MRES_SUPERCEDE);
jz("skip_original");
{
if (m_jitdata->pfn_original) {
2017-06-26 15:44:12 +03:00
//jit_debug("Call original %s\n", m_jitdata->name);
2016-07-30 02:03:01 +03:00
mov(ecx, m_jitdata->pfn_original);
call_func(ecx);
}
if (m_jitdata->has_ret) {
if (m_jitdata->pfn_original)
mov(dword_ptr[esp + orig_ret], eax);
else
mov(dword_ptr[esp + orig_ret], TRUE); // for should collide :/
jmp("skip_supercede");
}
}
L("skip_original");
{
if (m_jitdata->has_ret) {
// if supercede
mov(eax, dword_ptr[esp + over_ret]);
mov(dword_ptr[esp + orig_ret], eax);
L("skip_supercede");
}
}
L("skip_all");
// call post
2017-06-26 18:10:34 +03:00
for (auto plug : *m_jitdata->plugins) {
2017-05-09 18:34:55 +03:00
if (plug->status() < PL_RUNNING) // allow only running and paused
2017-01-13 02:12:09 +03:00
continue;
2016-07-30 02:03:01 +03:00
size_t fn_table = *(size_t *)(size_t(plug) + m_jitdata->post_table_offset);
// plugin don't want any hooks from that table
if (!fn_table)
continue;
CUniqueLabel go_next_plugin("go_next_plugin");
// check status and handler set
mov(ecx, dword_ptr[fn_table + m_jitdata->pfn_offset]);
2017-05-09 18:34:55 +03:00
cmp(byte_ptr[plug->status_ptr()], PL_RUNNING);
2016-07-30 02:03:01 +03:00
jecxz(go_next_plugin);
2017-02-14 01:20:27 +03:00
jnz(go_next_plugin);
2016-07-30 02:03:01 +03:00
2017-06-26 18:10:34 +03:00
if (&plug == &m_jitdata->plugins->front()) { // init
2016-07-30 02:03:01 +03:00
xor_(eax, eax);
mov(dword_ptr[globals + mg_mres], MRES_IGNORED);
mov(dword_ptr[globals + mg_prev_mres], eax); // MRES_UNSET
mov(dword_ptr[globals + mg_status], eax); // NULL
}
2017-06-26 18:10:34 +03:00
else {
mov(eax, dword_ptr[globals + mg_mres]);
mov(dword_ptr[globals + mg_mres], MRES_IGNORED);
mov(dword_ptr[globals + mg_prev_mres], eax);
}
2016-07-30 02:03:01 +03:00
2017-06-26 15:44:12 +03:00
jit_debug("Calling post [%s] for plug [%s]\n", m_jitdata->name, plug->description());
2016-07-30 02:03:01 +03:00
call_func(ecx);
mov(edx, dword_ptr[globals + mg_mres]);
mov(ecx, dword_ptr[globals + mg_status]);
cmp(ecx, edx);
cmovl(ecx, edx);
mov(dword_ptr[globals + mg_status], ecx);
if (m_jitdata->has_ret) {
cmp(edx, MRES_SUPERCEDE);
mov(ecx, dword_ptr[esp + over_ret]);
cmovz(ecx, eax);
mov(dword_ptr[esp + over_ret], ecx);
}
L(go_next_plugin);
}
// call metamod's post hook if present
if (m_jitdata->mm_hook && m_jitdata->mm_hook_time == P_POST) {
mov(ecx, m_jitdata->mm_hook);
call_func(ecx);
}
movaps(xmm0, xmmword_ptr[esp + mg_backup + sizeof(int)]);
mov(eax, dword_ptr[esp + mg_backup]);
movaps(xmmword_ptr[globals], xmm0);
mov(dword_ptr[globals + xmmreg_size], eax);
2016-07-30 02:03:01 +03:00
if (m_jitdata->has_ret) {
mov(eax, dword_ptr[esp + orig_ret]);
cmp(dword_ptr[globals + mg_status], MRES_OVERRIDE);
cmovz(eax, dword_ptr[esp + over_ret]);
}
// epilogue
2017-02-08 00:49:20 +03:00
mov(esp, ebp);
2016-07-30 02:03:01 +03:00
pop(ebp);
pop(ebx);
2017-06-26 15:44:12 +03:00
jit_debug("Leave %s\n", m_jitdata->name);
2016-07-30 02:03:01 +03:00
ret();
}
2017-05-09 19:31:09 +03:00
void CForwardCallbackJIT::call_func(Reg32 addr)
2016-07-30 02:03:01 +03:00
{
2017-01-06 22:11:28 +03:00
const size_t fixed_args_count = m_jitdata->args_count - (m_jitdata->has_varargs ? 1u /* excluding format string */ : 0u);
2017-05-09 19:31:09 +03:00
const size_t strbuf_offset = m_jitdata->has_ret ? sizeof(int) * 2u /* orig + over */ : 0u;
2016-07-30 02:03:01 +03:00
2017-01-06 22:11:28 +03:00
// push formatted buf instead of format string
2016-07-30 02:03:01 +03:00
if (m_jitdata->has_varargs) {
if (strbuf_offset) {
lea(eax, dword_ptr[esp + strbuf_offset]);
push(eax);
}
else
push(esp);
2016-07-30 02:03:01 +03:00
}
// push normal args
2017-01-06 22:11:28 +03:00
for (size_t j = fixed_args_count; j > 0; j--)
push(dword_ptr[ebp + first_arg_offset + (j - 1) * sizeof(int)]);
2016-07-30 02:03:01 +03:00
// call
call(addr);
// pop stack
if (m_jitdata->args_count)
2017-01-06 22:11:28 +03:00
add(esp, m_jitdata->args_count * sizeof(int));
2016-07-30 02:03:01 +03:00
}
2017-06-26 15:44:12 +03:00
void CForwardCallbackJIT::jit_debug(const char* format, ...)
{
#ifdef JIT_DEBUG
va_list argptr;
char string[1024] = "";
va_start(argptr, format);
Q_vsnprintf(string, sizeof string, format, argptr);
va_end(argptr);
char* memory_leak = Q_strdup(string); // yes, I'm lazy
static size_t print_ptr = size_t(&printf);
static size_t fprint_ptr = size_t(&mdebug_to_file);
pushad();
push(size_t(memory_leak));
call(dword_ptr[size_t(&print_ptr)]);
#ifdef JIT_DEBUG_FILE
call(dword_ptr[size_t(&fprint_ptr)]);
#endif
add(esp, 4);
popad();
#endif
}
CJit::CJit() : m_callback_allocator(static_allocator::mp_rwx), m_tramp_allocator(static_allocator::mp_rwx)
{
}
2016-07-30 02:03:01 +03:00
size_t CJit::compile_callback(jitdata_t* jitdata)
{
if (!is_hook_needed(jitdata)) {
return jitdata->pfn_original;
}
CForwardCallbackJIT callback(jitdata);
callback.Assemble();
auto code = callback.GetCode();
auto codeSize = callback.GetCodeSize();
auto ptr = m_callback_allocator.allocate(codeSize);
2016-07-30 02:03:01 +03:00
return (size_t)memcpy(ptr, code, codeSize);
}
2017-01-13 02:12:09 +03:00
size_t CJit::compile_tramp(size_t ptr_to_func)
2016-07-30 02:03:01 +03:00
{
2017-01-13 02:12:09 +03:00
auto code = (uint8 *)m_tramp_allocator.allocate(2 + sizeof(int));
2016-07-30 02:03:01 +03:00
2017-01-13 02:12:09 +03:00
// jmp dword [ptr_to_func]
code[0] = 0xFFu;
code[1] = 0x25u;
*(size_t *)&code[2] = ptr_to_func;
2016-07-30 02:03:01 +03:00
2017-01-13 02:12:09 +03:00
return (size_t)code;
2016-07-30 02:03:01 +03:00
}
void CJit::clear_callbacks()
{
m_callback_allocator.deallocate_all();
}
void CJit::clear_tramps()
{
m_tramp_allocator.deallocate_all();
2016-07-30 02:03:01 +03:00
}
bool CJit::is_hook_needed(jitdata_t* jitdata)
{
if (jitdata->mm_hook)
return true;
if (!jitdata->plugins)
return false;
2017-06-26 18:10:34 +03:00
for (auto& plug : *jitdata->plugins) {
2017-05-09 19:31:09 +03:00
const size_t fn_table = *(size_t *)(size_t(plug) + jitdata->table_offset);
const size_t fn_table_post = *(size_t *)(size_t(plug) + jitdata->post_table_offset);
2016-07-30 02:03:01 +03:00
if (fn_table || fn_table_post) {
return true;
}
}
return false;
}