From 44902cb49cd51e2bb2f1cef05cb3c0e799b83434 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sat, 3 Aug 2024 16:11:57 +0100 Subject: Rework OS abstractions - As much as possible avoid dragging system headers into translation units. This should avoid namespace pollution and, hopefully, speed up builds a little bit. - Avoid leaning on the UCRT so much on Windows - prefer native win32 calls and native file handles except where doing so is inconvenient (in particular, for stat(), which we might try and replace later). - Also, switch from SystemFunction036 to ProcessPrng on Windows. This requires us to generate a stub for bcryptprimitives.dll because Microsoft haven't bothered to provide a link library, but the function is better-documented and seems to be a more direct under-the-hood call as well. Apparently it's what's used by the major web browsers these days, which seems like a good indication it's stable and trusted. - Lastly, remove a bunch of functions and macros and stuff that weren't actually being used. It seems good to try and keep the scope of OS-dependent stuff relatively contained and only add to it when actually required. --- src/os.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 src/os.c (limited to 'src/os.c') diff --git a/src/os.c b/src/os.c new file mode 100644 index 0000000..49cab63 --- /dev/null +++ b/src/os.c @@ -0,0 +1,204 @@ +/* + * Copyright © 2024 Michael Smith + * + * 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 +#include +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "intdefs.h" + +#ifdef _WIN32 + +int os_lasterror(void) { return GetLastError(); } + +// N.B. file handle values are 32-bit, even in 64-bit builds. I'm not crazy! + +int os_open_read(const ushort *path) { + return (int)(ssize)CreateFileW(path, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0); +} +int os_open_write(const ushort *file) { + return (int)(ssize)CreateFileW(file, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, 0); +} +int os_open_writetrunc(const ushort *file) { + return (int)(ssize)CreateFileW(file, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, 0); +} + +int os_read(int f, void *buf, int max) { + ulong n; + return ReadFile((void *)(ssize)f, buf, max, &n, 0) ? n : -1; +} +int os_write(int f, const void *buf, int len) { + ulong n; + return WriteFile((void *)(ssize)f, buf, len, &n, 0) ? n : -1; +} + +vlong os_fsize(int f) { + vlong ret; + if (!GetFileSizeEx((void *)(ssize)f, (LARGE_INTEGER *)&ret)) return -1; + return ret; +} + +void os_close(int f) { + CloseHandle((void *)(ssize)f); +} + +void os_getcwd(ushort buf[static 260]) { GetCurrentDirectoryW(260, buf); } + +bool os_mkdir(const ushort *path) { return CreateDirectoryW(path, 0); } +bool os_unlink(const ushort *path) { return DeleteFileW(path); } +bool os_rmdir(const ushort *path) { return RemoveDirectoryW(path); } + +int __stdcall ProcessPrng(char *data, usize sz); // from bcryptprimitives.dll +void os_randombytes(void *buf, int sz) { ProcessPrng(buf, sz); } + +void *os_dlhandle(const ushort *name) { + return GetModuleHandleW(name); +} +void *os_dlsym(void *restrict lib, const char *restrict s) { + return (void *)GetProcAddress(lib, s); +} +int os_dlfile(void *lib, ushort *buf, int sz) { + unsigned int n = GetModuleFileNameW(lib, buf, sz); + if (n == 0 || n == sz) return -1; + // get the canonical capitalisation, as for some reason GetModuleFileName() + // returns all lowercase. this doesn't really matter except it looks nicer + GetLongPathNameW(buf, buf, n + 1); + // the drive letter will also be lower case, if it is an actual drive letter + // of course. it should be; I'm not gonna lose sleep over UNC paths and such + if (buf[0] >= L'a' && buf[0] <= L'z' && buf[1] == L':') buf[0] &= ~32u; + return n; +} + +bool os_mprot(void *addr, int len, int mode) { + ulong old; + return !!VirtualProtect(addr, len, mode, &old); +} + +#else + +int os_lasterror(void) { return errno; } + +int os_open_read(const char *path) { + return open(path, O_RDONLY | O_CLOEXEC); +} +int os_open_write(const char *path) { + return open(path, O_RDWR | O_CLOEXEC | O_CREAT, 0644); +} +int os_open_writetrunc(const char *path) { + return open(path, O_RDWR | O_CLOEXEC | O_CREAT | O_TRUNC, 0644); +} +int os_read(int f, void *buf, int max) { return read(f, buf, max); } +int os_write(int f, const void *buf, int max) { return write(f, buf, max); } +void os_close(int f) { close(f); } + +vlong os_fsize(int f) { + struct stat s; + if (stat(f, &s) == -1) return -1; + return s.st_size; +} + +void os_getcwd(char buf[PATH_MAX]) { getcwd(buf, PATH_MAX); } + +bool os_mkdir(const char *path) { return mkdir(path, 0555) != -1; } +bool os_unlink(const char *path) { return unlink(path) != -1; } +bool os_rmdir(const char *path) { return rmdir(path) != -1; } + +void *os_dlsym(void *restrict lib, const char *restrict name) { + return dlsym(lib, name); +} + +bool os_mprot(void *addr, int len, int mode) { + // round down address and round up size + addr = (void *)((ulong)addr & ~(4095)); + len = len + 4095 & ~(4095); + return mprotect(addr, len, mode) != -1; +} + +void os_randombytes(void *buf, int sz) { while (getentropy(buf, sz) == -1); } + +#endif + +#ifdef __linux__ +// glibc-specific stuff here. 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 +struct link_map { + ulong l_addr; + const char *l_name; + void *l_ld; + struct link_map *l_next, *l_prev; + // [more private stuff below] +}; + +static struct link_map *lmbase = 0; + +void *os_dlhandle(const char *name) { + extern struct link_map *lmbase; // note: defined in sst.c for now + if (!lmbase) { // IMPORTANT: not thread safe. don't forget later! + lmbase = (struct link_map *)dlopen("libc.so.6", RTLD_LAZY | RTLD_NOLOAD); + dlclose(lmbase); // assume success + while (lmbase->l_prev) lmbase = 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 link_map *lm = 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; +} + +int os_dlfile(void *lib, char *buf, int sz) { + struct link_map *lm = lib; + int ssz = strlen(lm->l_name) + 1; + if (ssz > sz) { errno = ENAMETOOLONG; return -1; } + memcpy(buf, lm->l_name, ssz); + return ssz; +} +#endif + +// vi: sw=4 ts=4 noet tw=80 cc=80 -- cgit v1.2.3