diff options
| -rwxr-xr-x | compile | 14 | ||||
| -rw-r--r-- | compile.bat | 29 | ||||
| -rw-r--r-- | src/asm.h | 39 | ||||
| -rw-r--r-- | src/con_.S | 45 | ||||
| -rw-r--r-- | src/con_.c | 93 | ||||
| -rw-r--r-- | src/hook.S | 26 | ||||
| -rw-r--r-- | src/hook.c | 14 | ||||
| -rw-r--r-- | src/hook.h | 2 | ||||
| -rw-r--r-- | src/sst.c | 4 | ||||
| -rw-r--r-- | test/hook.test.c | 71 |
10 files changed, 172 insertions, 165 deletions
@@ -35,8 +35,8 @@ fi objs= cc() { _bn="`basename "$1"`" - objs="$objs .build/${_bn%%.c}.o" - _mn=" -DMODULE_NAME=${_bn%%.c}" + objs="$objs .build/$_bn.o" + _mn=" -DMODULE_NAME=${_bn%%.*}" # ugly annoying special case if [ "$_mn" = " -DMODULE_NAME=con_" ]; then _mn=" -DMODULE_NAME=con" elif [ "$_mn" = "-DMODULE_NAME=sst" ]; then _mn=; fi @@ -61,6 +61,7 @@ src="\ chunklets/x86.c clientcon.c con_.c + con_.S crypto.c democustom.c demorec.c @@ -75,6 +76,7 @@ src="\ gameserver.c hexcolour.c hook.c + hook.S hud.c inputhud.c kvsys.c @@ -99,7 +101,8 @@ $HOSTCC -O2 -fuse-ld=lld $warnings $stdflags \ -o .build/mkgamedata src/build/mkgamedata.c src/os.c $HOSTCC -O2 -fuse-ld=lld $warnings $stdflags \ -o .build/mkentprops src/build/mkentprops.c src/os.c -.build/gluegen `for s in $src; do echo "src/$s"; done` +# pass only .c files to gluegen (ignore .S) +.build/gluegen `for s in $src; do if [ "${s##*.}" = "c" ]; then echo "src/$s"; fi; done` .build/mkgamedata gamedata/engine.txt gamedata/gamelib.txt gamedata/inputsystem.txt \ gamedata/matchmaking.txt gamedata/vgui2.txt gamedata/vguimatsurface.txt gamedata/vphysics.txt .build/mkentprops gamedata/entprops.txt @@ -110,11 +113,6 @@ ld $HOSTCC -O2 -g3 $warnings $stdflags -include test/test.h -o .build/bitbuf.test test/bitbuf.test.c .build/bitbuf.test -# XXX: skipping this test on linux for now but should enable when we can test it -#$HOSTCC -m32 -O2 -g3 -include test/test.h -o .build/hook.test test/hook.test.c -#.build/hook.test -$HOSTCC -O2 -g3 $warnings $stdflags -include test/test.h -o .build/kv.test test/kv.test.c -.build/kv.test $HOSTCC -O2 -g3 $warnings $stdflags -include test/test.h -o .build/x86.test test/x86.test.c .build/x86.test diff --git a/compile.bat b/compile.bat index 0affd05..3cd8c84 100644 --- a/compile.bat +++ b/compile.bat @@ -37,17 +37,17 @@ set objs= goto :main
:cc
-for /F %%b in ("%1") do set basename=%%~nb
+for /F %%f in ("%1") do (
+ set basename=%%~nf
+ set ext=%%~xf
+)
set dmodname= -DMODULE_NAME=%basename%
:: ugly annoying special cases
if "%dmodname%"==" -DMODULE_NAME=con_" set dmodname= -DMODULE_NAME=con
if "%dmodname%"==" -DMODULE_NAME=sst" set dmodname=
-set objs=%objs% .build/%basename%.o
+set objs=%objs% .build/%basename%%ext%.o
%CC% -c -flto -mno-stack-arg-probe %cflags% %warnings% %stdflags% -I.build/include ^
--D_DLL%dmodname% -o .build/%basename%.o %1 || goto :end
-goto :eof
-
-:src
+-D_DLL%dmodname% -o .build/%basename%%ext%.o %1 || goto :end
goto :eof
:main
@@ -55,9 +55,15 @@ goto :eof set stdflags=-std=c2x -D_CRT_SECURE_NO_WARNINGS -DWIN32_LEAN_AND_MEAN -DNOMINMAX
set src=
+:: metasrc has C sources only -> we don't attempt to run gluegen over asm files.
+:: we could also leave out chunklets, but that doesn't really matter that much.
+set metasrc=
:: funny hack to build a list conveniently, lol.
setlocal EnableDelayedExpansion
-for /f "tokens=2" %%f in ('findstr /B /C:":+ " "%~nx0"') do set src=!src! src/%%f
+for /f "tokens=2" %%f in ('findstr /B /C:":+ " "%~nx0"') do (
+ set src=!src! src/%%f
+ if "%%~xf"==".c" set metasrc=!metasrc! src/%%f
+)
setlocal DisableDelayedExpansion
:+ ac.c
:+ alias.c
@@ -65,6 +71,7 @@ setlocal DisableDelayedExpansion :+ bind.c
:+ clientcon.c
:+ con_.c
+:+ con_.S
:+ chatrate.c
:+ chunklets/fastspin.c
:+ chunklets/msg.c
@@ -83,6 +90,7 @@ setlocal DisableDelayedExpansion :+ gameserver.c
:+ hexcolour.c
:+ hook.c
+:+ hook.S
:+ hud.c
:+ inputhud.c
:+ kvsys.c
@@ -121,7 +129,7 @@ if %host64%==1 ( -L.build %lbcryptprimitives_host% -o .build/mkgamedata.exe src/build/mkgamedata.c src/os.c || goto :end
%HOSTCC% -fuse-ld=lld -O2 %warnings% %stdflags% -include stdbool.h ^
-L.build %lbcryptprimitives_host% -o .build/mkentprops.exe src/build/mkentprops.c src/os.c || goto :end
-.build\gluegen.exe%src% || goto :end
+.build\gluegen.exe%metasrc% || goto :end
.build\mkgamedata.exe gamedata/engine.txt gamedata/gamelib.txt gamedata/inputsystem.txt ^
gamedata/matchmaking.txt gamedata/vgui2.txt gamedata/vguimatsurface.txt gamedata/vphysics.txt || goto :end
.build\mkentprops.exe gamedata/entprops.txt || goto :end
@@ -135,7 +143,7 @@ if "%dbg%"=="1" ( ) else (
set clibs=-lucrt
)
-%CC% -fuse-ld=lld -shared -flto %ldflags% -Wl,/IMPLIB:.build/sst.lib,/Brepro,/nodefaultlib ^
+%CC% -fuse-ld=lld -shared -flto %ldflags% -Wl,-implib:.build/sst.lib,-Brepro,-nodefaultlib,-safeseh:no ^
-L.build %clibs% -lkernel32 -luser32 -lbcryptprimitives -lshlwapi -ld3d9 -ldsound ^
-ltier0 -lvstdlib -lntdll -o .build/sst.dll%objs% .build/dll.res || goto :end
:: get rid of another useless file (can we just not create this???)
@@ -160,9 +168,6 @@ if exist .build\sst.old.dll del .build\sst.old.dll >nul 2>nul %HOSTCC% -fuse-ld=lld -O2 -g %warnings% %stdflags% -include test/test.h -o .build/bitbuf.test.exe test/bitbuf.test.c || goto :end
.build\bitbuf.test.exe || goto :end
-:: special case: test must be 32-bit
-%HOSTCC% -fuse-ld=lld -m32 -O2 -g %warnings% %stdflags% -L.build -lbcryptprimitives -include test/test.h -o .build/hook.test.exe test/hook.test.c || goto :end
-.build\hook.test.exe || goto :end
%HOSTCC% -fuse-ld=lld -O2 -g %warnings% %stdflags% -include test/test.h -o .build/x86.test.exe test/x86.test.c || goto :end
.build\x86.test.exe || goto :end
diff --git a/src/asm.h b/src/asm.h new file mode 100644 index 0000000..72d5041 --- /dev/null +++ b/src/asm.h @@ -0,0 +1,39 @@ +/* + * Copyright © Michael Smith <mikesmiffy128@gmail.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef INC_ASM_H +#define INC_ASM_H + +/* + * 32-bit Windows-specific symbol mangling for global definitions. Note: does + * not apply to stdcall things; which have additional decorations. + */ +#if defined(_WIN32) && !defined(_WIN64) +#define ASM_MANGLE(x) _##x +#define ASM_MANGLE_STR(x) "_" x +#else +#define ASM_MANGLE(x) x +#define ASM_MANGLE_STR(x) x +#endif + +/* Special section definition for hook trampolines, self-modifying code, etc. */ +#define ASM_RWX_SECTION rwx +#define ASM_RWX_SECTION_STR "rwx" +#define ASM_RWX_SECTION_FLAGS "bwx" + +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/con_.S b/src/con_.S new file mode 100644 index 0000000..0c6ec2d --- /dev/null +++ b/src/con_.S @@ -0,0 +1,45 @@ +/* + * Copyright © Michael Smith <mikesmiffy128@gmail.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "asm.h" + +// On Windows, _con_colourmsg is subject to selfmod() in con_.c which overwrites +// it with another implementation on OE; put it in rwx. On Linux, .text is fine. +#ifdef _WIN32 +.section ASM_RWX_SECTION, ASM_RWX_SECTION_FLAGS +#else +.section .text, "xr" +#endif +.globl ASM_MANGLE(_con_colourmsg) +.p2align 4 +ASM_MANGLE(_con_colourmsg): + // For some reason the assembler arbitrarily refuses to put instructions in + // a section marked as containing uninitialised data. So, we have to list + // out the instruction bytes manually here: + // mov eax, [_con_iface] + .byte 0xA1 + .long ASM_MANGLE(_con_iface) + // mov [esp + 4], eax // put coniface in the empty stack slot + .byte 0x89 + .byte 0x44 + .byte 0x24 + .byte 0x04 + // jmp dword ptr _con_colourmsgf // jump to the real function + .byte 0xFF + .byte 0x25 + .long ASM_MANGLE(_con_colourmsgf) + +// vi: sw=4 ts=4 noet tw=80 cc=80 @@ -75,26 +75,11 @@ DECL_VFUNC(struct ICvar, void *, FindVar_nonp2, 12, const char *) DECL_VFUNC(struct ICvar, void *, FindVar_OE, 7, const char *) #endif -static struct ICvar *coniface; -static void *colourmsgf; +struct ICvar *_con_iface; // extern for con_.S +void *_con_colourmsgf; // " -#ifdef _WIN32 -#pragma section("selfmod", execute) -__attribute((used, section("selfmod"), noinline)) -#endif -asm_only void _con_colourmsg(void *dummy, const struct rgba *c, - const char *fmt, ...) { - // NE: ConsoleColorPrintf is virtual, so the dummy param is a carve-out for - // `this` (which is coniface). - __asm volatile ( - "mov eax, %0\n" - "mov [esp + 4], eax\n" // put coniface in the empty stack slot - "jmp dword ptr %1\n" // jump to the real function - : - : "m" (coniface), "m" (colourmsgf) - : "eax", "memory" - ); -} +// this is defined in con_.S, in order to set the proper section attributes. +void _con_colourmsg(void *dummy, const struct rgba *c, const char *fmt, ...); #ifdef _WIN32 // this function is defined as data because we'll be using it to self-modify the @@ -114,23 +99,11 @@ asm_only static void _con_colourmsg_OE(void *dummy, const struct rgba *c, "sub esp, 4\n" // pad the stack back out for the caller "jmp ebx\n" // return to saved address : - : "m" (coniface), "m" (colourmsgf) + : "m" (_con_iface), "m" (_con_colourmsgf) : "eax", "ebx", "memory" ); } #define SELFMOD_LEN 15 // above instructions assemble to this many bytes! - -static bool selfmod() { - if (!os_mprot((void *)_con_colourmsg, SELFMOD_LEN, PAGE_EXECUTE_READWRITE)) { - errmsg_errorsys("couldn't make memory writable"); - return false; - } - memcpy((void *)&_con_colourmsg, (void *)&_con_colourmsg_OE, SELFMOD_LEN); - if (!os_mprot((void *)_con_colourmsg, SELFMOD_LEN, PAGE_EXECUTE_READ)) { - errmsg_warnsys("couldn't restore self-modified page to read-only"); - } - return true; -} #endif static void VCALLCONV dtor(void *_) {} // we don't use constructors/destructors @@ -244,13 +217,13 @@ static void VCALLCONV ChangeStringValue(struct con_var *this, const char *s, float oldf) { char *old = alloca(this->v2.strlen); ChangeStringValue_common(this, &this->v2, old, s); - CallGlobalChangeCallbacks(coniface, this, old, oldf); + CallGlobalChangeCallbacks(_con_iface, this, old, oldf); } #ifdef _WIN32 static void VCALLCONV ChangeStringValue_OE(struct con_var *this, const char *s) { char *old = alloca(this->v1.strlen); ChangeStringValue_common(this, &this->v1, old, s); - CallGlobalChangeCallbacks_OE(coniface, this, old); + CallGlobalChangeCallbacks_OE(_con_iface, this, old); } #endif @@ -446,7 +419,7 @@ void con_regvar(struct con_var *v) { struct con_var_common *c = con_getvarcommon(v); c->strval = extmalloc(c->strlen); // note: _DEF_CVAR() sets strlen member memcpy(c->strval, c->defaultval, c->strlen); - RegisterConCommand(coniface, v); + RegisterConCommand(_con_iface, v); } void con_regcmd(struct con_cmd *c) { @@ -454,7 +427,7 @@ void con_regcmd(struct con_cmd *c) { if_hot (!GAMETYPE_MATCHES(OE)) if (c->base.flags & CON_INIT_HIDDEN) { c->base.flags = (c->base.flags & ~CON_INIT_HIDDEN) | _CON_NE_HIDDEN; } - RegisterConCommand(coniface, c); + RegisterConCommand(_con_iface, c); } void con_hide(struct con_cmdbase *b) { @@ -504,7 +477,7 @@ static bool find_Con_ColorPrintf() { for (uchar *p = insns; p - insns < 320;) { if (p[0] == X86_PUSHECX && p[1] == X86_PUSHIW && p[6] == X86_CALL && p[11] == X86_ALUMI8S && p[12] == X86_MODRM(3, 0, 4)) { - colourmsgf = p + 11 + mem_loads32(p + 7); + _con_colourmsgf = p + 11 + mem_loads32(p + 7); return true; } NEXT_INSN(p, "Con_ColorPrintf function"); @@ -529,39 +502,39 @@ static void badver() { } bool con_detect(int pluginver) { - if (coniface = factory_engine("VEngineCvar007", 0)) { + if (_con_iface = factory_engine("VEngineCvar007", 0)) { // GENIUS HACK (BUT STILL BAD): Portal 2 has everything in ICvar shifted // down 3 places due to the extra stuff in IAppSystem. This means that // if we look up the Portal 2-specific cvar using FindCommandBase, it // *actually* calls the const-overloaded FindVar on other branches, // which just happens to still work fine. From there, we can figure out // the actual ABI to use to avoid spectacular crashes. - if (FindCommandBase_p2(coniface, "portal2_square_portals")) { + if (FindCommandBase_p2(_con_iface, "portal2_square_portals")) { _gametype_tag |= _gametype_tag_Portal2; return true; } - if (FindCommand_nonp2(coniface, "l4d2_snd_adrenaline")) { + if (FindCommand_nonp2(_con_iface, "l4d2_snd_adrenaline")) { // while we're here, also distinguish Survivors, the stupid Japanese // arcade game a few people seem to care about for some reason // (which for some other reason also has some vtable changes) - if (FindVar_nonp2(coniface, "avatarbasemodel")) { + if (FindVar_nonp2(_con_iface, "avatarbasemodel")) { _gametype_tag |= _gametype_tag_L4DS; } else { _gametype_tag |= _gametype_tag_L4D2; } - if (FindVar_nonp2(coniface, "sv_zombie_touch_trigger_delay")) { + if (FindVar_nonp2(_con_iface, "sv_zombie_touch_trigger_delay")) { _gametype_tag |= _gametype_tag_L4D2_2125plus; } - if (FindVar_nonp2(coniface, "director_cs_weapon_spawn_chance")) { + if (FindVar_nonp2(_con_iface, "director_cs_weapon_spawn_chance")) { _gametype_tag |= _gametype_tag_TheLastStand; } return true; } - if (FindVar_nonp2(coniface, "z_difficulty")) { + if (FindVar_nonp2(_con_iface, "z_difficulty")) { _gametype_tag |= _gametype_tag_L4D1; // Crash Course update - if (FindCommand_nonp2(coniface, "director_log_scavenge_items")) { + if (FindCommand_nonp2(_con_iface, "director_log_scavenge_items")) { _gametype_tag |= _gametype_tag_L4D1_1015plus; // seems there was some code shuffling in the Mac update (1022). // this update came out like 2-3 weeks after The Sacrifice @@ -575,43 +548,45 @@ bool con_detect(int pluginver) { helpuserhelpus(pluginver, '7'); return false; } - if (coniface = factory_engine("VEngineCvar004", 0)) { + if (_con_iface = factory_engine("VEngineCvar004", 0)) { // TODO(compat): are there any cases where 004 is incompatible? could // this crash? find out! if (pluginver == 3) _gametype_tag |= _gametype_tag_2013; else _gametype_tag |= _gametype_tag_OrangeBox; // detect Portal 1 versions while we're here... - if (FindCommand_nonp2(coniface, "upgrade_portalgun")) { + if (FindCommand_nonp2(_con_iface, "upgrade_portalgun")) { _gametype_tag |= _gametype_tag_Portal1; - if (!FindVar_nonp2(coniface, "tf_escort_score_rate")) { + if (!FindVar_nonp2(_con_iface, "tf_escort_score_rate")) { _gametype_tag |= _gametype_tag_Portal1_3420; } } - else if (FindCommand_nonp2(coniface, "phys_swap")) { + else if (FindCommand_nonp2(_con_iface, "phys_swap")) { _gametype_tag |= _gametype_tag_HL2series; } return true; } - if (coniface = factory_engine("VEngineCvar003", 0)) { + if (_con_iface = factory_engine("VEngineCvar003", 0)) { #ifdef _WIN32 // there's no OE on linux! _gametype_tag |= _gametype_tag_OE; // for deletion/unlinking on unload, we need an indirect linked list // pointer. calling GetCommands gives us a direct pointer. so we have to // actually pull out the indirect pointer from the actual asm lol. - if (!find_linkedlist((uchar *)VFUNC(coniface, GetCommands_OE))) { + if (!find_linkedlist((uchar *)VFUNC(_con_iface, GetCommands_OE))) { errmsg_errorx("couldn't find command list pointer"); return false; } if (!find_argcargv()) return false; if (!find_Con_ColorPrintf()) return false; - if (!selfmod()) return false; + // note: _con_colourmsg is already RWX, just overwrite it. + memcpy((void *)&_con_colourmsg, (void *)&_con_colourmsg_OE, + SELFMOD_LEN); // NOTE: the default static struct layout is for NE; immediately after // engineapi init finishes, the generated glue code will shunt // everything along for OE if required, in shuntvars(). since all the // gluegen code is currently hooked up in sst.c this is a little bit // annoyingly removed from here. not sure how to do it better, sorry. off_cvar_common = offsetof(struct con_var, v1); - if (FindVar_OE(coniface, "mm_ai_facehugger_enablehugeattack")) { + if (FindVar_OE(_con_iface, "mm_ai_facehugger_enablehugeattack")) { _gametype_tag |= _gametype_tag_DMoMM; } return true; @@ -632,7 +607,7 @@ bool con_detect(int pluginver) { } static int *find_host_initialized() { - const uchar *insns = colourmsgf; + const uchar *insns = _con_colourmsgf; for (const uchar *p = insns; p - insns < 32;) { // cmp byte ptr [<pointer>], <value> if (p[0] == X86_ALUMI8 && p[1] == X86_MODRM(0, 7, 5)) { @@ -653,8 +628,8 @@ void con_init() { if (host_initialized && *host_initialized == 0) *host_initialized = 1; } else { - colourmsgf = coniface->vtable[vtidx_ConsoleColorPrintf]; - dllid = AllocateDLLIdentifier(coniface); + _con_colourmsgf = _con_iface->vtable[vtidx_ConsoleColorPrintf]; + dllid = AllocateDLLIdentifier(_con_iface); } void **pc = _con_vtab_cmd + 3 + NVDTOR, **pv = _con_vtab_var + 3 + NVDTOR, @@ -770,11 +745,11 @@ void con_disconnect() { return; } #endif - UnregisterConCommands(coniface, dllid); + UnregisterConCommands(_con_iface, dllid); } struct con_var *con_findvar(const char *name) { - return FindVar(coniface, name); + return FindVar(_con_iface, name); } struct con_cmd *con_findcmd(const char *name) { @@ -790,7 +765,7 @@ struct con_cmd *con_findcmd(const char *name) { return 0; } #endif - return FindCommand(coniface, name); + return FindCommand(_con_iface, name); } // NOTE: getters here still go through the parent pointer although we stopped diff --git a/src/hook.S b/src/hook.S new file mode 100644 index 0000000..7baa856 --- /dev/null +++ b/src/hook.S @@ -0,0 +1,26 @@ +/* + * Copyright © Michael Smith <mikesmiffy128@gmail.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "asm.h" +#include "rwx.h" + +#define SPACE 2048 // NOTE: MUST match SPACE in hook.c! + +.section RWX_SECTION, RWX_SECTION_FLAGS +.globl ASM_MANGLE(_hook_trampolinespc) +.comm ASM_MANGLE(_hook_trampolinespc), SPACE + +// vi: sw=4 ts=4 noet tw=80 cc=80 @@ -28,14 +28,9 @@ // Almost certainly breaks in some weird cases. Oh well! Most of the time, // vtable hooking is more reliable, this is only for, uh, emergencies. -static _Alignas(4096) uchar trampolines[4096]; -static uchar *curtrampoline = trampolines; - -bool hook_init() { - // PE doesn't support rwx sections, not sure about ELF. Meh, just set it - // here instead. - return os_mprot(trampolines, 4096, PAGE_EXECUTE_READWRITE); -} +#define SPACE 2048 // NOTE: MUST match SPACE in hook.S! +extern uchar _hook_trampolinespc[]; // defined in hook.S +static uchar *curtrampoline = _hook_trampolinespc; struct hook_inline_prep_ret hook_inline_prep(void *func, void **trampoline) { uchar *p = func; @@ -61,7 +56,8 @@ struct hook_inline_prep_ret hook_inline_prep(void *func, void **trampoline) { len += ilen; if (len >= 5) { // we should have statically made trampoline buffer size big enough - assume(curtrampoline - trampolines < sizeof(trampolines) - len - 6); + assume(curtrampoline - (uchar *)_hook_trampolinespc < + SPACE - len - 6); *curtrampoline = len; // stuff length in there for quick unhooking uchar *newtrampoline = curtrampoline + 1; curtrampoline += len + 6; @@ -22,8 +22,6 @@ #include "feature.h" #include "langext.h" -bool hook_init(); - /* * Replaces a vtable entry with a target function and returns the original * function. @@ -540,10 +540,6 @@ static void hook_plugin_unload_cbv2(struct con_cmdargs *args) { } static bool do_load(ifacefactory enginef, ifacefactory serverf) { - if_cold (!hook_init()) { - errmsg_warnsys("couldn't set up memory for function hooking"); - return false; - } factory_engine = enginef; factory_server = serverf; if_cold (!engineapi_init(ifacever)) return false; if (GAMETYPE_MATCHES(OE)) shuntvars(); // see also comment in con_detect() diff --git a/test/hook.test.c b/test/hook.test.c deleted file mode 100644 index 6a9a7b4..0000000 --- a/test/hook.test.c +++ /dev/null @@ -1,71 +0,0 @@ -/* This file is dedicated to the public domain. */ - -{.desc = "inline function hooking"}; - -#ifdef _WIN32 - -#include "../src/chunklets/x86.c" -#include "../src/hook.c" -#include "../src/os.c" - -#include <stdarg.h> -#include <stdio.h> -#include <string.h> - -// stub -void con_warn(const char *msg, ...) { - va_list l; - va_start(l, msg); - vfprintf(stderr, msg, l); - va_end(l); -} - -typedef int (*testfunc)(int, int); - -__attribute__((noinline)) static int func1(int a, int b) { return a + b; } -static int (*orig_func1)(int, int); -static int hook1(int a, int b) { return orig_func1(a, b) + 5; } - -__attribute__((noinline)) static int func2(int a, int b) { return a - b; } -static int (*orig_func2)(int, int); -static int hook2(int a, int b) { return orig_func2(a, b) + 5; } - -// basic reimplementation of old API to support existing test cases. -// XXX: we could probably have tests at the boundaries of the new API too, -// although the current tests are only testing for regressions in x86 jmp logic. -static inline void *test_hook_inline(void *func, void *target) { - void *trampoline; - struct hook_inline_prep_ret prep = hook_inline_prep(func, &trampoline); - if (prep.err) return 0; - if (!hook_inline_mprot(prep.prologue)) return 0; - hook_inline_commit(prep.prologue, target); - return trampoline; -} - -TEST("Inline hooks should be able to wrap the original function") { - if (!hook_init()) return false; - orig_func1 = (testfunc)test_hook_inline((void *)&func1, (void *)&hook1); - if (!orig_func1) return false; - return func1(5, 5) == 15; -} - -TEST("Inline hooks should be removable again") { - if (!hook_init()) return false; - orig_func1 = (testfunc)test_hook_inline((void *)&func1, (void *)&hook1); - if (!orig_func1) return false; - unhook_inline((void *)orig_func1); - return func1(5, 5) == 10; -} - -TEST("Multiple functions should be able to be inline-hooked at once") { - if (!hook_init()) return false; - orig_func1 = (testfunc)test_hook_inline((void *)&func1, (void *)&hook1); - if (!orig_func1) return false; - orig_func2 = (testfunc)test_hook_inline((void *)&func2, (void *)&hook2); - if (!orig_func2) return false; - return func2(5, 5) == 5; -} - -#endif - -// vi: sw=4 ts=4 noet tw=80 cc=80 |
