From b18326a75078530df7712667f41b4ea354e1da3e Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Wed, 23 Mar 2022 02:38:21 +0000 Subject: Work around plugin unloading bug in old branches This also introduces some stuff for interacting with the current plugin list. Other plugin management utilies are Coming Soon... --- compile.bat | 2 +- src/sst.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/compile.bat b/compile.bat index b2a880a..da1e2d2 100644 --- a/compile.bat +++ b/compile.bat @@ -49,7 +49,7 @@ call :cc src/kv.c || exit /b call :cc src/rinput.c || exit /b call :cc src/sst.c || exit /b call :cc src/udis86.c || exit /b -clang -m32 -shared -O2 -flto -fuse-ld=lld -Wl,/implib:.build/sst.lib,/Brepro ^ +clang -m32 -fuse-ld=lld -shared -O2 -flto -Wl,/IMPLIB:.build/sst.lib,/Brepro ^ -L.build -luser32 -ladvapi32 -lshlwapi -ltier0 -lvstdlib -o sst.dll%objs% .build/dll.res || exit /b :: get rid of another useless file (can we just not create this???) del .build\sst.lib diff --git a/src/sst.c b/src/sst.c index 5c1568d..aeb7f4c 100644 --- a/src/sst.c +++ b/src/sst.c @@ -39,6 +39,7 @@ static int plugin_ver; // this is where we start dynamically adding virtual functions, see vtable[] // array below static const void **vtable_firstdiff; +static const void *const *const plugin_obj; // most plugin callbacks are unused - define dummy functions for each signature static void VCALLCONV nop_v_v(void *this) {} @@ -91,6 +92,56 @@ static const char *VCALLCONV GetStringForSymbol_hook(void *this, int s) { // vstdlib symbol, only currently used in l4d2 but exists everywhere so oh well IMPORT void *KeyValuesSystem(void); +// XXX: not sure if all this stuff should, like, go somewhere? + +struct CUtlMemory { + void *mem; + int alloccnt; + int growsz; +}; + +struct CUtlVector { + struct CUtlMemory m; + int sz; + void *mem_again_for_some_reason; +}; + +struct CServerPlugin /* : IServerPluginHelpers */ { + void **vtable; + struct CUtlVector plugins; + /*IPluginHelpersCheck*/ void *pluginhlpchk; +}; + +struct CPlugin { + char description[128]; + bool paused; + void *theplugin; // our own "this" pointer (or whichever other plugin it is) + int ifacever; + // should be the plugin library, but in old Source branches it's just null, + // because CServerPlugin::Load() erroneously shadows this field with a local + void *module; +}; + +#ifdef _WIN32 +extern long __ImageBase; // this is actually the PE header struct but don't care +#define ownhandle() ((void *)&__ImageBase) +#else +// sigh, _GNU_SOURCE crap. define here instead >:( +typedef struct { + const char *dli_fname; + void *dli_fbase; + const char *dli_sname; + void *dli_saddr; +} Dl_info; +int dladdr1(const void *addr, Dl_info *info, void **extra_info, int flags); +static inline void *ownhandle(void) { + Dl_info dontcare; + void *dl; + dladdr1((void *)&ownhandle, &dontcare, &dl, /*RTLD_DL_LINKMAP*/ 2); + return dl; +} +#endif + static bool do_load(ifacefactory enginef, ifacefactory serverf) { factory_engine = enginef; factory_server = serverf; #ifndef __linux__ @@ -162,6 +213,24 @@ e: con_colourmsg(RGBA(64, 255, 64, 255), } static void do_unload(void) { + struct CServerPlugin *pluginhandler = + factory_engine("ISERVERPLUGINHELPERS001", 0); + if (pluginhandler) { // if not, oh well too bad we tried :^) + struct CPlugin **plugins = pluginhandler->plugins.m.mem; + int n = pluginhandler->plugins.sz; + for (struct CPlugin **pp = plugins; pp - plugins < n; ++pp) { + if ((*pp)->theplugin = (void *)&plugin_obj) { + // see comment in CPlugin above. setting this to the real handle + // right before the engine tries to unload us allows it to + // actually unload us instead of just doing nothing. + // in newer branches that don't have this bug, this is still + // correct anyway so no need to bother checking. + (*pp)->module = ownhandle(); + break; + } + } + } + if (has_autojump) autojump_end(); if (has_demorec) demorec_end(); #ifdef _WIN32 @@ -196,7 +265,7 @@ static bool VCALLCONV Load(void *this, ifacefactory enginef, return already_loaded; } -static void Unload(void *this) { +static void VCALLCONV Unload(void *this) { // the game tries to unload on a failed load, for some reason if (skip_unload) { skip_unload = false; -- cgit v1.2.3