this repo has no description
1
fork

Configure Feed

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

feat: Initial WSL1 support

- mldr: Replaced `mmap` calls with `compatible_mmap`, a function
that removes `MAP_GROWSDOWN` on incompatible systems, and emulates
`MAP_FIXED_NOREPLACE` on older kernels.
- mldr: Implemented lifetime pipes. These pipes are created during
process creation and sent to darlingserver. The pipe is preserved
through exec calls and automatically closes on process termination.
- kernel/emulation: `execve` now preserves mldr lifetime pipes through
the `__mldr_lifetime_pipe` environment variable.
- kernel/emulation: Parameters for calls to `dserver_rpc_checkin` have
been updated to reflect changes in darlingserver.

+250 -30
+12
src/kernel/emulation/linux/elfcalls_wrapper.c
··· 91 91 void __dserver_close_socket(int socket) { 92 92 return elfcalls()->dserver_close_socket(socket); 93 93 }; 94 + 95 + int __dserver_get_process_lifetime_pipe() { 96 + return elfcalls()->dserver_get_process_lifetime_pipe(); 97 + } 98 + 99 + int __dserver_process_lifetime_pipe_refresh(void) { 100 + return elfcalls()->dserver_process_lifetime_pipe_refresh(); 101 + }; 102 + 103 + void __dserver_close_process_lifetime_pipe(int* fds) { 104 + return elfcalls()->dserver_close_process_lifetime_pipe(fds); 105 + };
+4
src/kernel/emulation/linux/elfcalls_wrapper.h
··· 33 33 void __dserver_per_thread_socket_refresh(void); 34 34 void __dserver_close_socket(int socket); 35 35 36 + int __dserver_get_process_lifetime_pipe(void); 37 + int __dserver_process_lifetime_pipe_refresh(void); 38 + void __dserver_close_process_lifetime_pipe(int* fds); 39 + 36 40 #ifdef __cplusplus 37 41 } 38 42 #endif
+23 -16
src/kernel/emulation/linux/process/execve.c
··· 18 18 #include "../simple.h" 19 19 20 20 #include <darlingserver/rpc.h> 21 + #include "../elfcalls_wrapper.h" 21 22 #include "../unistd/write.h" 22 23 #include <mach-o/loader.h> 23 24 #include <mach-o/fat.h> ··· 48 49 49 50 vc.flags = VCHROOT_FOLLOW; 50 51 vc.dfd = get_perthread_wd(); 51 - 52 + 52 53 strcpy(vc.path, fname); 53 54 54 55 ret = vchroot_expand(&vc); ··· 60 61 int fd = sys_open(fname, BSD_O_RDONLY, 0); 61 62 if (fd < 0) 62 63 return fd; 63 - 64 + 64 65 ret = sys_read(fd, shebang, sizeof(shebang)); 65 66 if (ret < 0) 66 67 return ret; ··· 99 100 nl = memchr(shebang, '\n', ret); 100 101 if (!nl) 101 102 return -ENOEXEC; 102 - 103 + 103 104 *nl = '\0'; 104 105 for (i = 2; isspace(shebang[i]); i++); 105 - 106 + 106 107 interp = &shebang[i]; 107 - 108 + 108 109 for (i = 0; !isspace(interp[i]) && interp[i]; i++); 109 - 110 + 110 111 if (interp[i] == '\0') 111 112 arg = NULL; 112 113 else 113 114 arg = &interp[i]; 114 - 115 + 115 116 if (arg != NULL) 116 117 { 117 118 *arg = '\0'; // terminate interp ··· 121 122 if (*arg == '\0') 122 123 arg = NULL; // no argument, just whitespace 123 124 } 124 - 125 + 125 126 // Count original arguments 126 127 while (argvp[len++]); 127 - 128 + 128 129 // Allocate a new argvp 129 130 modargvp = (const char**) __builtin_alloca(sizeof(void*) * (len+3)); 130 - 131 + 131 132 i = 0; 132 133 modargvp[i++] = mldr_path; 133 134 modargvp[i++] = vc.path; // expanded later 134 135 if (arg != NULL) 135 136 modargvp[i++] = arg; 136 137 modargvp[i] = fname; 137 - 138 + 138 139 // Append original arguments 139 140 for (j = 1; j < len+1; j++) 140 141 modargvp[i+j] = argvp[j]; 141 - 142 + 142 143 argvp = modargvp; 143 144 strcpy(vc.path, interp); 144 145 ··· 181 182 struct linux_sockaddr_un* server_socket_address = dserver_rpc_hooks_get_server_address(); 182 183 const char* server_socket_path = server_socket_address->sun_path; 183 184 185 + char mldr_lifetime_pipe_env[32] = { '\0' }; 186 + __simple_snprintf(mldr_lifetime_pipe_env, sizeof(mldr_lifetime_pipe_env) - 1, "__mldr_lifetime_pipe=%d", __dserver_get_process_lifetime_pipe()); 187 + 184 188 // count original env vars 185 189 while (envp[len++]); 186 190 187 - // allocate a new envp and env0 188 - modenvp = (const char**)__builtin_alloca(sizeof(void*) * (len + 1)); 191 + const int new_env_count = 2; 192 + 193 + // allocate a new envp and env0, env1 194 + modenvp = (const char**)__builtin_alloca(sizeof(void*) * (len + new_env_count)); 189 195 buf = __builtin_alloca(strlen(server_socket_path) + sizeof("__mldr_sockpath=")); 190 196 191 197 // set up the new env0 192 198 strcpy(buf, "__mldr_sockpath="); 193 199 strcat(buf, server_socket_path); 194 200 modenvp[0] = buf; 201 + modenvp[1] = mldr_lifetime_pipe_env; 195 202 196 203 // append original env vars 197 - for (int i = 1; i < len+1; i++) 198 - modenvp[i] = envp[i-1]; 204 + for (int i = new_env_count; i < len + new_env_count; i++) 205 + modenvp[i] = envp[i-new_env_count]; 199 206 200 207 envp = modenvp; 201 208 }
+6 -1
src/kernel/emulation/linux/process/fork.c
··· 39 39 40 40 // create a new dserver RPC socket 41 41 __dserver_per_thread_socket_refresh(); 42 + int newReadFd = __dserver_process_lifetime_pipe_refresh(); 42 43 43 44 // guard it 44 45 guard_entry_options_t options; 45 46 options.close = __dserver_close_socket; 46 47 guard_table_add(__dserver_per_thread_socket(), guard_flag_prevent_close | guard_flag_close_on_fork, &options); 47 48 48 - if (dserver_rpc_checkin(true) < 0) { 49 + int dummy_stack_variable; 50 + if (dserver_rpc_checkin(true, &dummy_stack_variable, newReadFd) < 0) { 49 51 // we can't do ANYTHING if darlingserver fails to acknowledge us 50 52 __simple_printf("Failed to checkin with darlingserver after fork\n"); 51 53 __simple_abort(); 52 54 } 53 55 if (wdfd >= 0) 54 56 sys_fchdir(wdfd); 57 + 58 + int pipe[2] = { newReadFd, -1 }; 59 + __dserver_close_process_lifetime_pipe(pipe); 55 60 } 56 61 57 62 return ret;
+26
src/startup/mldr/elfcalls/elfcalls.c
··· 64 64 65 65 extern void __mldr_close_rpc_socket(int socket); 66 66 67 + extern int __mldr_create_process_lifetime_pipe(int* fds); 68 + extern void __mldr_close_process_lifetime_pipe(int* fds); 69 + extern int __dserver_process_lifetime_pipe_fd; 70 + 71 + static int __dserver_get_process_lifetime_pipe() { 72 + return __dserver_process_lifetime_pipe_fd; 73 + } 74 + 75 + static int __dserver_process_lifetime_pipe_refresh() { 76 + // the read end has already been closed. 77 + int pipe[2] = { -1, __dserver_process_lifetime_pipe_fd }; 78 + __mldr_close_process_lifetime_pipe(pipe); 79 + 80 + if (__mldr_create_process_lifetime_pipe(pipe) == -1) { 81 + fprintf(stderr, "Failed to create process lifetime pipe: %d (%s)\n", errno, strerror(errno)); 82 + abort(); 83 + } 84 + 85 + __dserver_process_lifetime_pipe_fd = pipe[1]; 86 + return pipe[0]; 87 + } 88 + 67 89 void elfcalls_make(struct elf_calls* calls) 68 90 { 69 91 calls->dlopen = dlopen_simple; ··· 98 120 calls->dserver_per_thread_socket = __darling_thread_rpc_socket; 99 121 calls->dserver_per_thread_socket_refresh = __darling_thread_rpc_socket_refresh; 100 122 calls->dserver_close_socket = __mldr_close_rpc_socket; 123 + 124 + calls->dserver_get_process_lifetime_pipe = __dserver_get_process_lifetime_pipe; 125 + calls->dserver_process_lifetime_pipe_refresh = __dserver_process_lifetime_pipe_refresh; 126 + calls->dserver_close_process_lifetime_pipe = __mldr_close_process_lifetime_pipe; 101 127 }
+5
src/startup/mldr/elfcalls/elfcalls.h
··· 61 61 int (*dserver_per_thread_socket)(void); 62 62 void (*dserver_per_thread_socket_refresh)(void); 63 63 void (*dserver_close_socket)(int socket); 64 + 65 + // darlingserver process lifetime pipe info 66 + int (*dserver_get_process_lifetime_pipe)(void); 67 + int (*dserver_process_lifetime_pipe_refresh)(void); 68 + void (*dserver_close_process_lifetime_pipe)(int* fds); 64 69 }; 65 70 66 71 #endif
+7 -5
src/startup/mldr/elfcalls/threads.c
··· 175 175 } 176 176 177 177 args.stack_bottom = args.stack_addr - stack_size; 178 - 178 + 179 179 // pthread_attr_setstack is buggy. The documentation states we should provide the lowest 180 180 // address of the stack, yet some versions regard it as the highest address instead. 181 181 // Therefore it's better to just make the pthread stack as small as possible and then switch ··· 191 191 args.pth = pth; 192 192 pthread_create(&nativeLibcThread, &attr, darling_thread_entry, &args); 193 193 pthread_attr_destroy(&attr); 194 - 194 + 195 195 while (args.pth != NULL) 196 196 sched_yield(); 197 197 ··· 202 202 { 203 203 struct arg_struct* in_args = (struct arg_struct*) p; 204 204 struct arg_struct args; 205 - 205 + 206 206 memcpy(&args, in_args, sizeof(args)); 207 207 208 208 dthread_t dthread = args.pth; ··· 229 229 *flags |= args.is_workqueue ? DWQ_FLAG_THREAD_TSD_BASE_SET : DTHREAD_START_TSD_BASE_SET; 230 230 231 231 // let's check-in with darlingserver on this new thread 232 - if (dserver_rpc_explicit_checkin(t_server_socket, false) < 0) { 232 + int dummy_stack_variable; 233 + // the lifetime pipe fd is ignored as the proces should already have been registered 234 + if (dserver_rpc_explicit_checkin(t_server_socket, false, &dummy_stack_variable, -1) < 0) { 233 235 // we can't do ANYTHING if darlingserver doesn't acknowledge us successfully 234 236 abort(); 235 237 } ··· 327 329 // Let's just hang around forever. 328 330 sigset_t mask; 329 331 memset(&mask, 0, sizeof(mask)); 330 - 332 + 331 333 while (1) 332 334 sigsuspend(&mask); 333 335 }
+5 -4
src/startup/mldr/loader.c
··· 14 14 static int native_prot(int prot); 15 15 static void load(const char* path, cpu_type_t cpu, bool expect_dylinker, char** argv, struct load_results* lr); 16 16 static void setup_space(struct load_results* lr, bool is_64_bit); 17 + static void* compatible_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 17 18 18 19 #ifndef PAGE_SIZE 19 20 # define PAGE_SIZE 4096 ··· 171 172 addr += slide; 172 173 173 174 // Some segments' filesize != vmsize, thus this mprotect(). 174 - rv = mmap((void*)addr, seg->vmsize, useprot, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0); 175 + rv = compatible_mmap((void*)addr, seg->vmsize, useprot, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0); 175 176 if (rv == (void*)MAP_FAILED) 176 177 { 177 178 if (seg->vmaddr == 0 && useprot == 0) { ··· 187 188 else 188 189 { 189 190 size_t size = seg->vmsize - seg->filesize; 190 - rv = mmap((void*) PAGE_ALIGN(seg->vmaddr + seg->vmsize - size), PAGE_ROUNDUP(size), useprot, 191 + rv = compatible_mmap((void*) PAGE_ALIGN(seg->vmaddr + seg->vmsize - size), PAGE_ROUNDUP(size), useprot, 191 192 MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0); 192 193 if (rv == (void*)MAP_FAILED) 193 194 { ··· 210 211 if (seg->filesize < seg->vmsize) { 211 212 flag = MAP_FIXED; 212 213 } 213 - rv = mmap((void*)addr, seg->filesize, useprot, 214 + rv = compatible_mmap((void*)addr, seg->filesize, useprot, 214 215 flag | MAP_PRIVATE, fd, seg->fileoff + fat_offset); 215 216 if (rv == (void*)MAP_FAILED) 216 217 { ··· 223 224 exit(1); 224 225 } 225 226 } 226 - 227 + 227 228 if (seg->fileoff == 0) 228 229 mappedHeader = (struct MACH_HEADER_STRUCT*) (seg->vmaddr + slide); 229 230 }
+1
src/startup/mldr/loader.h
··· 22 22 unsigned long stack_top; 23 23 char* socket_path; 24 24 int kernfd; 25 + int lifetime_pipe; 25 26 26 27 size_t argc; 27 28 size_t envc;
+161 -4
src/startup/mldr/mldr.c
··· 41 41 #include <darlingserver/rpc.h> 42 42 #include <sys/ptrace.h> 43 43 #include <pthread.h> 44 + #include <sys/utsname.h> 44 45 45 46 #ifndef PAGE_SIZE 46 47 # define PAGE_SIZE 4096 ··· 55 56 }; 56 57 57 58 int __dserver_main_thread_socket_fd = -1; 59 + int __dserver_process_lifetime_pipe_fd = -1; 58 60 59 61 // The idea of mldr is to load dyld_path into memory and set up the stack 60 62 // as described in dyldStartup.S. ··· 73 75 static void setup_space(struct load_results* lr, bool is_64_bit); 74 76 static void process_special_env(struct load_results* lr); 75 77 static void start_thread(struct load_results* lr); 78 + static bool is_kernel_at_least(int major, int minor); 79 + static void* compatible_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 76 80 #ifdef __x86_64__ 77 81 static void setup_stack64(const char* filepath, struct load_results* lr); 78 82 #endif ··· 94 98 static const char* const skip_env_vars[] = { 95 99 "__mldr_bprefs=", 96 100 "__mldr_sockpath=", 101 + "__mldr_lifetime_pipe", 97 102 }; 98 103 99 104 void* __mldr_main_stack_top = NULL; 105 + 106 + static int kernel_major = -1; 107 + static int kernel_minor = -1; 100 108 101 109 int main(int argc, char** argv, char** envp) 102 110 { ··· 258 266 } 259 267 260 268 __mldr_main_stack_top = (void*)mldr_load_results.stack_top; 261 - 269 + 262 270 start_thread(&mldr_load_results); 263 271 264 272 __builtin_unreachable(); ··· 504 512 lr->socket_path = __dserver_socket_address_data.sun_path; 505 513 } 506 514 515 + lr->lifetime_pipe = -1; 516 + str = getenv("__mldr_lifetime_pipe"); 517 + 518 + if (str != NULL) { 519 + sscanf(str, "%i", &lr->lifetime_pipe); 520 + } 521 + 507 522 str = getenv("DYLD_ROOT_PATH"); 508 523 509 524 if (str != NULL && lr->root_path == NULL) { ··· 517 532 static void unset_special_env() { 518 533 unsetenv("__mldr_bprefs"); 519 534 unsetenv("__mldr_sockpath"); 535 + unsetenv("__mldr_lifetime_pipe"); 520 536 }; 521 537 522 538 typedef struct socket_bitmap { ··· 734 750 socket_bitmap_put(&socket_bitmap, socket); 735 751 }; 736 752 753 + int __mldr_create_process_lifetime_pipe(int* fds) { 754 + // These pipes are not required for Linux 5.3 or newer, 755 + // we already have pidfd_open. 756 + if (is_kernel_at_least(5, 3)) { 757 + fds[0] = fds[1] = -1; 758 + return 0; 759 + } 760 + 761 + int pre_fds[2]; 762 + if (pipe(pre_fds) == -1) { 763 + goto err_out; 764 + } 765 + 766 + for (int i = 0; i < 2; ++i) { 767 + fds[i] = socket_bitmap_get(&socket_bitmap); 768 + if (fds[i] < 0) { 769 + goto err_out; 770 + } 771 + 772 + if (dup2(pre_fds[i], fds[i]) < 0) { 773 + socket_bitmap_put(&socket_bitmap, fds[i]); 774 + fds[i] = -1; 775 + goto err_out; 776 + } 777 + 778 + close(pre_fds[i]); 779 + pre_fds[i] = -1; 780 + } 781 + 782 + return 0; 783 + 784 + err_out: 785 + for (int i = 0; i < 2; ++i) { 786 + if (fds[i] >= 0) { 787 + socket_bitmap_put(&socket_bitmap, fds[i]); 788 + close(fds[i]); 789 + } 790 + 791 + if (pre_fds[i] >= 0) { 792 + close(pre_fds[i]); 793 + } 794 + } 795 + 796 + return -1; 797 + } 798 + 799 + void __mldr_close_process_lifetime_pipe(int* fds) { 800 + for (int i = 0; i < 2; ++i) { 801 + if (fds[i] != -1) { 802 + close(fds[i]); 803 + socket_bitmap_put(&socket_bitmap, fds[i]); 804 + } 805 + } 806 + } 807 + 737 808 static void setup_space(struct load_results* lr, bool is_64_bit) { 738 809 commpage_setup(is_64_bit); 739 810 ··· 756 827 size = limit.rlim_cur; 757 828 } 758 829 759 - if (mmap((void*)(lr->stack_top - size), size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE | MAP_GROWSDOWN, -1, 0) == MAP_FAILED) { 830 + if (compatible_mmap((void*)(lr->stack_top - size), size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE | MAP_GROWSDOWN, -1, 0) == MAP_FAILED) { 760 831 fprintf(stderr, "Failed to allocate stack of %lu bytes: %d (%s)\n", size, errno, strerror(errno)); 761 832 exit(1); 762 833 } ··· 771 842 772 843 __dserver_main_thread_socket_fd = lr->kernfd; 773 844 774 - if (dserver_rpc_checkin(false) < 0) { 845 + int lifetime_pipe[2]; 846 + 847 + // this process is created using exec from another Darling process. 848 + // darlingserver should already have the read pipe, so we don't need 849 + // to check that in. 850 + if (lr->lifetime_pipe != -1) { 851 + lifetime_pipe[1] = socket_bitmap_get(&socket_bitmap); 852 + 853 + if (lr->lifetime_pipe != lifetime_pipe[1]) { 854 + // move the existing pipe to a higher fd number, and invalidate 855 + // the old fd to prevent interfering with fds provided by 856 + // socket_bitmap_get 857 + if (dup2(lr->lifetime_pipe, lifetime_pipe[1]) == -1) { 858 + fprintf(stderr, "Failed to dup process lifetime pipe: %d (%s)\n", errno, strerror(errno)); 859 + exit(1); 860 + } 861 + close(lr->lifetime_pipe); 862 + } 863 + 864 + lifetime_pipe[0] = -1; 865 + } else { 866 + if (__mldr_create_process_lifetime_pipe(lifetime_pipe) == -1) { 867 + fprintf(stderr, "Failed to create process lifetime pipe: %d (%s)\n", errno, strerror(errno)); 868 + exit(1); 869 + } 870 + } 871 + 872 + lr->lifetime_pipe = lifetime_pipe[1]; 873 + 874 + // store the write end of the pipe; the read end is sent to darlingserver. 875 + __dserver_process_lifetime_pipe_fd = lifetime_pipe[1]; 876 + 877 + int dummy_stack_variable; 878 + if (dserver_rpc_checkin(false, &dummy_stack_variable, lifetime_pipe[0]) < 0) { 775 879 fprintf(stderr, "Failed to checkin with darlingserver\n"); 776 880 exit(1); 777 881 } 882 + 883 + // keep our write end while closing the unused read end. 884 + lifetime_pipe[1] = -1; 885 + __mldr_close_process_lifetime_pipe(lifetime_pipe); 778 886 779 887 if (!lr->root_path) { 780 888 static char vchroot_buffer[4096]; ··· 824 932 "r"(lr->stack_top) 825 933 : 826 934 ); 827 - #else 935 + #else 828 936 # error Unsupported platform! 829 937 #endif 830 938 }; 939 + 940 + static bool is_kernel_at_least(int major, int minor) { 941 + if (kernel_major == -1) { 942 + struct utsname uname_info; 943 + if (uname(&uname_info) == -1) { 944 + return false; 945 + } 946 + kernel_major = 0; 947 + kernel_minor = 0; 948 + size_t pos = 0; 949 + while (uname_info.release[pos] != '\0' && uname_info.release[pos] != '.') { 950 + kernel_major = kernel_major * 10 + uname_info.release[pos] - '0'; 951 + ++pos; 952 + } 953 + ++pos; 954 + while (uname_info.release[pos] != '\0' && uname_info.release[pos] != '.') { 955 + kernel_minor = kernel_minor * 10 + uname_info.release[pos] - '0'; 956 + ++pos; 957 + } 958 + } 959 + 960 + if (major != kernel_major) { 961 + return kernel_major > major; 962 + } 963 + 964 + return kernel_minor >= minor; 965 + } 966 + 967 + void* compatible_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { 968 + // MAP_FIXED_NOREPLACE is not supported on WSL1 (Linux < 4.17). 969 + bool fixed_noreplace_hack = false; 970 + if ((flags & MAP_FIXED_NOREPLACE) && !is_kernel_at_least(4, 17)) { 971 + flags &= ~MAP_FIXED_NOREPLACE; 972 + fixed_noreplace_hack = true; 973 + } 974 + void* result = mmap(addr, length, prot, flags, fd, offset); 975 + // MAP_GROWSDOWN is not supported on WSL1. See https://github.com/microsoft/WSL/issues/8095. 976 + if ((result == (void*)MAP_FAILED) && (flags & MAP_GROWSDOWN) && (errno == EOPNOTSUPP)) { 977 + result = mmap(addr, length, prot, (flags & ~MAP_GROWSDOWN), fd, offset); 978 + } 979 + if (fixed_noreplace_hack) { 980 + if (result != addr && result != (void*)MAP_FAILED) { 981 + errno = ESRCH; 982 + munmap(addr, length); 983 + return MAP_FAILED; 984 + } 985 + } 986 + return result; 987 + } 831 988 832 989 static void vchroot_unexpand_interpreter(struct load_results* lr) { 833 990 static char unexpanded[4096];