freebsd.c (5155B)
1 /* FreeBSD-specific env bits. Uses: 2 * - memfd_create(3) (FreeBSD 13+) for the dual-map fd 3 * - st_mtim (POSIX.1-2008) for mtime 4 * - dlsym(RTLD_DEFAULT) for the resolver 5 * 6 * If this file ever needs to support pre-13 FreeBSD, switch the fd source 7 * to shm_open(SHM_ANON, O_RDWR, 0) + shm_unlink(); the rest of the dance 8 * (ftruncate + two mmaps of the fd at distinct VAs) is identical. */ 9 10 #include <dlfcn.h> 11 #include <stdint.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <sys/mman.h> 15 #include <sys/stat.h> 16 #include <sys/sysctl.h> 17 #include <sys/types.h> 18 #include <unistd.h> 19 20 #include "env_posix.h" 21 22 /* ---------------- dual-mapped exec memory ---------------- */ 23 24 KitStatus os_execmem_reserve_exec(size_t size, KitExecMemRegion* out) { 25 int fd = memfd_create("kit-jit", 0); 26 void* w; 27 void* r; 28 ExecMemToken* tok; 29 if (fd < 0) return KIT_ERR; 30 if (ftruncate(fd, (off_t)size) != 0) { 31 close(fd); 32 return KIT_ERR; 33 } 34 35 w = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 36 if (w == MAP_FAILED) { 37 close(fd); 38 return KIT_NOMEM; 39 } 40 r = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); 41 if (r == MAP_FAILED) { 42 munmap(w, size); 43 close(fd); 44 return KIT_NOMEM; 45 } 46 close(fd); 47 48 tok = (ExecMemToken*)malloc(sizeof(*tok)); 49 if (!tok) { 50 munmap(r, size); 51 munmap(w, size); 52 return KIT_NOMEM; 53 } 54 tok->write_addr = w; 55 tok->runtime_addr = r; 56 tok->size = size; 57 58 exec_dual_register(w, r, size); 59 60 out->write = w; 61 out->runtime = r; 62 out->size = size; 63 out->token = tok; 64 return KIT_OK; 65 } 66 67 /* ---------------- dbg W^X dance ---------------- */ 68 /* Same shape as Linux: prefer alias lookup, fall back to transient mprotect 69 * of the runtime alias for single-mapping reservations. */ 70 71 static size_t page_floor(size_t v, size_t pg) { return v & ~(pg - 1); } 72 static size_t page_ceil(size_t v, size_t pg) { 73 return (v + pg - 1) & ~(pg - 1); 74 } 75 76 KitStatus os_dbg_code_write_begin(void* user, void* runtime_addr, size_t n, 77 void** write_out) { 78 size_t pg; 79 uintptr_t a; 80 uintptr_t base; 81 size_t span; 82 (void)user; 83 if (!runtime_addr || !n || !write_out) return KIT_INVALID; 84 if (exec_dual_lookup(runtime_addr, n, write_out) == 0) return KIT_OK; 85 pg = driver_host_page_size(); 86 a = (uintptr_t)runtime_addr; 87 base = page_floor(a, pg); 88 span = page_ceil((a - base) + n, pg); 89 if (mprotect((void*)base, span, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) 90 return KIT_ERR; 91 *write_out = runtime_addr; 92 return KIT_OK; 93 } 94 95 void os_dbg_code_write_end(void* user, void* runtime_addr, size_t n) { 96 void* w; 97 size_t pg; 98 uintptr_t a; 99 uintptr_t base; 100 size_t span; 101 (void)user; 102 if (exec_dual_lookup(runtime_addr, n, &w) == 0) return; 103 pg = driver_host_page_size(); 104 a = (uintptr_t)runtime_addr; 105 base = page_floor(a, pg); 106 span = page_ceil((a - base) + n, pg); 107 mprotect((void*)base, span, PROT_READ | PROT_EXEC); 108 } 109 110 void os_dbg_flush_icache(void* user, void* runtime_addr, size_t n) { 111 (void)user; 112 env_flush_icache(runtime_addr, n); 113 } 114 115 /* ---------------- st_mtim ---------------- */ 116 117 int os_stat_mtime_ns(const struct stat* sb, int64_t* out) { 118 *out = 119 (int64_t)sb->st_mtim.tv_sec * 1000000000LL + (int64_t)sb->st_mtim.tv_nsec; 120 return 0; 121 } 122 123 /* ---------------- dlsym ---------------- */ 124 125 void* os_dlsym(const char* name) { 126 void* p = dlsym(RTLD_DEFAULT, name); 127 if (!p && name[0] == '_' && name[1] != '\0') 128 p = dlsym(RTLD_DEFAULT, name + 1); 129 return p; 130 } 131 132 /* ---------------- host_target os/obj ---------------- */ 133 134 void os_host_target_fill(KitTargetSpec* t) { 135 t->os = KIT_OS_FREEBSD; 136 t->obj = KIT_OBJ_ELF; 137 } 138 139 /* ---------------- self executable path ---------------- */ 140 /* The KERN_PROC_PATHNAME sysctl returns the absolute path of a process's text 141 * image; pid -1 selects the calling process. A first call with a NULL buffer 142 * reports the required length (including the NUL). */ 143 int driver_self_exe_path(DriverEnv* env, char** out, size_t* out_size) { 144 int mib[4]; 145 size_t len = 0; 146 size_t cap; 147 char* buf; 148 149 if (!env || !out || !out_size) return 1; 150 mib[0] = CTL_KERN; 151 mib[1] = KERN_PROC; 152 mib[2] = KERN_PROC_PATHNAME; 153 mib[3] = -1; 154 if (sysctl(mib, 4, NULL, &len, NULL, 0) != 0 || len == 0) return 1; 155 cap = len; 156 buf = (char*)driver_alloc(env, cap); 157 if (!buf) return 1; 158 if (sysctl(mib, 4, buf, &len, NULL, 0) != 0) { 159 driver_free(env, buf, cap); 160 return 1; 161 } 162 buf[cap - 1u] = '\0'; /* defensive: ensure termination */ 163 *out = buf; 164 *out_size = cap; 165 return 0; 166 } 167 168 /* ---------------- default hosted dirs probe ---------------- */ 169 /* FreeBSD base system is flat: headers in /usr/include, crt + libc in /usr/lib 170 * and /lib (libc.so.7 lives in /lib). Host target only. */ 171 int driver_default_hosted_dirs(DriverEnv* env, KitTargetSpec target, 172 DriverHostedDirs* out) { 173 (void)env; 174 if (target.os != KIT_OS_FREEBSD) return 1; 175 if (driver_hosted_dirs_add_inc(out, "/usr/include") != 0) return 1; 176 if (driver_hosted_dirs_add_lib(out, "/usr/lib") != 0) return 1; 177 if (driver_hosted_dirs_add_lib(out, "/lib") != 0) return 1; 178 return out->nlibdirs ? 0 : 1; 179 }