]> git.saurik.com Git - apple/libplatform.git/blame - src/introspection/introspection.c
libplatform-254.40.4.tar.gz
[apple/libplatform.git] / src / introspection / introspection.c
CommitLineData
ada7c492
A
1#include "os/internal.h"
2#include "platform/introspection_private.h"
3#include "introspection_internal.h"
4
5#include <mach/mach_error.h>
6#include <mach/mach_init.h>
7#include <mach/mach_port.h>
8#include <mach/mach_vm.h>
9#include <mach/task.h>
10#include <mach/thread_act.h>
11
12#include <sys/sysctl.h>
13
14#include "libkern/OSAtomic.h"
15
16// Returns the number of thread entries that can be stored in a task page.
17static unsigned int
18_platform_threads_per_page(void)
19{
20 // Subtract out the header storage.
21 return (int)(vm_page_size / sizeof(struct platform_thread_s)) - 1;
22}
23
24// Returns the page-aligned base address for a given pthread structure address.
25static mach_vm_size_t
26_platform_pthread_addr(mach_vm_address_t addr) {
27 return trunc_page(addr); // XXX approximation
28}
29
30// Returns the page-aligned size for a given pthread structure address.
31static mach_vm_size_t
32_platform_pthread_size(mach_vm_address_t addr) {
33 return vm_page_size; // XXX approximation
34}
35
36static kern_return_t
37_platform_thread_deallocate(platform_thread_t thread)
38{
39 kern_return_t ret;
40 if (MACH_PORT_VALID(thread->act)) {
41 mach_port_deallocate(mach_task_self(), thread->act);
42 thread->act = MACH_PORT_NULL;
43 }
44
45 if (thread->pthread_addr != 0) {
46 ret = mach_vm_deallocate(mach_task_self(),
47 _platform_pthread_addr(thread->pthread_addr),
48 _platform_pthread_size(thread->pthread_addr));
49 thread->pthread_addr = 0;
50 }
51 return ret;
52}
53
54static kern_return_t
55_platform_task_deallocate(platform_task_t task)
56{
57 kern_return_t ret;
58
59 if (!task) {
60 return KERN_INVALID_TASK;
61 }
62
63 task_t port = task->metadata.port;
64 if (port != MACH_PORT_NULL) {
65 mach_port_deallocate(mach_task_self(), port);
66 }
67
68 platform_task_t ptr = task;
69 do {
70 mach_vm_address_t addr = (mach_vm_address_t)ptr;
71
72 // Deallocate threads.
73 int i, start = (ptr == task) ? 1 : 0; // Skip over meta data.
74 for (i = start; i < _platform_threads_per_page() - start; ++i) {
75 _platform_thread_deallocate(&ptr->threads[i]);
76 }
77
78 ptr = ptr->header.next;
79 ret = mach_vm_deallocate(mach_task_self(), addr, vm_page_size);
80 } while (ptr);
81
82 return ret;
83}
84
85extern int __sysctl(int *, unsigned int, void *, size_t *, void *, size_t);
86
87static kern_return_t
88_platform_task_query_64_bit(platform_task_t task)
89{
90 task_flags_info_data_t task_flags_info;
91 mach_msg_type_number_t count = TASK_FLAGS_INFO_COUNT;
92
93 kern_return_t ret = task_info(task->metadata.port, TASK_FLAGS_INFO, (task_info_t) &task_flags_info, &count);
94 if (ret == KERN_SUCCESS) {
95 task->metadata.is_64_bit = (task_flags_info.flags & TF_LP64) ? true : false;
96 } else if (ret == KERN_INVALID_ARGUMENT) {
97 pid_t pid;
98 kern_return_t ret = pid_for_task(task->metadata.port, &pid);
99 if (ret != KERN_SUCCESS) return ret;
100
101 struct kinfo_proc info;
102 size_t size = sizeof(info);
103 int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid };
104 unsigned int len = sizeof(mib) / sizeof(*mib);
105 int res = __sysctl(mib, len, &info, &size, NULL, 0);
106 if (res == 0 && size >= sizeof(info)) {
107 task->metadata.is_64_bit = (info.kp_proc.p_flag & P_LP64) != 0;
108 }
109 }
110
111 return ret;
112}
113
114kern_return_t
115platform_task_attach(platform_task_t *out_task, task_t port)
116{
117 kern_return_t ret;
118
119 // Test some invariants.
120 _Static_assert(sizeof(struct platform_task_header_s) == 32, "");
121 _Static_assert(sizeof(struct platform_task_metadata_s) == 32, "");
122 _Static_assert(sizeof(struct platform_thread_s) == 32, "");
123
124 // Allocate storage for the returned task handle.
125 mach_vm_address_t addr = 0;
126 mach_vm_size_t size = vm_page_size;
127 ret = mach_vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE);
128 if (ret != KERN_SUCCESS) return ret;
129
130 platform_task_t result = (platform_task_t)addr;
131
132 // Keep a reference to the task port.
133 ret = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1);
134 if (ret != KERN_SUCCESS) {
135 _platform_task_deallocate(result);
136 return ret;
137 }
138
139 result->header.head = result;
140 result->metadata.port = port;
141
142 ret = _platform_task_query_64_bit(result);
143 if (ret != KERN_SUCCESS) {
144 _platform_task_deallocate(result);
145 return ret;
146 }
147
148 *out_task = result;
149
150 return ret;
151}
152
153kern_return_t
154platform_task_detach(platform_task_t task)
155{
156 kern_return_t ret;
157 ret = _platform_task_deallocate(task);
158 return ret;
159}
160
161bool
162platform_task_is_64_bit(platform_task_t task)
163{
164 return task->metadata.is_64_bit;
165}
166
167kern_return_t
168platform_task_suspend_threads(platform_task_t task)
169{
170 return KERN_NOT_SUPPORTED;
171}
172
173kern_return_t
174platform_task_resume_threads(platform_task_t task)
175{
176 return KERN_NOT_SUPPORTED;
177}
178
179kern_return_t
180platform_task_perform(platform_task_t task,
181 mach_vm_address_t func_addr,
182 mach_vm_address_t data_addr)
183{
184 return KERN_NOT_SUPPORTED;
185}
186
187static platform_task_t
188_platform_thread_get_task(platform_thread_t thread)
189{
190 platform_task_t task = (platform_task_t)trunc_page((uintptr_t)thread);
191 platform_task_t head = task->header.head;
192 if (head) {
193 task = head;
194 }
195 return task;
196}
197
198static kern_return_t
199_platform_thread_map(platform_task_t task,
200 platform_thread_t thread,
201 mach_vm_address_t thread_handle)
202{
203 kern_return_t ret;
204 vm_prot_t cur_protection, max_protection;
205
206 vm_offset_t data = 0;
207 mach_vm_size_t wordsize = task->metadata.is_64_bit ? 8 : 4;
208 mach_msg_type_number_t size;
209 ret = mach_vm_read(_platform_thread_get_task(thread)->metadata.port,
210 thread_handle, // &TSD[0]
211 wordsize,
212 &data,
213 &size);
214 if (ret != KERN_SUCCESS) return ret;
215
216 mach_vm_address_t pthread_addr = (uintptr_t)*(void **)data; // deref TSD[0]
217 mach_vm_deallocate(mach_task_self(), data, size);
218
219 mach_vm_address_t src_addr = _platform_pthread_addr(pthread_addr);
220 mach_vm_offset_t offset = pthread_addr - src_addr;
221 mach_vm_address_t dst_addr = 0;
222 ret = mach_vm_remap(mach_task_self(),
223 &dst_addr,
224 _platform_pthread_size(pthread_addr),
225 0,
226 VM_FLAGS_ANYWHERE,
227 _platform_thread_get_task(thread)->metadata.port,
228 src_addr,
229 0, // no copy
230 &cur_protection,
231 &max_protection,
232 VM_INHERIT_NONE);
233 if (ret == KERN_SUCCESS) {
234 thread->pthread_addr = dst_addr + offset;
235 }
236
237 return ret;
238}
239
240// Add a mach thread to the task's thread array. Updates the existing entry
241// with the same unique id if one exists, otherwise allocates a new entry.
242// Consumes the reference to the thread act mach port.
243static kern_return_t
244_platform_task_add_mach_thread(platform_task_t task, thread_act_t act)
245{
246 int i;
247 kern_return_t ret;
248
249 thread_identifier_info_data_t info;
250 mach_msg_type_number_t info_count = THREAD_IDENTIFIER_INFO_COUNT;
251 ret = thread_info(act,
252 THREAD_IDENTIFIER_INFO,
253 (thread_info_t)&info,
254 &info_count);
255 if (ret != KERN_SUCCESS) return ret;
256
257 // Anything older than the previous generation is a candidate for recycling.
258 uint32_t gen = task->metadata.gen - 1;
259
260 // Look for an existing slot with this unique ID or the first empty slot.
261 platform_thread_t empty = NULL;
262 platform_thread_t found = NULL;
263 platform_task_t last, ptr = task;
264 do {
265 int start = (ptr == task) ? 1 : 0; // Skip over meta data.
266 for (i = start; i < _platform_threads_per_page() - start; ++i) {
267 platform_thread_t thread = &ptr->threads[i];
268 if (!empty &&
269 thread->refcnt == 0 &&
270 (thread->unique_id == 0 || thread->gen < gen)) {
271 empty = &ptr->threads[i];
272 } else if (task->threads[i].unique_id == info.thread_id) {
273 found = &ptr->threads[i];
274 break;
275 }
276 }
277 last = ptr;
278 } while (!found && (ptr = ptr->header.next));
279
280 if (found) {
281 mach_port_deallocate(mach_task_self(), found->act);
282 found->act = act;
283 found->gen = task->metadata.gen;
284 } else {
285 if (!empty) {
286 // Allocate new storage if necessary.
287 mach_vm_address_t addr = 0;
288 mach_vm_size_t size = vm_page_size;
289 ret = mach_vm_allocate(mach_task_self(),
290 &addr,
291 size,
292 VM_FLAGS_ANYWHERE);
293 if (ret != KERN_SUCCESS) return ret;
294
295 last = last->header.next = (platform_task_t)addr;
296 last->header.head = task;
297
298 empty = &last->threads[0];
299 } else {
300 _platform_thread_deallocate(empty);
301 }
302
303 empty->act = act; // transfer ownership
304 empty->gen = task->metadata.gen;
305 empty->unique_id = info.thread_id;
306 ret = _platform_thread_map(task, empty, info.thread_handle);
307 }
308
309 return ret;
310}
311
312kern_return_t
313platform_task_update_threads(platform_task_t task)
314{
315 kern_return_t ret;
316 thread_act_array_t array;
317 mach_msg_type_number_t array_count;
318 ret = task_threads(task->metadata.port, &array, &array_count);
319 if (ret != KERN_SUCCESS) return ret;
320
321 ++task->metadata.gen;
322 task->metadata.cursor = &task->threads[1]; // Reset iteration cursor.
323
324 unsigned int i;
325 for (i = 0; i < array_count; ++i) {
326 ret = _platform_task_add_mach_thread(task, array[i]);
327 }
328
329 mach_vm_size_t array_size = array_count * sizeof(*array);
330 mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)array, array_size);
331
332 return ret;
333}
334
335platform_thread_t
336platform_task_copy_next_thread(platform_task_t task)
337{
338 int i;
339 platform_thread_t result = NULL;
340 platform_thread_t cursor = task->metadata.cursor;
341
342 if (cursor == NULL) {
343 // End of iteration.
344 return NULL;
345 }
346
347 uint32_t gen = task->metadata.gen;
348 platform_task_t ptr = (platform_task_t)trunc_page((uintptr_t)cursor);
349
350 do {
351 if (cursor->gen == gen && cursor->unique_id != 0) {
352 result = cursor;
353 }
354
355 ++cursor;
356
357 if ((uintptr_t)cursor >= ((uintptr_t)ptr + vm_page_size)) {
358 ptr = ptr->header.next;
359 if (ptr) {
360 cursor = &ptr->threads[0];
361 } else {
362 cursor = NULL;
363 }
364 }
365 } while (!result && cursor);
366
367 task->metadata.cursor = cursor;
368
369 if (result) {
370 OSAtomicIncrement32(&result->refcnt);
371 }
372
373 return result;
374}
375
376platform_thread_id_t
377platform_thread_get_unique_id(platform_thread_t thread)
378{
379 return thread->unique_id;
380}
381
382void
383platform_thread_release(platform_thread_t thread)
384{
385 int32_t refcnt = OSAtomicDecrement32(&thread->refcnt);
386 if (refcnt < 0) {
387 __LIBPLATFORM_CLIENT_CRASH__(refcnt, "Over-release of platform thread object");
388 }
389}
390
391kern_return_t
392platform_thread_abort_safely(platform_thread_t thread)
393{
394 kern_return_t ret;
395 ret = thread_abort_safely(thread->act);
396 return ret;
397}
398
399kern_return_t
400platform_thread_suspend(platform_thread_t thread)
401{
402 kern_return_t ret;
403 ret = thread_suspend(thread->act);
404 return ret;
405}
406
407kern_return_t
408platform_thread_resume(platform_thread_t thread)
409{
410 kern_return_t ret;
411 ret = thread_resume(thread->act);
412 return ret;
413}
414
415kern_return_t
416platform_thread_info(platform_thread_t thread,
417 thread_flavor_t flavor,
418 void *info,
419 size_t *size)
420{
421 kern_return_t ret;
422 mach_msg_type_number_t count = (int)*size / sizeof(natural_t);
423 ret = thread_info(thread->act, flavor, info, &count);
424 *size = count * sizeof(natural_t);
425 return ret;
426}
427
428kern_return_t
429platform_thread_get_state(platform_thread_t thread,
430 thread_state_flavor_t flavor,
431 void *state,
432 size_t *size)
433{
434 kern_return_t ret;
442fbc9d 435 mach_msg_type_number_t count = (int)(*size / sizeof(natural_t));
ada7c492
A
436 ret = thread_get_state(thread->act, flavor, state, &count);
437 *size = count * sizeof(natural_t);
438 return ret;
439}
440
441kern_return_t
442platform_thread_set_state(platform_thread_t thread,
443 thread_state_flavor_t flavor,
444 const void *state,
445 size_t size)
446{
447 kern_return_t ret;
442fbc9d 448 mach_msg_type_number_t count = (int)(size / sizeof(natural_t));
ada7c492
A
449 ret = thread_set_state(thread->act, flavor, (thread_state_t)state, count);
450 return ret;
451}
452
453kern_return_t
454platform_thread_perform(platform_thread_t thread,
455 mach_vm_address_t func_addr,
456 mach_vm_address_t data_addr)
457{
458 return KERN_NOT_SUPPORTED;
459}
460
461const void *
462platform_thread_get_pthread(platform_thread_t thread)
463{
464 return (const void *) thread->pthread_addr;
465}
466
467#ifdef MAIN
468
469// cc -DMAIN -I../../include/platform introspection.c
470
471#include <stdio.h>
472#include <unistd.h>
473
474int main(int argc, char *argv[]) {
475 kern_return_t ret;
476
477 task_t port = MACH_PORT_NULL;
478 ret = task_for_pid(mach_task_self(), getppid(), &port);
479 if (ret != KERN_SUCCESS) {
480 mach_error("task_for_pid", ret);
481 return 1;
482 }
483
484 platform_task_t task = NULL;
485 ret = platform_task_attach(&task, port);
486 if (ret != KERN_SUCCESS) {
487 mach_error("platform_task_attach", ret);
488 return 1;
489 }
490
491 printf("Task is %s.\n", platform_task_is_64_bit(task) ? "64-bit" : "32-bit");
492
493 int i;
494 for (i = 0; i < 3; ++i) {
495 ret = platform_task_update_threads(task);
496 if (ret != KERN_SUCCESS) {
497 mach_error("platform_task_update_threads", ret);
498 return 1;
499 }
500
501 platform_thread_t thread;
502 while ((thread = platform_task_copy_next_thread(task))) {
503 printf("thread = { .unique_id = 0x%llx, pthread_addr = 0x%llx }\n",
504 thread->unique_id,
505 thread->pthread_addr);
506 printf("pthread = { .sig = %lx, .unique_id = 0x%llx }\n",
507 *(unsigned long *)thread->pthread_addr,
508 *(uint64_t *)((uintptr_t)thread->pthread_addr + 32));
509
510 platform_thread_release(thread);
511 }
512
513 sleep(3);
514 }
515
516 ret = platform_task_detach(task);
517 if (ret != KERN_SUCCESS) {
518 mach_error("platform_task_detach", ret);
519 return 1;
520 }
521
522 return 0;
523}
524#endif