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-06-26 18:26:13 +03:00
mg_esp_save = 20
2016-07-30 02:03:01 +03:00
} ;
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 ( )
{
// prologue
2017-02-14 00:53:54 +03:00
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
2017-02-14 00:53:54 +03:00
2016-07-30 02:03:01 +03:00
enum / / stack map
{
2017-02-14 01:51:47 +03:00
/* META GLOBALS BACKUP */
/* STRING BUFFER */
over_ret = sizeof ( int ) ,
orig_ret = 0
2016-07-30 02:03:01 +03:00
} ;
auto globals = ebx ;
2017-02-14 01:51:47 +03:00
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 ) {
2017-02-14 01:51:47 +03:00
size_t strbuf_offset = locals_size ;
2017-01-06 22:11:28 +03:00
2017-02-14 00:53:54 +03:00
sub ( esp , framesize + = align ( MAX_STRBUF_LEN , xmmreg_size ) ) ;
2016-07-30 02:03:01 +03:00
// format varargs
2017-02-14 00:53:54 +03:00
lea ( edx , dword_ptr [ ebp + first_arg_offset + m_jitdata - > args_count * sizeof ( int ) ] ) ; // varargs ptr
2017-02-14 01:51:47 +03:00
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 ) ;
2017-02-14 00:53:54 +03:00
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 ) ;
2017-06-26 18:26:13 +03:00
size_t mg_backup = framesize - sizeof ( meta_globals_t ) ;
2017-02-14 01:51:47 +03:00
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 ) ) ;
2017-02-14 01:51:47 +03:00
movaps ( xmm0 , xmmword_ptr [ globals ] ) ;
2017-06-26 18:26:13 +03:00
movq ( xmm1 , qword_ptr [ globals + xmmreg_size ] ) ;
movaps ( xmmword_ptr [ esp + mg_backup + sizeof ( int ) * 2 ] , xmm0 ) ;
movq ( qword_ptr [ esp + mg_backup ] , xmm1 ) ;
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
2017-06-29 19:59:55 +03:00
mov ( dword_ptr [ globals + mg_status ] , MRES_UNSET ) ;
2017-06-26 21:57:58 +03:00
mov ( dword_ptr [ globals + mg_esp_save ] , esp ) ;
2016-07-30 02:03:01 +03:00
// 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-27 02:19:45 +03:00
if ( plug = = m_jitdata - > plugins - > front ( ) ) { // init meta globals
2016-07-30 02:03:01 +03:00
mov ( dword_ptr [ globals + mg_mres ] , MRES_IGNORED ) ;
2017-06-29 19:59:55 +03:00
mov ( dword_ptr [ globals + mg_prev_mres ] , MRES_UNSET ) ;
2016-07-30 02:03:01 +03:00
}
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
call_func ( ecx ) ;
2017-06-27 00:10:45 +03:00
// update highest meta result
2016-07-30 02:03:01 +03:00
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 ) ;
2017-06-29 19:59:55 +03:00
// save return value if override or supercede
2016-07-30 02:03:01 +03:00
if ( m_jitdata - > has_ret ) {
mov ( ecx , dword_ptr [ esp + over_ret ] ) ;
2017-06-29 19:59:55 +03:00
cmp ( edx , MRES_OVERRIDE ) ;
cmovae ( ecx , eax ) ;
2016-07-30 02:03:01 +03:00
mov ( dword_ptr [ esp + over_ret ] , ecx ) ;
}
L ( go_next_plugin ) ;
}
2017-06-27 00:10:45 +03:00
// call original if it needed
2016-07-30 02:03:01 +03:00
cmp ( dword_ptr [ globals + mg_status ] , MRES_SUPERCEDE ) ;
jz ( " skip_original " ) ;
{
2017-06-27 00:10:45 +03:00
// and present
2016-07-30 02:03:01 +03:00
if ( m_jitdata - > pfn_original ) {
mov ( ecx , m_jitdata - > pfn_original ) ;
call_func ( ecx ) ;
}
2017-06-27 00:10:45 +03:00
// store original return value
2016-07-30 02:03:01 +03:00
if ( m_jitdata - > has_ret ) {
if ( m_jitdata - > pfn_original )
mov ( dword_ptr [ esp + orig_ret ] , eax ) ;
else
2017-06-27 00:10:45 +03:00
mov ( dword_ptr [ esp + orig_ret ] , TRUE ) ; // fix for should collide :/
2016-07-30 02:03:01 +03:00
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-27 02:19:45 +03:00
if ( plug = = m_jitdata - > plugins - > front ( ) ) { // init meta globals
2016-07-30 02:03:01 +03:00
mov ( dword_ptr [ globals + mg_mres ] , MRES_IGNORED ) ;
2017-06-29 19:59:55 +03:00
mov ( dword_ptr [ globals + mg_prev_mres ] , MRES_UNSET ) ;
2016-07-30 02:03:01 +03:00
}
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
call_func ( ecx ) ;
2017-06-27 00:10:45 +03:00
// update highest meta result
2016-07-30 02:03:01 +03:00
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 ) ;
2017-06-29 19:59:55 +03:00
// save return value if override or supercede
2016-07-30 02:03:01 +03:00
if ( m_jitdata - > has_ret ) {
2017-06-29 19:59:55 +03:00
cmp ( edx , MRES_OVERRIDE ) ;
2016-07-30 02:03:01 +03:00
mov ( ecx , dword_ptr [ esp + over_ret ] ) ;
2017-06-29 19:59:55 +03:00
cmovae ( ecx , eax ) ;
2016-07-30 02:03:01 +03:00
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 ) ;
}
2017-06-27 00:10:45 +03:00
// restore meta globals
2017-06-26 18:26:13 +03:00
movaps ( xmm0 , xmmword_ptr [ esp + mg_backup + sizeof ( int ) * 2 ] ) ;
movq ( xmm1 , qword_ptr [ esp + mg_backup ] ) ;
2017-02-14 01:51:47 +03:00
movaps ( xmmword_ptr [ globals ] , xmm0 ) ;
2017-06-26 18:26:13 +03:00
movq ( qword_ptr [ globals + xmmreg_size ] , xmm1 ) ;
2016-07-30 02:03:01 +03:00
2017-06-27 00:10:45 +03:00
// setup return value and override it if needed
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 ) ;
2017-06-29 19:59:55 +03:00
cmovae ( eax , dword_ptr [ esp + over_ret ] ) ;
2016-07-30 02:03:01 +03:00
}
// epilogue
2017-02-08 00:49:20 +03:00
mov ( esp , ebp ) ;
2016-07-30 02:03:01 +03:00
pop ( ebp ) ;
2017-02-14 00:53:54 +03:00
pop ( ebx ) ;
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 ) {
2017-02-14 01:51:47 +03:00
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 - - )
2017-02-14 00:53:54 +03:00
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 ) ;
pushad ( ) ;
push ( size_t ( memory_leak ) ) ;
call ( dword_ptr [ size_t ( & print_ptr ) ] ) ;
# ifdef JIT_DEBUG_FILE
2017-06-28 21:05:53 +04:00
static size_t fprint_ptr = size_t ( & mdebug_to_file ) ;
2017-06-26 15:44:12 +03:00
call ( dword_ptr [ size_t ( & fprint_ptr ) ] ) ;
# endif
add ( esp , 4 ) ;
popad ( ) ;
# endif
}
2017-01-09 01:30:23 +03:00
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 ( ) ;
2017-01-09 01:30:23 +03:00
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 ( )
{
2017-01-09 01:30:23 +03:00
m_callback_allocator . deallocate_all ( ) ;
}
void CJit : : clear_tramps ( )
{
m_tramp_allocator . deallocate_all ( ) ;
2016-07-30 02:03:01 +03:00
}
2017-06-26 21:57:58 +03:00
size_t CJit : : is_callback_retaddr ( uint32 addr )
{
if ( m_callback_allocator . contain ( addr ) ) {
2017-06-27 00:10:45 +03:00
// FF D1 call ecx
// 83 C4 20 add esp, 20h ; optional
// 8B 13 mov edx, [ebx]
2017-06-26 21:57:58 +03:00
char * ptr = ( char * ) addr - 2 ;
return mem_compare ( ptr , " \xFF \xD1 \x83 \xC4 " , 4 ) | | mem_compare ( ptr , " \xFF \xD1 \x8B \x13 " , 4 ) ;
}
return false ;
}
char * CJit : : find_callback_pattern ( char * pattern , size_t len )
{
return m_callback_allocator . find_pattern ( pattern , len ) ;
}
2016-07-30 02:03:01 +03:00
bool CJit : : is_hook_needed ( jitdata_t * jitdata )
{
2017-06-29 19:59:55 +03:00
if ( ! jitdata - > pfn_original )
return true ;
2016-07-30 02:03:01 +03:00
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 ;
}