Rizin
unix-like reverse engineering framework and cli tools
xnu_threads.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2009-2019 pancake <pancake@nopcode.org>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
4 #include <rz_debug.h>
5 
6 // TODO much work remains to be done
7 #include "xnu_debug.h"
8 #include "xnu_threads.h"
9 
10 static void xnu_thread_free(xnu_thread_t *thread) {
11  kern_return_t kr;
12  if (!thread) {
13  return;
14  }
15  free(thread->name);
16  // if we free our thread from the list we need to decrement the ref
17  // count
18  kr = mach_port_deallocate(mach_task_self(), thread->port);
19  if (kr != KERN_SUCCESS) {
20  eprintf("failed to deallocate thread port\n");
21  }
22  free(thread);
23 }
24 
25 // XXX this should work as long as in arm trace bit relies on this
27  rz_return_val_if_fail(dbg && thread, false);
28  kern_return_t rc;
29 #if __x86_64__ || __i386__
30  thread->flavor = x86_DEBUG_STATE;
31  thread->count = x86_DEBUG_STATE_COUNT;
32  thread->state_size = (dbg->bits == RZ_SYS_BITS_64)
33  ? sizeof(x86_debug_state64_t)
34  : sizeof(x86_debug_state32_t);
35  thread->state = &thread->drx.uds;
36  rc = thread_get_state(thread->port, thread->flavor,
37  (thread_state_t)&thread->drx, &thread->count);
38 #elif __arm64__ || __arm64 || __aarch64 || __aarch64__
39  if (dbg->bits == RZ_SYS_BITS_64) {
40  thread->count = ARM_DEBUG_STATE64_COUNT;
41  thread->flavor = ARM_DEBUG_STATE64;
42  rc = thread_get_state(thread->port, thread->flavor,
43  (thread_state_t)&thread->debug.drx64,
44  &thread->count);
45  } else {
46  thread->count = ARM_DEBUG_STATE32_COUNT;
47  thread->flavor = ARM_DEBUG_STATE32;
48  rc = thread_get_state(thread->port, thread->flavor,
49  (thread_state_t)&thread->debug.drx32,
50  &thread->count);
51  }
52 #elif __arm__ || __arm || __armv7__
53  thread->count = ARM_DEBUG_STATE_COUNT;
54  thread->flavor = ARM_DEBUG_STATE;
55  rc = thread_get_state(thread->port, thread->flavor,
56  (thread_state_t)&thread->debug.drx,
57  &thread->count);
58 #else
59 #warning xnu_thread_get_drx: Unsupported architecture
60  rc = KERN_FAILURE;
61 #endif
62  if (rc != KERN_SUCCESS) {
63  LOG_MACH_ERROR("thread_get_state", rc);
64  thread->count = 0;
65  return false;
66  }
67  return true;
68 }
69 
71  rz_return_val_if_fail(dbg && thread, false);
72  kern_return_t rc;
73 #if __i386__ || __x86_64__
74  x86_debug_state_t *regs = &thread->drx;
75  if (!regs) {
76  return false;
77  }
78  thread->flavor = x86_DEBUG_STATE;
79  thread->count = x86_DEBUG_STATE_COUNT;
80  if (dbg->bits == RZ_SYS_BITS_64) {
81  regs->dsh.flavor = x86_DEBUG_STATE64;
82  regs->dsh.count = x86_DEBUG_STATE64_COUNT;
83  } else {
84  regs->dsh.flavor = x86_DEBUG_STATE32;
85  regs->dsh.count = x86_DEBUG_STATE32_COUNT;
86  }
87  rc = thread_set_state(thread->port, thread->flavor,
88  (thread_state_t)regs, thread->count);
89 #elif __arm64__ || __arm64 || __aarch64 || __aarch64__
90  if (dbg->bits == RZ_SYS_BITS_64) {
91  thread->count = ARM_DEBUG_STATE64_COUNT;
92  thread->flavor = ARM_DEBUG_STATE64;
93  rc = thread_set_state(thread->port, thread->flavor,
94  (thread_state_t)&thread->debug.drx64,
95  thread->count);
96  } else {
97  thread->count = ARM_DEBUG_STATE32_COUNT;
98  thread->flavor = ARM_DEBUG_STATE32;
99  rc = thread_set_state(thread->port, thread->flavor,
100  (thread_state_t)&thread->debug.drx32,
101  thread->count);
102  }
103 #elif __arm__ || __arm || __armv7__
104  thread->count = ARM_DEBUG_STATE_COUNT;
105  thread->flavor = ARM_DEBUG_STATE;
106  rc = thread_set_state(thread->port, thread->flavor,
107  (thread_state_t)&thread->debug.drx,
108  &thread->count);
109 #elif __POWERPC__
110 /* not supported */
111 #ifndef PPC_DEBUG_STATE32
112 #define PPC_DEBUG_STATE32 1
113 #endif
114  ppc_debug_state_t *regs;
115  // thread->flavor = PPC_DEBUG_STATE32;
116  // thread->count = RZ_MIN (thread->count, sizeof (regs->uds.ds32));
117  return false;
118 #else
119  regs->dsh.flavor = 0;
120  thread->count = 0;
121 #endif
122  if (rc != KERN_SUCCESS) {
123  LOG_MACH_ERROR("thread_set_state", rc);
124  thread->count = 0;
125  return false;
126  }
127  return true;
128 }
129 
131  rz_return_val_if_fail(dbg && thread, false);
132  kern_return_t rc;
133  RZ_REG_T *regs = (RZ_REG_T *)&thread->gpr;
134  if (!regs) {
135  return false;
136  }
137 #if __i386__ || __x86_64__
138  // thread->flavor is used in a switch+case but in regs->tsh.flavor we
139  // specify
140  thread->state = &regs->uts;
141  // thread->state = regs;
142  thread->flavor = x86_THREAD_STATE;
143  thread->count = x86_THREAD_STATE_COUNT;
144  if (dbg->bits == RZ_SYS_BITS_64) {
145  regs->tsh.flavor = x86_THREAD_STATE64;
146  regs->tsh.count = x86_THREAD_STATE64_COUNT;
147  } else {
148  regs->tsh.flavor = x86_THREAD_STATE32;
149  regs->tsh.count = x86_THREAD_STATE32_COUNT;
150  }
151 #elif __arm64 || __aarch64 || __arm64__ || __aarch64__
152 #if 0
153  /* unified doesnt seems to work */
154  thread->flavor = ARM_UNIFIED_THREAD_STATE;
155  thread->count = ARM_UNIFIED_THREAD_STATE_COUNT;
156 #endif
157  // thread->state = regs;
158  thread->state = &regs->uts;
159  if (dbg->bits == RZ_SYS_BITS_64) {
160  thread->flavor = ARM_THREAD_STATE64;
161  thread->count = ARM_THREAD_STATE64_COUNT;
162  thread->state_size = sizeof(arm_thread_state64_t);
163  } else {
164  thread->flavor = ARM_THREAD_STATE32;
165  thread->count = ARM_THREAD_STATE32_COUNT;
166  thread->state_size = sizeof(arm_thread_state32_t);
167  }
168 #elif __arm || __arm__ || __armv7__
169  thread->flavor = ARM_THREAD_STATE;
170  thread->count = ARM_THREAD_STATE_COUNT;
171  thread->state_size = sizeof(arm_thread_state_t);
172 
173 #endif
174  rc = thread_set_state(thread->port, thread->flavor,
175  (thread_state_t)regs, thread->count);
176  if (rc != KERN_SUCCESS) {
177  LOG_MACH_ERROR("thread_set_state", rc);
178  thread->count = 0;
179  return false;
180  }
181  return true;
182 }
183 
185  rz_return_val_if_fail(dbg && thread, false);
186  RZ_REG_T *regs = &thread->gpr;
187  if (!regs) {
188  return false;
189  }
190  kern_return_t rc;
191 #if __POWERPC__
192  thread->state = regs;
193 #elif __arm64 || __aarch64 || __aarch64__ || __arm64__
194  // thread->state = regs;
195  thread->state = &regs->uts;
196  if (dbg->bits == RZ_SYS_BITS_64) {
197  thread->flavor = ARM_THREAD_STATE64;
198  thread->count = ARM_THREAD_STATE64_COUNT;
199  thread->state_size = sizeof(arm_thread_state64_t);
200  } else {
201  thread->flavor = ARM_THREAD_STATE;
202  thread->count = ARM_THREAD_STATE_COUNT;
203  thread->state_size = sizeof(arm_thread_state32_t);
204  }
205 #elif __arm || __arm__ || __armv7__
206  thread->state = regs;
207  thread->flavor = ARM_THREAD_STATE;
208  thread->count = ARM_THREAD_STATE_COUNT;
209  thread->state_size = sizeof(arm_thread_state_t);
210 #elif __x86_64__ || __i386__
211  thread->state = &regs->uts;
212  thread->flavor = x86_THREAD_STATE;
213  thread->count = x86_THREAD_STATE_COUNT;
214  thread->state_size = (dbg->bits == RZ_SYS_BITS_64) ? sizeof(x86_thread_state64_t) : sizeof(x86_thread_state32_t);
215 #endif
216  rc = thread_get_state(thread->port, thread->flavor,
217  (thread_state_t)regs, &thread->count);
218  if (rc != KERN_SUCCESS) {
219  LOG_MACH_ERROR("thread_get_state", rc);
220  thread->count = 0;
221  return false;
222  }
223  return true;
224 }
225 
227 #if __POWERPC__
228  thread->name = strdup("unknown");
229  return false;
230 #else
231  mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
232  thread_identifier_info_data_t identifier_info;
233  kern_return_t kr = thread_info(thread->port, THREAD_BASIC_INFO,
234  (thread_info_t)&thread->basic_info, &count);
235  if (kr != KERN_SUCCESS) {
236  eprintf("Fail to get thread_basic_info\n");
237  return false;
238  }
239  count = THREAD_IDENTIFIER_INFO_COUNT;
240  kr = thread_info(thread->port, THREAD_IDENTIFIER_INFO,
241  (thread_info_t)&identifier_info, &count);
242  if (kr != KERN_SUCCESS) {
243  eprintf("Fail to get thread_identifier_info\n");
244  return false;
245  }
246  RZ_FREE(thread->name);
247 #if TARGET_OS_IPHONE
248  // TODO proc_pidinfo here
249  thread->name = strdup("unknown");
250 #else
251  struct proc_threadinfo proc_threadinfo;
252  int ret_proc = proc_pidinfo(dbg->pid, PROC_PIDTHREADINFO,
253  identifier_info.thread_handle,
254  &proc_threadinfo, PROC_PIDTHREADINFO_SIZE);
255  if (ret_proc && proc_threadinfo.pth_name[0]) {
256  thread->name = strdup(proc_threadinfo.pth_name);
257  } else {
258  thread->name = strdup("unknown");
259  }
260 #endif
261 #endif
262  return true;
263 }
264 
266  xnu_thread_t *thread = RZ_NEW0(xnu_thread_t);
267  if (!thread) {
268  return NULL;
269  }
270  thread->port = port;
271  if (!xnu_fill_info_thread(dbg, thread)) {
272  free(thread->name);
273  thread->name = strdup("unknown");
274  }
275  return thread;
276 }
277 
279  if (!xnu_fill_info_thread(dbg, thread)) {
280  free(thread->name);
281  thread->name = strdup("unknown");
282  }
283  return true;
284 }
285 
286 static int thread_find(thread_t *port, xnu_thread_t *a) {
287  return (a && port && (a->port == *port)) ? 0 : 1;
288 }
289 
291  thread_array_t thread_list = NULL;
292  unsigned int thread_count = 0;
293  xnu_thread_t *thread;
294  kern_return_t kr;
295  task_t task;
296  int i;
297 
298  if (!dbg->threads) {
300  if (!dbg->threads) {
301  return false;
302  }
303  }
304  // ok we have the list that will hold our threads, now is time to get
305  // them
306  task = pid_to_task(dbg->pid);
307  if (!task) {
308  return false;
309  }
310  kr = task_threads(task, &thread_list, &thread_count);
311  if (kr != KERN_SUCCESS) {
312  // we can get into this when the process has terminated but we
313  // still hold the old task because we are caching it
314  eprintf("Failed to get list of task's threads\n");
315  return false;
316  }
317  if (rz_list_empty(dbg->threads)) {
318  // it's the first time write all threads inside the list
319  for (i = 0; i < thread_count; i++) {
320  thread = xnu_get_thread_with_info(dbg, thread_list[i]);
321  if (!thread) {
322  eprintf("Failed to fill_thread\n");
323  continue;
324  }
325  if (!rz_list_append(dbg->threads, thread)) {
326  eprintf("Failed to add thread to list\n");
327  xnu_thread_free(thread);
328  }
329  }
330  } else {
331  RzListIter *iter, *iter2;
332  // first pass to get rid of those threads that are not longer
333  // alive
334  rz_list_foreach_safe (dbg->threads, iter, iter2, thread) {
335  bool flag = true; // this flag will denote when delete
336  // a thread
337  for (i = 0; i < thread_count; i++) {
338  if (thread->port == thread_list[i]) {
339  flag = false;
340  break;
341  }
342  }
343  if (flag) {
344  // it is not longer alive so remove from the
345  // list
347  } else {
348  // otherwise update the info
349  xnu_update_thread_info(dbg, thread);
350  }
351  }
352  // ok now we have to insert those threads that we don't have
353  for (i = 0; i < thread_count; i++) {
354  xnu_thread_t *t;
355  iter = rz_list_find(dbg->threads, &thread_list[i],
357  // it means is already in our list
358  if (iter) {
359  // free the ownership over the thread
360  kr = mach_port_deallocate(mach_task_self(),
361  thread_list[i]);
362  if (kr != KERN_SUCCESS) {
363  eprintf("Failed to deallocate port\n");
364  }
365  continue;
366  }
367  // otherwise insert it
368  t = xnu_get_thread_with_info(dbg, thread_list[i]);
370  }
371  }
372  // once that is over we need to free the buffer
373  (void)vm_deallocate(mach_task_self(), (mach_vm_address_t)thread_list,
374  thread_count * sizeof(thread_t));
375  return true;
376 }
377 
379  if (!dbg || tid < 0) {
380  return NULL;
381  }
383  eprintf("Failed to update thread_list xnu_udpate_thread_list\n");
384  return NULL;
385  }
386  // TODO get the current thread
387  RzListIter *it = rz_list_find(dbg->threads, (const void *)(size_t)&tid,
389  if (!it) {
390  tid = rz_xnu_get_cur_thread(dbg);
391  it = rz_list_find(dbg->threads, (const void *)(size_t)&tid,
393  if (!it) {
394  eprintf("Thread not found get_xnu_thread\n");
395  return NULL;
396  }
397  }
398  return (xnu_thread_t *)it->data;
399 }
400 
401 /* XXX: right now it just returns the first thread, not the one selected in dbg->tid */
403  thread_t th;
404  thread_array_t threads = NULL;
405  unsigned int n_threads = 0;
406  task_t t = pid_to_task(dbg->pid);
407  if (!t) {
408  return -1;
409  }
410  if (task_threads(t, &threads, &n_threads) != KERN_SUCCESS) {
411  return -1;
412  }
413  if (n_threads > 0) {
414  memcpy(&th, threads, sizeof(th));
415  } else {
416  th = -1;
417  }
418  vm_deallocate(mach_task_self(), (vm_address_t)threads, n_threads * sizeof(thread_act_t));
419  return th;
420 }
static char * regs[]
Definition: analysis_sh.c:203
#define RZ_IPI
Definition: analysis_wasm.c:11
lzma_index ** i
Definition: index.h:629
#define NULL
Definition: cris-opc.c:27
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void count
Definition: sflib.h:98
RzDebug * dbg
Definition: desil.c:30
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
RZ_API RZ_BORROW RzListIter * rz_list_find(RZ_NONNULL const RzList *list, const void *p, RZ_NONNULL RzListComparator cmp)
Returns RzListIter element which matches via the RzListComparator.
Definition: list.c:620
RZ_API RZ_OWN RzList * rz_list_newf(RzListFree f)
Returns a new initialized RzList pointer and sets the free method.
Definition: list.c:248
RZ_API void rz_list_delete(RZ_NONNULL RzList *list, RZ_NONNULL RzListIter *iter)
Removes an entry in the list by using the RzListIter pointer.
Definition: list.c:162
RZ_API RZ_BORROW RzListIter * rz_list_append(RZ_NONNULL RzList *list, void *data)
Appends at the end of the list a new element.
Definition: list.c:288
return strdup("=SP r13\n" "=LR r14\n" "=PC r15\n" "=A0 r0\n" "=A1 r1\n" "=A2 r2\n" "=A3 r3\n" "=ZF zf\n" "=SF nf\n" "=OF vf\n" "=CF cf\n" "=SN or0\n" "gpr lr .32 56 0\n" "gpr pc .32 60 0\n" "gpr cpsr .32 64 0 ____tfiae_________________qvczn\n" "gpr or0 .32 68 0\n" "gpr tf .1 64.5 0 thumb\n" "gpr ef .1 64.9 0 endian\n" "gpr jf .1 64.24 0 java\n" "gpr qf .1 64.27 0 sticky_overflow\n" "gpr vf .1 64.28 0 overflow\n" "gpr cf .1 64.29 0 carry\n" "gpr zf .1 64.30 0 zero\n" "gpr nf .1 64.31 0 negative\n" "gpr itc .4 64.10 0 if_then_count\n" "gpr gef .4 64.16 0 great_or_equal\n" "gpr r0 .32 0 0\n" "gpr r1 .32 4 0\n" "gpr r2 .32 8 0\n" "gpr r3 .32 12 0\n" "gpr r4 .32 16 0\n" "gpr r5 .32 20 0\n" "gpr r6 .32 24 0\n" "gpr r7 .32 28 0\n" "gpr r8 .32 32 0\n" "gpr r9 .32 36 0\n" "gpr r10 .32 40 0\n" "gpr r11 .32 44 0\n" "gpr r12 .32 48 0\n" "gpr r13 .32 52 0\n" "gpr r14 .32 56 0\n" "gpr r15 .32 60 0\n" "gpr r16 .32 64 0\n" "gpr r17 .32 68 0\n")
#define x86_THREAD_STATE_COUNT
@ x86_DEBUG_STATE64
@ x86_THREAD_STATE64
@ x86_DEBUG_STATE32
@ x86_THREAD_STATE32
@ x86_DEBUG_STATE
@ x86_THREAD_STATE
#define x86_THREAD_STATE64_COUNT
#define eprintf(x, y...)
Definition: rlcc.c:7
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
void(* RzListFree)(void *ptr)
Definition: rz_list.h:11
int(* RzListComparator)(const void *value, const void *list_data)
Definition: rz_list.h:33
@ RZ_SYS_BITS_64
Definition: rz_sys.h:21
#define RZ_NEW0(x)
Definition: rz_types.h:284
#define RZ_FREE(x)
Definition: rz_types.h:369
#define a(i)
Definition: sha256.c:41
thread_basic_info_data_t basic_info
Definition: xnu_threads.h:53
thread_t port
Definition: xnu_threads.h:51
char * name
Definition: xnu_threads.h:52
ut32 state_size
Definition: xnu_threads.h:57
RZ_REG_T gpr
Definition: xnu_threads.h:55
void * state
Definition: xnu_threads.h:56
RzList * threads
Definition: rz_debug.h:251
int bits
Definition: rz_debug.h:243
void * data
Definition: rz_list.h:14
static uv_thread_t * threads
Definition: threadpool.c:38
if(dbg->bits==RZ_SYS_BITS_64)
Definition: windows-arm64.h:4
task_t pid_to_task(int pid)
Definition: xnu_debug.c:498
#define LOG_MACH_ERROR(name, rc)
Definition: xnu_debug.h:17
RZ_IPI bool rz_xnu_thread_set_gpr(RzDebug *dbg, xnu_thread_t *thread)
Definition: xnu_threads.c:130
RZ_IPI int rz_xnu_update_thread_list(RzDebug *dbg)
Definition: xnu_threads.c:290
RZ_IPI xnu_thread_t * rz_xnu_get_thread(RzDebug *dbg, int tid)
Definition: xnu_threads.c:378
static bool xnu_fill_info_thread(RzDebug *dbg, xnu_thread_t *thread)
Definition: xnu_threads.c:226
static void xnu_thread_free(xnu_thread_t *thread)
Definition: xnu_threads.c:10
static int xnu_update_thread_info(RzDebug *dbg, xnu_thread_t *thread)
Definition: xnu_threads.c:278
static xnu_thread_t * xnu_get_thread_with_info(RzDebug *dbg, thread_t port)
Definition: xnu_threads.c:265
RZ_IPI thread_t rz_xnu_get_cur_thread(RzDebug *dbg)
Definition: xnu_threads.c:402
RZ_IPI bool rz_xnu_thread_set_drx(RzDebug *dbg, xnu_thread_t *thread)
Definition: xnu_threads.c:70
RZ_IPI bool rz_xnu_thread_get_gpr(RzDebug *dbg, xnu_thread_t *thread)
Definition: xnu_threads.c:184
static int thread_find(thread_t *port, xnu_thread_t *a)
Definition: xnu_threads.c:286
RZ_IPI bool rz_xnu_thread_get_drx(RzDebug *dbg, xnu_thread_t *thread)
Definition: xnu_threads.c:26