diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/abi.h | 50 | ||||
-rw-r--r-- | src/ac.c | 6 | ||||
-rw-r--r-- | src/build/cmeta.c | 35 | ||||
-rw-r--r-- | src/build/codegen.c | 5 | ||||
-rw-r--r-- | src/build/mkgamedata.c | 2 | ||||
-rw-r--r-- | src/chunklets/fastspin.c | 1 | ||||
-rw-r--r-- | src/con_.c | 5 | ||||
-rw-r--r-- | src/con_.h | 18 | ||||
-rw-r--r-- | src/extmalloc.c | 50 | ||||
-rw-r--r-- | src/extmalloc.h | 8 | ||||
-rw-r--r-- | src/gameinfo.c | 3 | ||||
-rw-r--r-- | src/hook.c | 45 | ||||
-rw-r--r-- | src/hook.h | 6 | ||||
-rw-r--r-- | src/os-unix.h | 53 | ||||
-rw-r--r-- | src/sst.c | 44 | ||||
-rw-r--r-- | src/stubs/tier0.c | 6 |
16 files changed, 218 insertions, 119 deletions
@@ -74,7 +74,7 @@ struct msvc_rtti_locator { // I mean seriously look at this crap! #define DEF_MSVC_BASIC_RTTI(mod, name, vtab, typestr) \ -const mod struct msvc_rtti_locator name; \ +mod const struct msvc_rtti_locator name; \ static const struct { \ struct msvc_rtti_descriptor_head d; \ char classname[sizeof("" typestr)]; \ @@ -93,24 +93,48 @@ mod const struct msvc_rtti_locator name = { \ #else -#warning FIXME! More stuff needs to be implemented/fixed here! +struct itanium_type_info_vtable { + void *dtor1, *dtor2; +}; struct itanium_type_info { - struct itanium_type_info_vtable { - void *dtor1, *dtor2; // ??? - // there's some more functions here in libstdc++: is_pointer, - // is_function, do_catch, etc. however they're not specified in itanium - // abi doc. hoping to do without them, but we'll see I guess - } *vtable; + struct itanium_type_info_vtable *vtable; const char *name; }; +struct itanium_vmi_type_info { + struct itanium_type_info base; + uint flags; + uint nbases; + // then there's a flexible array of `__base_class_type_info`s, but for our + // purposes we can just have zero bases and avoid dealing with more nonsense +}; + +struct itanium_type_info_vtable_wrapper { + // Oh CHRIST, Unix RTTI is bonkers too. Each type_info is itself one of + // several subclasses with its own RTTI; the RTTI has RTTI! + ssize topoffset; + struct itanium_type_info *rtti; + struct itanium_type_info_vtable vtable; +}; + +// `typeinfo for __cxxabiv1::__vmi_class_type_info` in libcxxabi - type_info +// comparison is identity-based, so we need to import this from a shared object +// in order to implement the same ABI. I honestly don't know whether this or the +// MSVC design is more stupid. +extern struct itanium_type_info _ZTIN10__cxxabiv121__vmi_class_type_infoE; + +// XXX: just static for now, as only used for cvars and lto would dedupe anyway. +static void _itanium_type_info_dtor(void *this) {} + #define DEF_ITANIUM_BASIC_RTTI(mod, name, typestr) \ - mod struct itanium_type_info name = { \ - &(struct itanium_type_info_vtable){ \ - 0, 0 /* FIXME/TEMP: definitely need real functions here! */ \ - }, \ - typestr \ + mod const struct itanium_vmi_type_info name = { \ + &(struct itanium_type_info_vtable_wrapper){ \ + 0, &_ZTIN10__cxxabiv121__vmi_class_type_infoE, \ + (void *)&_itanium_type_info_dtor, (void *)&_itanium_type_info_dtor \ + }.vtable, \ + typestr, \ + 0, 0 \ }; #endif @@ -401,7 +401,7 @@ INIT { if (madvise(keybox, 4096, MADV_DONTFORK) == -1 || madvise(keybox, 4096, MADV_DONTDUMP) == - 1 || mlock(keybox, 4096) == -1) { - errormsg_errorstd("couldn't secure session state"); + errmsg_errorstd("couldn't secure session state"); goto e; } // TODO(linux): call other init things @@ -420,7 +420,7 @@ INIT { #ifdef _WIN32 e: WerUnregisterExcludedMemoryBlock(keybox); // this'd better not fail! e2: VirtualFree(keybox, 4096, MEM_RELEASE); -#elif +#else e: munmap(keybox, 4096); #endif unhook_inline((void *)orig_DispatchInputEvent); @@ -433,7 +433,7 @@ END { WerUnregisterExcludedMemoryBlock(keybox); // this'd better not fail! VirtualFree(keybox, 4096, MEM_RELEASE); win32_end(); -#elif defined(__linux__) +#else munmap(keybox, 4096); // TODO(linux): call other cleanup things #endif diff --git a/src/build/cmeta.c b/src/build/cmeta.c index 40aba3a..0c9b3ca 100644 --- a/src/build/cmeta.c +++ b/src/build/cmeta.c @@ -97,24 +97,17 @@ static char *join_tokens(const Token *tok, const Token *end) { #include "../3p/openbsd/asprintf.c" // missing from libc; plonked here for now #endif -static void die1(const char *s) { +static void die(const char *s) { fprintf(stderr, "cmeta: fatal: %s\n", s); exit(100); } -#ifndef _WIN32 -static void die2(const char *s1, const char *s2) { - fprintf(stderr, "cmeta: fatal: %s%s\n", s1, s2); - exit(100); -} -#endif - static char *readsource(const os_char *f) { int fd = os_open(f, O_RDONLY); if (fd == -1) return 0; uint bufsz = 8192; char *buf = malloc(bufsz); - if (!buf) die1("couldn't allocate memory"); + if (!buf) die("couldn't allocate memory"); int nread; int off = 0; while ((nread = read(fd, buf + off, bufsz - off)) > 0) { @@ -122,12 +115,12 @@ static char *readsource(const os_char *f) { if (off == bufsz) { bufsz *= 2; // somewhat arbitrary cutoff - if (bufsz == 1 << 30) die1("input file is too large"); + if (bufsz == 1 << 30) die("input file is too large"); buf = realloc(buf, bufsz); - if (!buf) die1("couldn't reallocate memory"); + if (!buf) die("couldn't reallocate memory"); } } - if (nread == -1) die1("couldn't read file"); + if (nread == -1) die("couldn't read file"); buf[off] = 0; close(fd); return buf; @@ -141,7 +134,7 @@ const struct cmeta *cmeta_loadfile(const os_char *f) { if (!buf) return 0; #ifdef _WIN32 char *realname = malloc(wcslen(f) + 1); - if (!realname) die1("couldn't allocate memory"); + if (!realname) die("couldn't allocate memory"); // XXX: being lazy about Unicode right now; a general purpose tool should // implement WTF8 or something. SST itself doesn't have any unicode paths // though, so don't really care as much. @@ -171,7 +164,7 @@ void cmeta_includes(const struct cmeta *cm, if (tp->kind == TK_STR) { // include strings are a special case; they don't have \escapes. char *copy = malloc(tp->len - 1); - if (!copy) die1("couldn't allocate memory"); + if (!copy) die("couldn't allocate memory"); memcpy(copy, tp->loc + 1, tp->len - 2); copy[tp->len - 2] = '\0'; cb(copy, false, ctxt); @@ -240,13 +233,13 @@ void cmeta_conmacros(const struct cmeta *cm, if (isplusminus) { // XXX: this is stupid but whatever char *plusname = malloc(sizeof("PLUS_") + tp->len); - if (!plusname) die1("couldn't allocate memory"); + if (!plusname) die("couldn't allocate memory"); memcpy(plusname, "PLUS_", 5); memcpy(plusname + sizeof("PLUS_") - 1, tp->loc, tp->len); plusname[sizeof("PLUS_") - 1 + tp->len] = '\0'; cb(plusname, false, unreg); char *minusname = malloc(sizeof("MINUS_") + tp->len); - if (!minusname) die1("couldn't allocate memory"); + if (!minusname) die("couldn't allocate memory"); memcpy(minusname, "MINUS_", 5); memcpy(minusname + sizeof("MINUS_") - 1, tp->loc, tp->len); minusname[sizeof("MINUS_") - 1 + tp->len] = '\0'; @@ -254,7 +247,7 @@ void cmeta_conmacros(const struct cmeta *cm, } else { char *name = malloc(tp->len + 1); - if (!name) die1("couldn't allocate memory"); + if (!name) die("couldn't allocate memory"); memcpy(name, tp->loc, tp->len); name[tp->len] = '\0'; cb(name, isvar, unreg); @@ -318,7 +311,7 @@ void cmeta_featinfomacros(const struct cmeta *cm, void (*cb)( if (equal(tp->next, "(") && tp->next->next) { tp = tp->next->next; char *param = malloc(tp->len + 1); - if (!param) die1("couldn't allocate memory"); + if (!param) die("couldn't allocate memory"); memcpy(param, tp->loc, tp->len); param[tp->len] = '\0'; cb(type, param, ctxt); @@ -335,10 +328,10 @@ static void pushmacroarg(const Token *last, const char *start, struct vec_str *list) { int len = last->loc - start + last->len; char *dup = malloc(len + 1); - if (!dup) die1("couldn't allocate memory"); + if (!dup) die("couldn't allocate memory"); memcpy(dup, start, len); dup[len] = '\0'; - if (!vec_push(list, dup)) die1("couldn't append to array"); + if (!vec_push(list, dup)) die("couldn't append to array"); } // XXX: maybe this should be used for the other functions too. it'd be less ugly @@ -400,7 +393,7 @@ void cmeta_evhandlermacros(const struct cmeta *cm, const char *modname, if (equal(tp, "HANDLE_EVENT") && equal(tp->next, "(")) { tp = tp->next->next; char *name = malloc(tp->len + 1); - if (!name) die1("couldn't allocate memory"); + if (!name) die("couldn't allocate memory"); memcpy(name, tp->loc, tp->len); name[tp->len] = '\0'; cb_handler(name, modname); diff --git a/src/build/codegen.c b/src/build/codegen.c index bb25395..3cf4d8b 100644 --- a/src/build/codegen.c +++ b/src/build/codegen.c @@ -14,6 +14,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -118,7 +119,7 @@ static void onfeatinfo(enum cmeta_featmacro type, const char *param, switch (type) { case CMETA_FEAT_REQUIRE:; bool optional = false; goto dep; case CMETA_FEAT_REQUEST: optional = true; -dep: struct feature *dep = skiplist_get_feature(&features, param); +dep:; struct feature *dep = skiplist_get_feature(&features, param); if (optional) dep->is_requested = true; if (!dep) { fprintf(stderr, "codegen: error: feature `%s` tried to depend " @@ -308,7 +309,7 @@ int OS_MAIN(int argc, os_char *argv[]) { } p[arglen] = '\0'; #else - const char *p = p->path; + const char *p = pi->path; #endif const char *lastslash = p - 1; for (; *p; ++p) { diff --git a/src/build/mkgamedata.c b/src/build/mkgamedata.c index fdb2aef..d49eef5 100644 --- a/src/build/mkgamedata.c +++ b/src/build/mkgamedata.c @@ -121,7 +121,7 @@ static void kv_cb(enum kv_token type, const char *p, uint len, void *ctxt) { } state->curent = state->curent->parent; break; - case KV_VAL: case KV_VAL_QUOTED: + case KV_VAL: case KV_VAL_QUOTED:; char *s = malloc(len + 1); if (!s) die("couldn't allocate value string"); memcpy(s, p, len); s[len] = '\0'; diff --git a/src/chunklets/fastspin.c b/src/chunklets/fastspin.c index bfaaf9b..e29bd68 100644 --- a/src/chunklets/fastspin.c +++ b/src/chunklets/fastspin.c @@ -51,6 +51,7 @@ void _mm_pause(); // don't pull in emmintrin.h for this #include <linux/futex.h> #include <sys/syscall.h> +#include <unistd.h> // some arches only have a _time64 variant. doesn't actually matter what // timespec ABI is used here, as we don't use/expose that functionality @@ -295,10 +295,13 @@ struct _con_vtab_var_wrap _con_vtab_var_wrap = { (void *)&AddFlags_var }; -void *_con_vtab_iconvar[7] = { +struct _con_vtab_iconvar_wrap _con_vtab_iconvar_wrap = { #ifdef _WIN32 0 // because of crazy overload vtable order we can't prefill *anything* #else + // RTTI members first on linux: + -offsetof(struct con_var, vtable_iconvar), + &varrtti, // colour is the last of the 4 on linux so we can at least prefill these 3 (void *)&SetValue_str_thunk, (void *)&SetValue_f_thunk, @@ -112,11 +112,6 @@ struct con_cmd { // ConCommand in engine bool has_complcb : 1, use_newcb : 1, use_newcmdiface : 1; }; -// con_var will be a bit different on linux; see offset_to_top etc. -#ifdef __linux__ -#warning FIXME: redo multi-vtable crap for itanium ABI! -#endif - struct con_var { // ConVar in engine struct con_cmdbase base; void **vtable_iconvar; // IConVar in engine (pure virtual) @@ -175,7 +170,7 @@ con_cmdcbv1 con_getcmdcbv1(const struct con_cmd *cmd); */ #if defined(__GNUC__) || defined(__clang__) #ifdef _WIN32 -#define __asm__(x) __asm__("_" x) // stupid mangling meme (not on linux, right?) +#define __asm__(x) __asm__("_" x) // stupid mangling meme, only on windows! #endif void con_msg(const char *fmt, ...) _CON_PRINTF(1, 2) __asm__("Msg"); void con_warn(const char *fmt, ...) _CON_PRINTF(1, 2) __asm__("Warning"); @@ -214,12 +209,19 @@ extern struct _con_vtab_var_wrap { #else // itanium ABI also has the top offset/"whole object" offset in libstdc++ ssize topoffset; - struct itanium_type_info *rtti; + const struct itanium_vmi_type_info *rtti; #endif void *vtable[19]; } _con_vtab_var_wrap; #define _con_vtab_var (_con_vtab_var_wrap.vtable) -extern void *_con_vtab_iconvar[]; +extern struct _con_vtab_iconvar_wrap { +#ifndef _WIN32 + ssize topoffset; + const struct itanium_vmi_type_info *rtti; +#endif + void *vtable[7]; +} _con_vtab_iconvar_wrap; +#define _con_vtab_iconvar _con_vtab_iconvar_wrap.vtable #define _DEF_CVAR(name_, desc, value, hasmin_, min, hasmax_, max, flags_) \ static struct con_var _cvar_##name_ = { \ diff --git a/src/extmalloc.c b/src/extmalloc.c index 3e14e0d..a23dc2a 100644 --- a/src/extmalloc.c +++ b/src/extmalloc.c @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2023 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 @@ -14,39 +14,49 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#ifndef _WIN32 +#include <stdlib.h> +#endif + #include "intdefs.h" #include "vcall.h" -// XXX: this is duped from os.h because I don't want to pull in Windows.h, -// consider splitting out the IMPORT/EXPORT defs to some other thing? -#ifdef _WIN32 -#define IMPORT __declspec(dllimport) // only needed for variables -#else -#define IMPORT -#endif - // XXX: not sure if "ext" is the best naming convention? use brain later -IMPORT void *g_pMemAlloc; +#ifdef _WIN32 + +__declspec(dllimport) void *g_pMemAlloc; // this interface has changed a bit between versions but thankfully the basic // functions we care about have always been at the start - nice and easy. -// unfortunately though, because the debug and non-debug versions are overloads -// and Microsoft are a bunch of crazies who decided vtable order should be -// affected by naming (overloads are grouped, and *reversed* inside of a -// group!?), we get this amusing ABI difference between platforms: -#ifdef _WIN32 +// note that since Microsoft are a bunch of crazies, overloads are grouped and +// reversed so the vtable order here is maybe not what you'd expect otherwise. DECL_VFUNC(void *, Alloc, 1, usize) DECL_VFUNC(void *, Realloc, 3, void *, usize) DECL_VFUNC(void, Free, 5, void *) -#else -DECL_VFUNC(void *, Alloc, 0, usize) -DECL_VFUNC(void *, Realloc, 1, void *, usize) -DECL_VFUNC(void, Free, 2, void *) -#endif void *extmalloc(usize sz) { return Alloc(g_pMemAlloc, sz); } void *extrealloc(void *mem, usize sz) { return Realloc(g_pMemAlloc, mem, sz); } void extfree(void *mem) { Free(g_pMemAlloc, mem); } +#else + +void Error(const char *fmt, ...); // stub left out of con_.h (not that useful) + +// Linux Source doesn't seem to bother with the custom allocator stuff at all. +// We still want to check for OoM though, because turning off overcommit is a +// right, not a privilege. Like func_vehicle. +void *extmalloc(usize sz) { + void *ret = malloc(sz); + if (!ret) Error("sst: out of memory"); + return ret; +} +void *extrealloc(void *mem, usize sz) { + void *ret = realloc(mem, sz); + if (!ret) Error("sst: out of memory"); + return ret; +} +// note: extfree is #defined to free in the header +#endif + // vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/extmalloc.h b/src/extmalloc.h index eb41730..6297360 100644 --- a/src/extmalloc.h +++ b/src/extmalloc.h @@ -23,10 +23,18 @@ * These functions are just like malloc/realloc/free, but they call into * Valve's memory allocator wrapper, which ensures that allocations crossing * plugin/engine boundaries won't cause any weird issues. + * + * On Linux, there is no allocation wrapper, but these still do their own OoM + * checking, so there's no need to think about that. */ void *extmalloc(usize sz); void *extrealloc(void *mem, usize sz); +#ifdef _WIN32 void extfree(void *mem); +#else +void free(void *); +#define extfree free +#endif #endif diff --git a/src/gameinfo.c b/src/gameinfo.c index 3713d6c..85220a8 100644 --- a/src/gameinfo.c +++ b/src/gameinfo.c @@ -80,7 +80,8 @@ bool gameinfo_init(void) { title[len - 9] = '\0'; } #else -#error TODO(linux): grab window handle and title from SDL (a bit involved...) +//#error TODO(linux): grab window handle and title from SDL (a bit involved...) + gameinfo_title = "Linux Game With As Yet Unkown Title"; #endif // SUPER crude algorithm to force uppercase titles like HALF-LIFE 2 or @@ -27,8 +27,6 @@ // Almost certainly breaks in some weird cases. Oh well! Most of the time, // vtable hooking is more reliable, this is only for, uh, emergencies. -#if defined(_WIN32) && !defined(_WIN64) - #if defined(__GNUC__) || defined(__clang__) __attribute__((aligned(4096))) #elif defined(_MSC_VER) @@ -45,6 +43,18 @@ bool hook_init(void) { return os_mprot(trampolines, sizeof(trampolines), PAGE_EXECUTE_READWRITE); } +static inline void iflush(void *p, int len) { +#if defined(_WIN32) + // -1 is the current process, and it's a constant in the WDK, so it's + // assumed we can safely avoid the useless GetCurrentProcess call + FlushInstructionCache((void *)-1, p, len); +#elif defined(__GNUC__) + __builtin___clear_cache((char *)p, (char *)p + len); +#else +#error no way to flush instruction cache +#endif +} + void *hook_inline(void *func_, void *target) { uchar *func = func_; // dumb hack: if we hit some thunk that immediately jumps elsewhere (which @@ -74,30 +84,21 @@ void *hook_inline(void *func_, void *target) { } } // for simplicity, just bump alloc the trampoline. no need to free anyway - if (nexttrampoline - trampolines > sizeof(trampolines) - len - 6) goto nosp; - // TODO(opt): stop pretending to be thread-safe, it's just slowing us down - uchar *trampoline = (uchar *)InterlockedExchangeAdd( - (volatile long *)&nexttrampoline, len + 6); - // avoid TOCTOU - if (trampoline - trampolines > sizeof(trampolines) - len - 6) { -nosp: con_warn("hook_inline: out of trampoline space\n"); + if (nexttrampoline - trampolines > sizeof(trampolines) - len - 6) { + con_warn("hook_inline: out of trampoline space\n"); return 0; } + uchar *trampoline = nexttrampoline; + nexttrampoline += len + 6; // NOT thread-safe. we don't need that anyway! *trampoline++ = len; // stick length in front for quicker unhooking memcpy(trampoline, func, len); trampoline[len] = X86_JMPIW; uint diff = func - (trampoline + 5); // goto the continuation memcpy(trampoline + len + 1, &diff, 4); - uchar jmp[8]; - jmp[0] = X86_JMPIW; diff = (uchar *)target - (func + 5); // goto the hook target - memcpy(jmp + 1, &diff, 4); - // pad with original bytes so we can do an 8-byte atomic write - memcpy(jmp + 5, func + 5, 3); - *(volatile uvlong *)func = *(uvlong *)jmp; // (assuming function is aligned) - // -1 is the current process, and it's a constant in the WDK, so it's - // assumed we can safely avoid the useless GetCurrentProcess call - FlushInstructionCache((void *)-1, func, len); + func[0] = X86_JMPIW; + memcpy(func + 1, &diff, 4); + iflush(func, 5); return trampoline; } @@ -107,13 +108,7 @@ void unhook_inline(void *orig) { int off = mem_load32(p + len + 1); uchar *q = p + off + 5; memcpy(q, p, 5); // XXX: not atomic atm! (does any of it even need to be?) - FlushInstructionCache((void *)-1, q, 5); + iflush(q, 5); } -#else - -// TODO(linux): Implement for Linux and/or x86_64 when needed... - -#endif - // vi: sw=4 ts=4 noet tw=80 cc=80 @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Michael Smith <mikesmiffy128@gmail.com> + * Copyright © 2023 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 @@ -41,6 +41,10 @@ static inline void unhook_vtable(void **vtable, usize off, void *orig) { /* * Returns a trampoline pointer, or null if hooking failed. Unlike hook_vtable, * handles memory protection for you. + * + * This function is not remotely thread-safe, and should never be called from + * any thread besides the main one nor be used to hook anything that gets called + * from other threads. */ void *hook_inline(void *func, void *target); diff --git a/src/os-unix.h b/src/os-unix.h index a25d8ed..097d300 100644 --- a/src/os-unix.h +++ b/src/os-unix.h @@ -47,31 +47,58 @@ typedef char os_char; static inline void *os_dlopen(const char *name) { return dlopen(name, RTLD_NOW); } -static inline void *os_dlhandle(const char *name) { - void *ret = dlopen(name, RTLD_NOW | RTLD_NOLOAD); - if (ret) dlclose(ret); - return ret; -} #define os_dlsym dlsym #ifdef __linux__ // note: this is glibc-specific. it shouldn't be used in build-time code, just // the plugin itself (that really shouldn't be a problem). + +// private struct hidden behind _GNU_SOURCE. see dlinfo(3) or <link.h> +struct gnu_link_map { + unsigned long l_addr; + const char *l_name; + void *l_ld; + struct gnu_link_map *l_next, *l_prev; + // [more private stuff below] +}; + +static inline void *os_dlhandle(const char *name) { + extern struct gnu_link_map *_os_lmbase; // note: defined in sst.c for now + if (!_os_lmbase) { // IMPORTANT: not thread safe. don't forget later! + _os_lmbase = (struct gnu_link_map *)dlopen("libc.so.6", + RTLD_LAZY | RTLD_NOLOAD); + dlclose(_os_lmbase); // assume success + while (_os_lmbase->l_prev) _os_lmbase = _os_lmbase->l_prev; + } + // this is a tiny bit crude, but basically okay. we just want to find + // something that roughly matches the basename, rather than needing an exact + // path, in a manner vaguely similar to Windows' GetModuleHandle(). that way + // we can just look up client.so or something without having to figure out + // where exactly that is. + for (struct gnu_link_map *lm = _os_lmbase; lm; lm = lm->l_next) { + if (name[0] == '/') { + if (!strcmp(name, lm->l_name)) return lm; + continue; + } + int namelen = strlen(lm->l_name); + int sublen = strlen(name); + if (sublen >= namelen) continue; + if (lm->l_name[namelen - sublen - 1] == '/' && !memcmp( + lm->l_name + namelen - sublen, name, sublen)) { + return lm; + } + } + return 0; +} + static inline int os_dlfile(void *m, char *buf, int sz) { - // private struct hidden behind _GNU_SOURCE. see dlinfo(3) or <link.h> - struct gnu_link_map { - unsigned long l_addr; - const char *l_name; - void *l_ld; - struct gnu_link_map *l_next, *l_prev; - // [more private stuff below] - }; struct gnu_link_map *lm = m; int ssz = strlen(lm->l_name) + 1; if (ssz > sz) { errno = ENAMETOOLONG; return -1; } memcpy(buf, lm->l_name, ssz); return ssz; } + #endif // unix mprot flags are much nicer but cannot be defined in terms of the windows @@ -14,6 +14,9 @@ * PERFORMANCE OF THIS SOFTWARE. */ +#ifndef _WIN32 +#include <stdlib.h> // unsetenv +#endif #include <string.h> #ifdef _WIN32 @@ -71,6 +74,8 @@ static void *ownhandle(void) { } return cached; } + +struct gnu_link_map *_os_lmbase = 0; // XXX: stupid place to put this, oh well #endif #ifdef _WIN32 @@ -139,9 +144,32 @@ DEF_CCMD_HERE(sst_autoload_enable, "Register SST to load on game startup", 0) { return; } // arbitrary aesthetic judgement - for (os_char *p = relpath; *p; ++p) if (*p == L'\\') *p = L'/'; + for (ushort *p = relpath; *p; ++p) if (*p == L'\\') *p = L'/'; #else -#error TODO(linux): implement this, it's late right now and I can't be bothered + const char *p = path, *q = startdir; + int slash = 0; + int i = 1; + for (;; ++i) { + if (p[i] == '/' && (q[i] == '/' || q[i] == '\0')) slash = i; + if (p[i] != q[i]) break; + } + int rellen = strlen(p + slash + 1) + 1; // include \0 + char *r = relpath; + if (q[i]) { + if (r - relpath >= PATH_MAX - 3 - rellen) { + errmsg_errorx("path to game is too long"); // eh... + return; + } + for (;;) { + r[0] = '.'; r[1] = '.'; r[2] = '/'; + r += 3; + for (;;) { + if (q[++i] == '/') break; + if (!q[i]) goto c; + } + } + } +c: memcpy(r, p + slash + 1, rellen); #endif int len = os_strlen(gameinfo_gamedir); if (len + sizeof("/addons/" VDFBASENAME ".vdf") > @@ -231,8 +259,8 @@ static void do_featureinit(void) { "CreateInterface"))) { errmsg_warndl("couldn't get client's CreateInterface"); } - void *inputsystemlib = os_dlhandle(OS_LIT("bin/") OS_LIT(OS_DLPREFIX) - OS_LIT("inputsystem") OS_LIT(OS_DLSUFFIX)); + void *inputsystemlib = os_dlhandle(OS_LIT("bin/") OS_LIT("inputsystem") + OS_LIT(OS_DLSUFFIX)); if (!inputsystemlib) { errmsg_warndl("couldn't get the input system library"); } @@ -240,8 +268,9 @@ static void do_featureinit(void) { "CreateInterface"))) { errmsg_warndl("couldn't get input system's CreateInterface"); } - inputsystem = factory_inputsystem("InputSystemVersion001", 0); - if (!inputsystem) errmsg_warnx("missing input system interface"); + else if (!(inputsystem = factory_inputsystem("InputSystemVersion001", 0))) { + errmsg_warnx("missing input system interface"); + } // ... and now for the real magic! initfeatures(); @@ -398,9 +427,6 @@ static void do_unload(void) { } #endif endfeatures(); -#ifdef __linux__ - if (clientlib) dlclose(clientlib); -#endif con_disconnect(); } diff --git a/src/stubs/tier0.c b/src/stubs/tier0.c index 2c9c578..75fb2db 100644 --- a/src/stubs/tier0.c +++ b/src/stubs/tier0.c @@ -4,6 +4,10 @@ F(Msg) F(Warning) -V(g_pMemAlloc) +#ifdef _WIN32 +V(g_pMemAlloc) // this doesn't exist at all on Linux +#else +F(Error) // only used for extmalloc() and nothing else :^) +#endif // vi: sw=4 ts=4 noet tw=80 cc=80 |