this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

xtrace: Add some additional libsystem_kernel hooks

xtrace_thread_exit informs xtrace when a thread dies, allowing it to
cleanup any necessary internal state (e.g. TLS variables).

xtrace_execve_inject allows xtrace to inject itself into the process
being executed by modifying the necessary env vars.

xtrace_postfork_child notifies xtrace when the process is forked on the
child-side, so it can cleanup any internal state (e.g. locks, TLS variables).

+236 -17
+1
src/kernel/emulation/linux/CMakeLists.txt
··· 303 303 vchroot_userspace.c 304 304 syscalls-table.S 305 305 linux-syscall.S 306 + xtrace-hooks.S 306 307 307 308 ${CMAKE_BINARY_DIR}/src/external/darlingserver/src/rpc.c 308 309 )
+5
src/kernel/emulation/linux/bsdthread/bsdthread_terminate.c
··· 15 15 int sem); 16 16 int semaphore_signal_trap_impl(int signal_name); 17 17 18 + extern void _xtrace_thread_exit(void); 19 + 18 20 long sys_bsdthread_terminate(void* stackaddr, unsigned long freesize, int port, 19 21 int join_sem) 20 22 { ··· 25 27 unsigned long freesize, unsigned long pthobj_size); 26 28 27 29 semaphore_signal_trap_impl(join_sem); 30 + 31 + // point of no return; let xtrace know 32 + _xtrace_thread_exit(); 28 33 29 34 return __darling_thread_terminate(stackaddr, freesize, pthread_obj_size); 30 35 #else
+7
src/kernel/emulation/linux/mach/lkm.c
··· 28 28 29 29 static bool use_per_thread_driver_fd = false; 30 30 31 + extern void _xtrace_postfork_child(void); 32 + 31 33 void mach_driver_init(const char** applep) 32 34 { 33 35 #ifdef VARIANT_DYLD ··· 96 98 } 97 99 #endif 98 100 #endif 101 + 102 + if (!applep) { 103 + // we're being called in the child after a fork; let xtrace know about it 104 + _xtrace_postfork_child(); 105 + } 99 106 } 100 107 101 108 void mach_driver_init_pthread(void) {
+5
src/kernel/emulation/linux/process/execve.c
··· 26 26 #include "../resources/dserver-rpc-defs.h" 27 27 extern bool isspace(char c); 28 28 29 + extern void _xtrace_execve_inject(const char*** envp_ptr); 30 + 29 31 static inline bool istext(char c) 30 32 { 31 33 return c >= 0x20 && c < 0x7F; ··· 227 229 228 230 LINUX_SYSCALL(__NR_rt_sigprocmask, 0 /* LINUX_SIG_BLOCK */, 229 231 &set, NULL, sizeof(linux_sigset_t)); 232 + 233 + // let xtrace inject itself into the execve, if necessary 234 + _xtrace_execve_inject(&envp); 230 235 231 236 ret = LINUX_SYSCALL(__NR_execve, path_to_exec, argvp, envp); 232 237 if (ret < 0)
+34
src/kernel/emulation/linux/xtrace-hooks.S
··· 1 + #if defined(__x86_64__) 2 + 3 + .macro xtrace_hook name 4 + .text 5 + .globl _\name 6 + _\name\(): 7 + .space 13, 0x90 8 + ret 9 + .endm 10 + 11 + #elif defined(__i386__) 12 + 13 + .macro xtrace_hook name 14 + .text 15 + .globl _\name 16 + _\name\(): 17 + .space 6, 0x90 18 + ret 19 + .endm 20 + 21 + #else 22 + 23 + #error Missing xtrace hook dummy function macro for this architecture 24 + 25 + #endif 26 + 27 + // void _xtrace_thread_exit(void); 28 + xtrace_hook _xtrace_thread_exit 29 + 30 + // void _xtrace_execve_inject(const char*** envp_ptr); 31 + xtrace_hook _xtrace_execve_inject 32 + 33 + // void _xtrace_postfork_child(void); 34 + xtrace_hook _xtrace_postfork_child
+184 -17
src/xtrace/xtracelib.c
··· 9 9 #include "mig_trace.h" 10 10 #include "tls.h" 11 11 #include "lock.h" 12 + #include "malloc.h" 12 13 #include <limits.h> 13 14 14 15 #include <darling/emulation/ext/for-xtrace.h> ··· 22 23 extern void darling_bsd_syscall_exit_trampoline(void); 23 24 extern int sys_thread_selfid(void); 24 25 26 + static void xtrace_thread_exit_hook(void); 27 + static void xtrace_execve_inject_hook(const char*** envp_ptr); 28 + static void xtrace_postfork_child_hook(void); 29 + 25 30 #ifdef __x86_64__ 26 31 struct hook 27 32 { ··· 38 43 extern struct hook* _darling_bsd_syscall_entry; 39 44 extern struct hook* _darling_bsd_syscall_exit; 40 45 46 + extern void _xtrace_thread_exit(void); 47 + extern void _xtrace_execve_inject(const char*** envp_ptr); 48 + extern void _xtrace_postfork_child(void); 49 + 41 50 static void xtrace_setup_mach(void); 42 51 static void xtrace_setup_bsd(void); 43 - static void setup_hook(struct hook* hook, void* fnptr); 52 + static void setup_hook(struct hook* hook, void* fnptr, bool jump); 44 53 static void xtrace_setup_options(void); 54 + static void xtrace_setup_misc_hooks(void); 45 55 46 56 static int xtrace_ignore = 1; 47 57 ··· 56 66 xtrace_setup_mig_tracing(); 57 67 xtrace_setup_mach(); 58 68 xtrace_setup_bsd(); 69 + xtrace_setup_misc_hooks(); 59 70 60 71 // override the default sigaltstack used by libsystem_kernel for the main thread 61 72 // (we need more than the default 8KiB; testing has shown that 16KiB seems to be enough) ··· 109 120 110 121 DEFINE_XTRACE_TLS_VAR(int, xtrace_per_thread_logfile, -1, xtrace_per_thread_logfile_destroy); 111 122 123 + static bool string_is_truthy(const char* string) { 124 + return string && (string[0] == '1' || string[0] == 'T' || string[0] == 't' || string[0] == 'Y' || string[0] == 'y'); 125 + }; 126 + 112 127 static void xtrace_setup_options(void) 113 128 { 114 129 const char* xtrace_log_file = getenv("XTRACE_LOG_FILE"); 115 130 116 - if (getenv("XTRACE_SPLIT_ENTRY_AND_EXIT") != NULL) 117 - xtrace_split_entry_and_exit = 1; 118 - if (getenv("XTRACE_NO_COLOR") != NULL) 119 - xtrace_no_color = 1; 120 - if (getenv("XTRACE_KPRINTF") != NULL) 121 - xtrace_kprintf = 1; 131 + xtrace_split_entry_and_exit = string_is_truthy(getenv("XTRACE_SPLIT_ENTRY_AND_EXIT")); 132 + xtrace_no_color = string_is_truthy(getenv("XTRACE_NO_COLOR")); 133 + xtrace_kprintf = string_is_truthy(getenv("XTRACE_KPRINTF")); 134 + xtrace_use_per_thread_logfile = string_is_truthy(getenv("XTRACE_LOG_FILE_PER_THREAD")); 122 135 123 - if (getenv("XTRACE_LOG_FILE_PER_THREAD") != NULL) 124 - xtrace_use_per_thread_logfile = 1; 125 - 126 - if (xtrace_log_file != NULL) { 136 + if (xtrace_log_file != NULL && xtrace_log_file[0] != '\0') { 127 137 xtrace_use_logfile = 1; 128 138 strlcpy(xtrace_logfile_base, xtrace_log_file, sizeof(xtrace_logfile_base)); 129 139 } 130 140 } 131 141 132 142 133 - static void setup_hook(struct hook* hook, void* fnptr) 143 + static void setup_hook(struct hook* hook, void* fnptr, bool jump) 134 144 { 135 145 // this hook is (in GAS syntax): 136 146 // movq $<fnptr value>, %r10 137 147 // call *%r10 148 + // the call turns to a jump if `jump` is true 138 149 hook->movabs[0] = 0x49; 139 150 hook->movabs[1] = 0xba; 140 151 hook->call[0] = 0x41; 141 152 hook->call[1] = 0xff; 142 - hook->call[2] = 0xd2; 153 + hook->call[2] = jump ? 0xe2 : 0xd2; 143 154 hook->addr = (uintptr_t)fnptr; 144 155 } 145 156 ··· 156 167 157 168 mprotect((void*) area, bytes, PROT_READ | PROT_WRITE | PROT_EXEC); 158 169 159 - setup_hook(_darling_mach_syscall_entry, darling_mach_syscall_entry_trampoline); 160 - setup_hook(_darling_mach_syscall_exit, darling_mach_syscall_exit_trampoline); 170 + setup_hook(_darling_mach_syscall_entry, darling_mach_syscall_entry_trampoline, false); 171 + setup_hook(_darling_mach_syscall_exit, darling_mach_syscall_exit_trampoline, false); 161 172 162 173 mprotect((void*) area, bytes, PROT_READ | PROT_EXEC); 163 174 } ··· 175 186 176 187 mprotect((void*) area, bytes, PROT_READ | PROT_WRITE | PROT_EXEC); 177 188 178 - setup_hook(_darling_bsd_syscall_entry, darling_bsd_syscall_entry_trampoline); 179 - setup_hook(_darling_bsd_syscall_exit, darling_bsd_syscall_exit_trampoline); 189 + setup_hook(_darling_bsd_syscall_entry, darling_bsd_syscall_entry_trampoline, false); 190 + setup_hook(_darling_bsd_syscall_exit, darling_bsd_syscall_exit_trampoline, false); 180 191 181 192 mprotect((void*) area, bytes, PROT_READ | PROT_EXEC); 182 193 } 183 194 195 + // like setup_hook, but also takes care of making memory writable for the hook setup and restoring it afterwards 196 + static void setup_hook_with_perms(struct hook* hook, void* fnptr, bool jump) { 197 + uintptr_t area = (uintptr_t)hook; 198 + uintptr_t areaEnd = ((uintptr_t)hook) + sizeof(struct hook); 199 + 200 + // __asm__("int3"); 201 + area &= ~(4096-1); 202 + areaEnd &= ~(4096-1); 203 + 204 + uintptr_t bytes = 4096 + (areaEnd-area); 205 + 206 + mprotect((void*) area, bytes, PROT_READ | PROT_WRITE | PROT_EXEC); 207 + 208 + setup_hook(hook, fnptr, jump); 209 + 210 + mprotect((void*) area, bytes, PROT_READ | PROT_EXEC); 211 + }; 212 + 213 + static void xtrace_setup_misc_hooks(void) { 214 + setup_hook_with_perms((void*)&_xtrace_thread_exit, xtrace_thread_exit_hook, true); 215 + setup_hook_with_perms((void*)&_xtrace_execve_inject, xtrace_execve_inject_hook, true); 216 + setup_hook_with_perms((void*)&_xtrace_postfork_child, xtrace_postfork_child_hook, true); 217 + }; 218 + 184 219 void xtrace_set_gray_color(void) 185 220 { 186 221 if (xtrace_no_color) ··· 391 426 _abort_with_payload_for_xtrace(0, 0, NULL, 0, message, 0); 392 427 __builtin_unreachable(); 393 428 }; 429 + 430 + static void xtrace_thread_exit_hook(void) { 431 + xtrace_tls_thread_cleanup(); 432 + }; 433 + 434 + static size_t envp_count(const char** envp) { 435 + size_t count = 0; 436 + for (const char** ptr = envp; *ptr != NULL; ++ptr) { 437 + ++count; 438 + } 439 + return count; 440 + }; 441 + 442 + static const char** envp_find(const char** envp, const char* key) { 443 + size_t key_length = strlen(key); 444 + 445 + for (const char** ptr = envp; *ptr != NULL; ++ptr) { 446 + const char* entry_key = *ptr; 447 + const char* entry_key_end = strchr(entry_key, '='); 448 + size_t entry_key_length = entry_key_end - entry_key; 449 + 450 + if (entry_key_length != key_length) { 451 + continue; 452 + } 453 + 454 + if (strncmp(key, entry_key, key_length) != 0) { 455 + continue; 456 + } 457 + 458 + return ptr; 459 + } 460 + 461 + return NULL; 462 + }; 463 + 464 + static const char* envp_make_entry(const char* key, const char* value) { 465 + size_t key_length = strlen(key); 466 + size_t value_length = strlen(value); 467 + char* entry = xtrace_malloc(key_length + value_length + 2); 468 + memcpy(entry, key, key_length); 469 + entry[key_length] = '='; 470 + memcpy(&entry[key_length + 1], value, value_length); 471 + entry[key_length + value_length + 2] = '\0'; 472 + return entry; 473 + }; 474 + 475 + static void envp_set(const char*** envp_ptr, const char* key, const char* value, bool* allocated) { 476 + const char** envp = *envp_ptr; 477 + 478 + if (!envp) { 479 + *envp_ptr = envp = xtrace_malloc(sizeof(const char*) * 2); 480 + envp[0] = envp_make_entry(key, value); 481 + envp[1] = NULL; 482 + *allocated = true; 483 + } else { 484 + const char** entry_ptr = envp_find(envp, key); 485 + 486 + if (entry_ptr) { 487 + *entry_ptr = envp_make_entry(key, value); 488 + } else { 489 + size_t count = envp_count(envp); 490 + const char** new_envp = xtrace_malloc(sizeof(const char*) * (count + 2)); 491 + 492 + memcpy(new_envp, envp, sizeof(const char*) * count); 493 + 494 + if (*allocated) { 495 + xtrace_free(envp); 496 + } 497 + 498 + new_envp[count] = envp_make_entry(key, value); 499 + new_envp[count + 1] = NULL; 500 + *allocated = true; 501 + *envp_ptr = new_envp; 502 + } 503 + } 504 + }; 505 + 506 + static const char* envp_get(const char** envp, const char* key) { 507 + const char** entry = envp_find(envp, key); 508 + 509 + if (!entry) { 510 + return NULL; 511 + } 512 + 513 + return strchr(*entry, '=') + 1; 514 + }; 515 + 516 + #define LIBRARY_PATH "/usr/lib/darling/libxtrace.dylib" 517 + #define LIBRARY_PATH_LENGTH (sizeof(LIBRARY_PATH) - 1) 518 + 519 + static void xtrace_execve_inject_hook(const char*** envp_ptr) { 520 + bool allocated = false; 521 + 522 + envp_set(envp_ptr, "XTRACE_SPLIT_ENTRY_AND_EXIT", xtrace_split_entry_and_exit ? "1" : "0", &allocated); 523 + envp_set(envp_ptr, "XTRACE_NO_COLOR", xtrace_no_color ? "1" : "0", &allocated); 524 + envp_set(envp_ptr, "XTRACE_KPRINTF", xtrace_kprintf ? "1" : "0", &allocated); 525 + envp_set(envp_ptr, "XTRACE_LOG_FILE_PER_THREAD", xtrace_use_per_thread_logfile ? "1" : "0", &allocated); 526 + envp_set(envp_ptr, "XTRACE_LOG_FILE", xtrace_use_logfile ? xtrace_logfile_base : "", &allocated); 527 + 528 + const char* insert_libraries = envp_get(*envp_ptr, "DYLD_INSERT_LIBRARIES"); 529 + size_t insert_libraries_length = insert_libraries ? strlen(insert_libraries) : 0; 530 + char* new_value = xtrace_malloc(insert_libraries_length + (insert_libraries_length == 0 ? 0 : 1) + LIBRARY_PATH_LENGTH + 1); 531 + size_t offset = 0; 532 + 533 + if (insert_libraries && insert_libraries_length > 0) { 534 + memcpy(&new_value[offset], insert_libraries, insert_libraries_length); 535 + offset += insert_libraries_length; 536 + 537 + new_value[offset] = ':'; 538 + ++offset; 539 + } 540 + 541 + memcpy(&new_value[offset], LIBRARY_PATH, LIBRARY_PATH_LENGTH); 542 + offset += LIBRARY_PATH_LENGTH; 543 + 544 + new_value[offset] = '\0'; 545 + 546 + envp_set(envp_ptr, "DYLD_INSERT_LIBRARIES", new_value, &allocated); 547 + }; 548 + 549 + static void xtrace_postfork_child_hook(void) { 550 + // TODO: cleanup TLS 551 + 552 + // reset the per-thread logfile (if necessary) 553 + if (xtrace_use_per_thread_logfile) { 554 + int fd = get_xtrace_per_thread_logfile(); 555 + if (fd >= 0) { 556 + _close_for_xtrace(fd); 557 + } 558 + set_xtrace_per_thread_logfile(-1); 559 + } 560 + };