summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcompile14
-rw-r--r--compile.bat29
-rw-r--r--src/asm.h39
-rw-r--r--src/con_.S45
-rw-r--r--src/con_.c93
-rw-r--r--src/hook.S26
-rw-r--r--src/hook.c14
-rw-r--r--src/hook.h2
-rw-r--r--src/sst.c4
-rw-r--r--test/hook.test.c71
10 files changed, 172 insertions, 165 deletions
diff --git a/compile b/compile
index 6e36880..67eebdf 100755
--- a/compile
+++ b/compile
@@ -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
diff --git a/src/con_.c b/src/con_.c
index ff71383..6d5ce03 100644
--- a/src/con_.c
+++ b/src/con_.c
@@ -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
diff --git a/src/hook.c b/src/hook.c
index 0355c18..baaa710 100644
--- a/src/hook.c
+++ b/src/hook.c
@@ -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;
diff --git a/src/hook.h b/src/hook.h
index 0aeae73..26796e2 100644
--- a/src/hook.h
+++ b/src/hook.h
@@ -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.
diff --git a/src/sst.c b/src/sst.c
index 17e020f..64dd303 100644
--- a/src/sst.c
+++ b/src/sst.c
@@ -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