this repo has no description
1
fork

Configure Feed

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

Fix workqueue thread support

I was accidentally clobbering the `stack_addr` arg which is actually the `flags` arg for workqueues.

Also, use `stack_addr` from `args` instead of `rdi` (which is the dthread pointer) because the user could have allocated a custom stack and libpthread passes that in.

Finally, workqueues have a separate flag to indicate that the TSD base has been set.

+39 -18
+5 -2
src/libelfloader/native/dthreads.h
··· 1 1 #ifndef _ELFLOADER_DTHREADS_H_ 2 2 #define _ELFLOADER_DTHREADS_H_ 3 3 4 - // Darwin + pthread = dthread ;) 4 + // Darwin + pthreads = dthreads ;) 5 + // also: "dwq" stands for "Darwin workqueue" 5 6 6 7 #include <stdint.h> 7 8 ··· 66 67 67 68 #define DTHREAD_DEFAULT_PRIORITY (1U << (DARWIN_THREAD_QOS_LEGACY - 1 + DTHREAD_PRIORITY_QOS_CLASS_SHIFT)) | ((uint8_t)-1 & DTHREAD_PRIORITY_PRIORITY_MASK) 68 69 70 + #define DWQ_FLAG_THREAD_TSD_BASE_SET 0x00200000 71 + 69 72 // *** 70 73 // KEEP IN SYNC WITH THE DEFINITION OF `struct _pthread` IN libpthread's `internal.h` 71 74 // *** ··· 124 127 void* tsd[DARWIN_EXTERNAL_POSIX_THREAD_KEYS_MAX + DARWIN_INTERNAL_POSIX_THREAD_KEYS_MAX]; 125 128 }* dthread_t; 126 129 127 - #endif // _ELFLOADER_DARWIN_PTHREADS_H_ 130 + #endif // _ELFLOADER_DTHREADS_H_
+34 -16
src/libelfloader/native/threads.c
··· 44 44 { 45 45 thread_ep entry_point; 46 46 uintptr_t real_entry_point; 47 - uintptr_t user_arg; 48 - uintptr_t stack_addr; 49 - uintptr_t flags; 47 + uintptr_t arg1; // `user_arg` for normal threads; `keventlist` for workqueues 48 + uintptr_t arg2; // `stack_addr` for normal threads; `flags` for workqueues 49 + uintptr_t arg3; // `flags` for normal threads; `nkevents` for workqueues 50 50 union { 51 51 void* _backwards_compat; // kept around to avoid modifiying assembly 52 52 int port; ··· 55 55 void* pth; 56 56 darling_thread_create_callbacks_t callbacks; 57 57 uintptr_t stack_bottom; 58 + uintptr_t stack_addr; 59 + bool is_workqueue; 58 60 }; 59 61 60 62 static void* darling_thread_entry(void* p); ··· 117 119 // stack_addr points to the top of the stack (i.e. the highest address) 118 120 *stack_addr = ((char*)base_addr) + stack_size + guard_size; 119 121 122 + // the dthread sits above the stack 123 + // (and by "above", i mean the lowest address of the dthread is the highest address of the stack) 120 124 dthread_t dthread = (dthread_t)*stack_addr; 125 + // zero-out the entrire dthread structure 121 126 memset(dthread, 0, sizeof(struct _dthread)); 122 127 123 - // the dthread sits above the stack 124 - // (and by "above", i mean the lowest address of the dthread is the highest address of the stack) 125 128 return dthread_structure_init(dthread, guard_size, *stack_addr, stack_size, base_addr, total_size); 126 129 }; 127 130 128 131 void* __darling_thread_create(unsigned long stack_size, unsigned long pth_obj_size, 129 132 void* entry_point, uintptr_t real_entry_point, 130 - uintptr_t user_arg, uintptr_t stack_addr, uintptr_t flags, 133 + uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, 131 134 darling_thread_create_callbacks_t callbacks, void* pth) 132 135 { 133 - struct arg_struct args = { (thread_ep) entry_point, real_entry_point, 134 - user_arg, stack_addr, flags, (int)0, pth_obj_size, NULL, callbacks, (char*)stack_addr - stack_size }; 136 + struct arg_struct args = { 137 + .entry_point = (thread_ep)entry_point, 138 + .real_entry_point = real_entry_point, 139 + .arg1 = arg1, 140 + .arg2 = arg2, 141 + .arg3 = arg3, 142 + .port = 0, 143 + .pth_obj_size = pth_obj_size, 144 + .pth = NULL, // set later on 145 + .callbacks = callbacks, 146 + .stack_addr = NULL, // set later on 147 + .is_workqueue = real_entry_point == 0, // our `workq_kernreturn` sets `real_entry_point` to NULL; `bsdthread_create` actually passes a value 148 + }; 135 149 pthread_attr_t attr; 136 150 pthread_t nativeLibcThread; 137 151 138 152 pthread_attr_init(&attr); 139 153 //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 140 154 // pthread_attr_setstacksize(&attr, stack_size); 141 - 155 + 142 156 // in some cases, we're already given a pthread object, stack, and guard page; 143 157 // in those cases, just use what we're given (it also contains more information) 144 158 // 145 159 // otherwise, allocate them ourselves 146 - if (pth == NULL) { 147 - pth = dthread_structure_allocate(stack_size, DEFAULT_DTHREAD_GUARD_SIZE, &stack_addr); 148 - args.stack_addr = stack_addr; 149 - args.stack_bottom = stack_addr - stack_size; 160 + if (pth == NULL || args.is_workqueue) { 161 + pth = dthread_structure_allocate(stack_size, DEFAULT_DTHREAD_GUARD_SIZE, &args.stack_addr); 162 + } else if (!args.is_workqueue) { 163 + // `arg2` is `stack_addr` for normal threads 164 + args.stack_addr = arg2; 150 165 } 166 + 167 + args.stack_bottom = args.stack_addr - stack_size; 151 168 152 169 // pthread_attr_setstack is buggy. The documentation states we should provide the lowest 153 170 // address of the stack, yet some versions regard it as the highest address instead. ··· 179 196 memcpy(&args, in_args, sizeof(args)); 180 197 181 198 dthread_t dthread = args.pth; 199 + uintptr_t* flags = args.is_workqueue ? &args.arg2 : &args.arg3; 182 200 183 201 // libpthread now expects the kernel to set the TSD 184 202 // so, since we're pretending to be the kernel handling threads... 185 203 args.callbacks->thread_set_tsd_base(&dthread->tsd[0]); 186 - args.flags |= DTHREAD_START_TSD_BASE_SET; 204 + *flags |= args.is_workqueue ? DWQ_FLAG_THREAD_TSD_BASE_SET : DTHREAD_START_TSD_BASE_SET; 187 205 188 206 args.port = dthread->tsd[DTHREAD_TSD_SLOT_MACH_THREAD_SELF] = args.callbacks->thread_self_trap(); 189 207 ··· 200 218 #ifdef __x86_64__ 201 219 __asm__ __volatile__ ( 202 220 "movq %1, %%rdi\n" 203 - "movq %%rdi, %%rsp\n" 221 + "movq 80(%0), %%rsp\n" 204 222 "movq 40(%0), %%rsi\n" 205 223 "movq 8(%0), %%rdx\n" 206 224 "testq %%rdx, %%rdx\n" ··· 220 238 #elif defined(__i386__) // args in eax, ebx, ecx, edx, edi, esi 221 239 __asm__ __volatile__ ( 222 240 "movl (%0), %%eax\n" 223 - "movl %1, %%esp\n" 241 + "movl 40(%0), %%esp\n" 224 242 "pushl %%eax\n" // address to be jumped to 225 243 "movl %1, 28(%0)\n" 226 244 "movl %1, %%eax\n" // 1st arg