/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ 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. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #define _HOSTLINUX 1 #define _HOSTWINDOWS 4 #define _HOSTXNU 8 #define _HOSTOPENBSD 16 #define _HOSTFREEBSD 32 #define _HOSTNETBSD 64 #ifndef SUPPORT_VECTOR #define SUPPORT_VECTOR -1 #endif #ifdef __aarch64__ #define IsAarch64() 1 #else #define IsAarch64() 0 #endif #define SupportsLinux() (SUPPORT_VECTOR & _HOSTLINUX) #define SupportsXnu() (SUPPORT_VECTOR & _HOSTXNU) #define SupportsWindows() (SUPPORT_VECTOR & _HOSTWINDOWS) #define SupportsFreebsd() (SUPPORT_VECTOR & _HOSTFREEBSD) #define SupportsOpenbsd() (SUPPORT_VECTOR & _HOSTOPENBSD) #define SupportsNetbsd() (SUPPORT_VECTOR & _HOSTNETBSD) #define IsLinux() (SupportsLinux() && __crt.os == _HOSTLINUX) #define IsXnu() (SupportsXnu() && __crt.os == _HOSTXNU) #define IsWindows() (SupportsWindows() && __crt.os == _HOSTWINDOWS) #define IsFreebsd() (SupportsFreebsd() && __crt.os == _HOSTFREEBSD) #define IsOpenbsd() (SupportsOpenbsd() && __crt.os == _HOSTOPENBSD) #define IsNetbsd() (SupportsNetbsd() && __crt.os == _HOSTNETBSD) #define O_RDONLY 0 #define PROT_NONE 0 #define PROT_READ 1 #define PROT_WRITE 2 #define PROT_EXEC 4 #define MAP_SHARED 1 #define MAP_PRIVATE 2 #define MAP_FIXED 16 #define MAP_ANONYMOUS 32 #define MAP_EXECUTABLE 4096 #define MAP_NORESERVE 16384 #define ELFCLASS32 1 #define ELFDATA2LSB 1 #define EM_NEXGEN32E 62 #define EM_AARCH64 183 #define ET_EXEC 2 #define ET_DYN 3 #define PT_LOAD 1 #define PT_DYNAMIC 2 #define PT_INTERP 3 #define EI_CLASS 4 #define EI_DATA 5 #define PF_X 1 #define PF_W 2 #define PF_R 4 #define AT_PHDR 3 #define AT_PHENT 4 #define AT_PHNUM 5 #define AT_PAGESZ 6 #define AT_EXECFN_LINUX 31 #define AT_EXECFN_NETBSD 2014 #define X_OK 1 #define XCR0_SSE 2 #define XCR0_AVX 4 #define PR_SET_MM 35 #define PR_SET_MM_EXE_FILE 13 #define EIO 5 #define EBADF 9 #define kNtInvalidHandleValue -1L #define kNtStdInputHandle -10u #define kNtStdOutputHandle -11u #define kNtStdErrorHandle -12u #define kNtFileTypeUnknown 0x0000 #define kNtFileTypeDisk 0x0001 #define kNtFileTypeChar 0x0002 #define kNtFileTypePipe 0x0003 #define kNtFileTypeRemote 0x8000 #define kNtGenericRead 0x80000000u #define kNtGenericWrite 0x40000000u #define kNtFileShareRead 0x00000001u #define kNtFileShareWrite 0x00000002u #define kNtFileShareDelete 0x00000004u #define kNtCreateNew 1 #define kNtCreateAlways 2 #define kNtOpenExisting 3 #define kNtOpenAlways 4 #define kNtTruncateExisting 5 #define kNtFileFlagOverlapped 0x40000000u #define kNtFileAttributeNotContentIndexed 0x00002000u #define kNtFileFlagBackupSemantics 0x02000000u #define kNtFileFlagOpenReparsePoint 0x00200000u #define kNtFileAttributeCompressed 0x00000800u #define kNtFileAttributeTemporary 0x00000100u #define kNtFileAttributeDirectory 0x00000010u struct NtOverlapped { unsigned long Internal; unsigned long InternalHigh; union { struct { unsigned Offset; unsigned OffsetHigh; }; void *Pointer; }; long hEvent; }; #define __dll_import(DLL, RET, FUNC, ARGS) \ extern RET(*__attribute__((__ms_abi__, __weak__)) FUNC) \ ARGS __asm__("dll$" DLL ".dll$" #FUNC) __dll_import("kernel32", void, ExitProcess, (unsigned)); __dll_import("kernel32", int, CloseHandle, (long)); __dll_import("kernel32", long, GetStdHandle, (unsigned)); __dll_import("kernel32", int, ReadFile, (long, void *, unsigned, unsigned *, struct NtOverlapped *)); __dll_import("kernel32", int, WriteFile, (long, const void *, unsigned, unsigned *, struct NtOverlapped *)); struct WinCrt { long fd2handle[3]; }; struct Crt { int os; int argc; char **argv; char **envp; long *auxv; int pagesz; int gransz; struct WinCrt *wincrt; } __crt; long SystemCall(long, long, long, long, long, long, long, int); long CallSystem(long a, long b, long c, long d, long e, long f, long g, int x) { if (IsXnu() && !IsAarch64()) x |= 0x2000000; return SystemCall(a, b, c, d, e, f, g, x); } wontreturn void _Exit(int rc) { int numba; if (!IsWindows()) { if (IsLinux()) { if (IsAarch64()) { numba = 94; } else { numba = 60; } } else { numba = 1; } CallSystem(rc, 0, 0, 0, 0, 0, 0, numba); } else { ExitProcess((unsigned)rc << 8); } __builtin_unreachable(); } static int ConvertFdToWin32Handle(int fd, long *out_handle) { if (fd < 0 || fd > 2) return -EBADF; *out_handle = __crt.wincrt->fd2handle[fd]; return 0; } int sys_close(int fd) { if (!IsWindows()) { int numba; if (IsLinux()) { if (IsAarch64()) { numba = 57; } else { numba = 3; } } else { numba = 6; } return CallSystem(fd, 0, 0, 0, 0, 0, 0, numba); } else { int rc; long handle; if (!(rc = ConvertFdToWin32Handle(fd, &handle))) { CloseHandle(handle); return 0; } else { return rc; } } } ssize_t sys_write(int fd, const void *data, size_t size) { if (!IsWindows()) { int numba; if (IsLinux()) { if (IsAarch64()) { numba = 64; } else { numba = 1; } } else { numba = 4; } return CallSystem(fd, (long)data, size, 0, 0, 0, 0, numba); } else { ssize_t rc; long handle; uint32_t got; if (!(rc = ConvertFdToWin32Handle(fd, &handle))) { if (WriteFile(handle, data, size, &got, 0)) { return got; } else { return -EIO; } } else { return rc; } } } ssize_t sys_pread(int fd, void *data, size_t size, long off) { int numba; if (IsLinux()) { if (IsAarch64()) { numba = 0x043; } else { numba = 0x011; } } else if (IsXnu()) { numba = 0x099; } else if (IsFreebsd()) { numba = 0x1db; } else if (IsOpenbsd()) { numba = 0x0a9; } else if (IsNetbsd()) { numba = 0x0ad; } else { __builtin_unreachable(); } return SystemCall(fd, (long)data, size, off, off, 0, 0, numba); } int sys_access(const char *path, int mode) { if (IsLinux() && IsAarch64()) { return SystemCall(-100, (long)path, mode, 0, 0, 0, 0, 48); } else { return CallSystem((long)path, mode, 0, 0, 0, 0, 0, IsLinux() ? 21 : 33); } } int sys_open(const char *path, int flags, int mode) { if (IsLinux() && IsAarch64()) { return SystemCall(-100, (long)path, flags, mode, 0, 0, 0, 56); } else { return CallSystem((long)path, flags, mode, 0, 0, 0, 0, IsLinux() ? 2 : 5); } } int sys_mprotect(void *addr, size_t size, int prot) { int numba; // all unix operating systems define the same values for prot if (IsLinux()) { if (IsAarch64()) { numba = 226; } else { numba = 10; } } else { numba = 74; } return CallSystem((long)addr, size, prot, 0, 0, 0, 0, numba); } long sys_mmap(void *addr, size_t size, int prot, int flags, int fd, long off) { long numba; if (IsLinux()) { if (IsAarch64()) { numba = 222; } else { numba = 9; } } else { // this flag isn't supported on non-Linux systems. since it's just // hinting the kernel, it should be inconsequential to just ignore flags &= ~MAP_NORESERVE; // this flag is ignored by Linux and it overlaps with bsd map_anon flags &= ~MAP_EXECUTABLE; if (flags & MAP_ANONYMOUS) { // all bsd-style operating systems share the same mag_anon magic flags &= ~MAP_ANONYMOUS; flags |= 4096; } if (IsFreebsd()) { numba = 477; } else if (IsOpenbsd()) { numba = 49; } else { numba = 197; // xnu, netbsd } } return CallSystem((long)addr, size, prot, flags, fd, off, off, numba); } wontreturn void __unix_start(long di, long *sp, char os) { // detect freebsd if (SupportsXnu() && os == _HOSTXNU) { os = _HOSTXNU; } else if (SupportsFreebsd() && di) { os = _HOSTFREEBSD; sp = (long *)di; } // extract arguments __crt.argc = *sp; __crt.argv = (char **)(sp + 1); __crt.envp = (char **)(sp + 1 + __crt.argc + 1); __crt.auxv = (long *)(sp + 1 + __crt.argc + 1); for (;;) { if (!*__crt.auxv++) { break; } } // detect openbsd if (SupportsOpenbsd() && !os && !__crt.auxv[0]) { os = _HOSTOPENBSD; } // detect netbsd and find end of words __crt.pagesz = IsAarch64() ? 16384 : 4096; for (long *ap = __crt.auxv; ap[0]; ap += 2) { if (ap[0] == AT_PAGESZ) { __crt.pagesz = ap[1]; } else if (SupportsNetbsd() && !os && ap[0] == AT_EXECFN_NETBSD) { os = _HOSTNETBSD; } } if (!os) { os = _HOSTLINUX; } __crt.gransz = __crt.pagesz; __crt.os = os; // call startup routines typedef int init_f(int, char **, char **, long *); extern init_f *__init_array_start[] __attribute__((__weak__)); extern init_f *__init_array_end[] __attribute__((__weak__)); for (init_f **fp = __init_array_end; fp-- > __init_array_start;) { (*fp)(__crt.argc, __crt.argv, __crt.envp, __crt.auxv); } // call program int main(int, char **, char **); _Exit(main(__crt.argc, __crt.argv, __crt.envp)); } wontreturn void __win32_start(void) { long sp[] = { 0, // argc 0, // empty argv 0, // empty environ 0, // empty auxv }; __crt.wincrt = &(struct WinCrt){ .fd2handle = { GetStdHandle(kNtStdInputHandle), GetStdHandle(kNtStdOutputHandle), GetStdHandle(kNtStdErrorHandle), }, }; __unix_start(0, sp, _HOSTWINDOWS); } ssize_t print(int fd, const char *s, ...) { int c; unsigned n; va_list va; char b[512]; va_start(va, s); for (n = 0; s; s = va_arg(va, const char *)) { while ((c = *s++)) { if (n < sizeof(b)) { b[n++] = c; } } } va_end(va); return sys_write(fd, b, n); } //////////////////////////////////////////////////////////////////////////////// char data[10] = "sup"; char bss[0xf9]; _Thread_local char tdata[10] = "hello"; _Thread_local char tbss[0xf9]; _Section(".love") int main(int argc, char **argv, char **envp) { if (argc == 666) { bss[0] = data[0]; tbss[0] = tdata[0]; } print(1, "hello world\n", NULL); }