1 //FIXME deallocate all the port when they are not longer needed
2
3 #include "xnu_threads.h"
4
5 #define set_trace_bit(dbg, thread) modify_trace_bit (dbg, thread, 1)
6 #define clear_trace_bit(dbg, thread) modify_trace_bit (dbg, thread, 0)
7
8
9 #if defined __i386__ || __x86_64__ // intel processors
10
11 /* Set/clear bit 8 (Trap Flag) of the EFLAGS processor control
12 register to enable/disable single-step mode.
13 ENABLE is a boolean, indicating whether to set (1) the Trap Flag
14 or clear it (0). */
15
modify_trace_bit(RDebug * dbg,xnu_thread_t * th,int enable)16 static bool modify_trace_bit(RDebug *dbg, xnu_thread_t *th, int enable) {
17 R_REG_T *state;
18 int ret;
19 ret = xnu_thread_get_gpr (dbg, th);
20 if (!ret) {
21 eprintf ("error to get gpr registers in trace bit intel\n");
22 return false;
23 }
24 state = (R_REG_T *)&th->gpr;
25 if (state->tsh.flavor == x86_THREAD_STATE32) {
26 state->uts.ts32.__eflags = (state->uts.ts32.__eflags & \
27 ~0x100UL) | (enable ? 0x100UL : 0);
28 } else if (state->tsh.flavor == x86_THREAD_STATE64) {
29 state->uts.ts64.__rflags = (state->uts.ts64.__rflags & \
30 ~0x100UL) | (enable ? 0x100UL : 0);
31 } else {
32 eprintf ("Invalid bit size\n");
33 return false;
34 }
35 if (!xnu_thread_set_gpr (dbg, th)) {
36 eprintf ("error xnu_thread_set_gpr in modify_trace_bit intel\n");
37 return false;
38 }
39 return true;
40 }
41
42 #elif __POWERPC__ //ppc processor
43 //XXX poor support at this stage i don't care so much. Once intel and arm done it could be done
44 //TODO add better support for ppc
modify_trace_bit(RDebug * dbg,void * th,int enable)45 static bool modify_trace_bit(RDebug *dbg, void *th, int enable) {
46 return false;
47 }
48 #if 0
49 static bool modify_trace_bit(RDebug *dbg, xnu_thread *th, int enable) {
50 return false;
51 R_REG_T state;
52 unsigned int state_count = R_REG_STATE_SZ;
53 kern_return_t kr;
54 kr = thread_get_state (th->tid, R_REG_STATE_T,
55 (thread_state_t)&state, &state_count);
56 if (kr != KERN_SUCCESS) {
57 eprintf ("error modify_trace_bit\n");
58 return false;
59 }
60 state.srr1 = (state.srr1 & ~0x400UL) | (enable ? 0x400UL : 0);
61 kr = thread_set_state (th->tid, R_REG_STATE_T,
62 (thread_state_t)&state, state_count);
63 if (kr != KERN_SUCCESS) {
64 eprintf ("Error to set thread state modificy_trace_bit ppc\n");
65 return false;
66 }
67 return true;
68 }
69 #endif
70
71 #elif __arm || __arm64 || __aarch64//arm processor
72
73 // BCR address match type
74 #define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21))
75 #define BCR_M_CONTEXT_ID_MATCH ((uint32_t)(1u << 21))
76 #define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21))
77 #define BCR_M_RESERVED ((uint32_t)(3u << 21))
78
79 // Link a BVR/BCR or WVR/WCR pair to another
80 #define E_ENABLE_LINKING ((uint32_t)(1u << 20))
81
82 // Byte Address Select
83 #define BAS_IMVA_PLUS_0 ((uint32_t)(1u << 5))
84 #define BAS_IMVA_PLUS_1 ((uint32_t)(1u << 6))
85 #define BAS_IMVA_PLUS_2 ((uint32_t)(1u << 7))
86 #define BAS_IMVA_PLUS_3 ((uint32_t)(1u << 8))
87 #define BAS_IMVA_0_1 ((uint32_t)(3u << 5))
88 #define BAS_IMVA_2_3 ((uint32_t)(3u << 7))
89 #define BAS_IMVA_ALL ((uint32_t)(0xfu << 5))
90
91 // Break only in privileged or user mode
92 #define S_RSVD ((uint32_t)(0u << 1))
93 #define S_PRIV ((uint32_t)(1u << 1))
94 #define S_USER ((uint32_t)(2u << 1))
95 #define S_PRIV_USER ((S_PRIV) | (S_USER))
96
97 #define BCR_ENABLE ((uint32_t)(1u))
98 #define WCR_ENABLE ((uint32_t)(1u))
99
100 // Watchpoint load/store
101 #define WCR_LOAD ((uint32_t)(1u << 3))
102 #define WCR_STORE ((uint32_t)(1u << 4))
103
104 // Single instruction step
105 // (SS bit in the MDSCR_EL1 register)
106 #define SS_ENABLE ((uint32_t)(1u))
107
108 #if __arm || __arm__ || __armv7 || __armv7__
is_thumb_32(ut16 op)109 static bool is_thumb_32(ut16 op) {
110 return (((op & 0xE000) == 0xE000) && (op & 0x1800));
111 }
112 #endif
113
modify_trace_bit(RDebug * dbg,xnu_thread_t * th,int enable)114 static int modify_trace_bit(RDebug *dbg, xnu_thread_t *th, int enable) {
115 int i = 0;
116 int ret = xnu_thread_get_drx (dbg, th);
117 if (!ret) {
118 eprintf ("error to get drx registers modificy_trace_bit arm\n");
119 return false;
120 }
121 #if __arm64 || __arm64__ || __aarch64 || __aarch64__
122 if (th->flavor == ARM_DEBUG_STATE32) {
123 arm_debug_state32_t *state = &th->debug.drx32;
124 if (enable) {
125 state->__mdscr_el1 = state->__mdscr_el1 | SS_ENABLE;
126 } else {
127 state->__mdscr_el1 = state->__mdscr_el1 & ~SS_ENABLE;
128 }
129 } else if (th->flavor == ARM_DEBUG_STATE64) {
130 arm_debug_state64_t *state = &th->debug.drx64;
131 if (enable) {
132 state->__mdscr_el1 = state->__mdscr_el1 | SS_ENABLE;
133 } else {
134 state->__mdscr_el1 = state->__mdscr_el1 & ~SS_ENABLE;
135 }
136 } else
137 #elif __arm || __arm__ || __armv7 || __armv7__
138 if (th->flavor == ARM_DEBUG_STATE) {
139 arm_debug_state_t *state = &th->debug.drx;
140 R_REG_T *regs;
141 ret = xnu_thread_get_gpr (dbg, th);
142 if (!ret) {
143 eprintf ("error to get gpr register modificy_trace_bit arm\n");
144 return false;
145 }
146 regs = (R_REG_T*)&th->gpr;
147 if (enable) {
148 static ut64 chained_address = 0;
149 RIOBind *bio = &dbg->iob;
150 //set a breakpoint that will stop when the PC doesn't
151 //match the current one
152 //set the current PC as the breakpoint address
153 if (chained_address) {
154 state->__bvr[i] = chained_address & 0xFFFFFFFCu;
155 chained_address = 0;
156 } else {
157 state->__bvr[i] = regs->ts_32.__pc & 0xFFFFFFFCu;
158 }
159 state->__bcr[i] = BCR_M_IMVA_MISMATCH | // stop on
160 // address
161 // mismatch
162 S_USER | // stop only in user mode
163 BCR_ENABLE; // enable this breakpoint
164 if (regs->ts_32.__cpsr & 0x20) {
165 ut16 op;
166 // Thumb breakpoint
167 if (regs->ts_32.__pc & 2)
168 state->__bcr[i] |= BAS_IMVA_2_3;
169 else
170 state->__bcr[i] |= BAS_IMVA_0_1;
171 if (bio->read_at (bio->io, regs->ts_32.__pc, (void *)&op, 2) < 1) {
172 eprintf ("Failed to read opcode modify_trace_bit\n");
173 return false;
174 }
175 if (is_thumb_32 (op)) {
176 chained_address = regs->ts_32.__pc + 2;
177 } else {
178 // Extend the number of bits to ignore for the mismatch
179 state->__bcr[i] |= BAS_IMVA_ALL;
180 }
181 } else {
182 // ARM breakpoint
183 state->__bcr[i] |= BAS_IMVA_ALL; // Stop when any address bits change
184 }
185 //disable bits
186 for (i = i + 1; i < 16; i++) {
187 //Disable all others
188 state->__bcr[i] = 0;
189 state->__bvr[i] = 0;
190 }
191 } else {
192 if (state->__bcr[i] & BCR_ENABLE) {
193 state->__bvr[i] = 0;
194 state->__bcr[i] = 0;
195 }
196 }
197 } else
198 #endif
199 {
200 eprintf ("Bad flavor modificy_trace_bit arm\n");
201 return false;
202 }
203 //set state
204 if (!xnu_thread_set_drx (dbg, th)) {
205 eprintf ("error to set drx modificy_trace_bit arm\n");
206 return false;
207 }
208 return true;
209 }
210
211 #elif __POWERPC__
212 // no need to do this here
modify_trace_bit(RDebug * dbg,xnu_thread * th,int enable)213 static int modify_trace_bit(RDebug *dbg, xnu_thread *th, int enable) {
214 return true;
215 }
216 #else
217 #error "unknown architecture"
218 #endif
219
220 // TODO: Tuck this into RDebug; `void *user` seems like a good candidate.
221 static xnu_exception_info ex = { { 0 } };
222
xnu_restore_exception_ports(int pid)223 static bool xnu_restore_exception_ports (int pid) {
224 kern_return_t kr;
225 int i;
226 task_t task = pid_to_task (pid);
227 if (!task)
228 return false;
229 for (i = 0; i < ex.count; i++) {
230 kr = task_set_exception_ports (task, ex.masks[i], ex.ports[i],
231 ex.behaviors[i], ex.flavors[i]);
232 if (kr != KERN_SUCCESS) {
233 eprintf ("fail to restore exception ports\n");
234 return false;
235 }
236 }
237 kr = mach_port_deallocate (mach_task_self (), ex.exception_port);
238 if (kr != KERN_SUCCESS) {
239 eprintf ("failed to deallocate exception port\n");
240 return false;
241 }
242 return true;
243 }
244
245
246 //TODO review more closely we are failing here
encode_reply(mig_reply_error_t * reply,mach_msg_header_t * hdr,int code)247 static void encode_reply(mig_reply_error_t *reply, mach_msg_header_t *hdr, int code) {
248 mach_msg_header_t *rh = &reply->Head;
249 rh->msgh_bits = MACH_MSGH_BITS (MACH_MSGH_BITS_REMOTE(hdr->msgh_bits), 0);
250 rh->msgh_remote_port = hdr->msgh_remote_port;
251 rh->msgh_size = (mach_msg_size_t) sizeof (mig_reply_error_t);
252 rh->msgh_local_port = MACH_PORT_NULL;
253 rh->msgh_id = hdr->msgh_id + 100;
254 reply->NDR = NDR_record;
255 reply->RetCode = code;
256 }
257
validate_mach_message(RDebug * dbg,exc_msg * msg)258 static bool validate_mach_message (RDebug *dbg, exc_msg *msg) {
259 kern_return_t kr;
260 #if __POWERPC__
261 return false;
262 #else
263 /*check if the message is for us*/
264 if (msg->hdr.msgh_local_port != ex.exception_port) {
265 return false;
266 }
267 /*gdb from apple check this so why not us*/
268 if (!(msg->hdr.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
269 return false;
270 }
271 /*mach exception we are interested*/
272 //XXX for i386 this id seems to be different
273 if (msg->hdr.msgh_id > 2405 || msg->hdr.msgh_id < 2401) {
274 return false;
275 }
276 /* check descriptors. */
277 if (msg->hdr.msgh_size <
278 sizeof (mach_msg_header_t) + sizeof (mach_msg_body_t) +
279 2 * sizeof (mach_msg_port_descriptor_t) +
280 sizeof (NDR_record_t) + sizeof (exception_type_t) +
281 sizeof (mach_msg_type_number_t) +
282 sizeof (mach_exception_data_t))
283 return false;
284 /* check data representation. */
285 if (msg->NDR.mig_vers != NDR_PROTOCOL_2_0 ||
286 msg->NDR.if_vers != NDR_PROTOCOL_2_0 ||
287 msg->NDR.mig_encoding != NDR_record.mig_encoding ||
288 msg->NDR.int_rep != NDR_record.int_rep ||
289 msg->NDR.char_rep != NDR_record.char_rep ||
290 msg->NDR.float_rep != NDR_record.float_rep) {
291 return false;
292 }
293 if (pid_to_task (dbg->pid) != msg->task.name) {
294 //we receive a exception from an unknown process this could
295 //happen if the child fork, as the created process will inherit
296 //its exception port
297 /*we got new rights to the task, get rid of it.*/
298 kr = mach_port_deallocate (mach_task_self (), msg->task.name);
299 if (kr != KERN_SUCCESS) {
300 eprintf ("validate_mach_message: failed to deallocate task port\n");
301 }
302 kr = mach_port_deallocate (mach_task_self (), msg->thread.name);
303 if (kr != KERN_SUCCESS) {
304 eprintf ("validate_mach_message2: failed to deallocated task port\n");
305 }
306 return false;
307 }
308 return true;
309 #endif
310 }
311
handle_dead_notify(RDebug * dbg,exc_msg * msg)312 static bool handle_dead_notify (RDebug *dbg, exc_msg *msg) {
313 if (msg->hdr.msgh_id == 0x48) {
314 dbg->pid = -1;
315 return true;
316 }
317 return false;
318 }
319
handle_exception_message(RDebug * dbg,exc_msg * msg,int * ret_code)320 static int handle_exception_message (RDebug *dbg, exc_msg *msg, int *ret_code) {
321 int ret = R_DEBUG_REASON_UNKNOWN;
322 kern_return_t kr;
323 *ret_code = KERN_SUCCESS;
324 switch (msg->exception) {
325 case EXC_BAD_ACCESS:
326 ret = R_DEBUG_REASON_SEGFAULT;
327 *ret_code = KERN_FAILURE;
328 kr = task_suspend (msg->task.name);
329 if (kr != KERN_SUCCESS) {
330 eprintf ("failed to suspend task bad access\n");
331 }
332 eprintf ("EXC_BAD_ACCESS\n");
333 break;
334 case EXC_BAD_INSTRUCTION:
335 ret = R_DEBUG_REASON_ILLEGAL;
336 *ret_code = KERN_FAILURE;
337 kr = task_suspend (msg->task.name);
338 if (kr != KERN_SUCCESS) {
339 eprintf ("failed to suspend task bad instruction\n");
340 }
341 eprintf ("EXC_BAD_INSTRUCTION\n");
342 break;
343 case EXC_ARITHMETIC:
344 eprintf ("EXC_ARITHMETIC\n");
345 break;
346 case EXC_EMULATION:
347 eprintf ("EXC_EMULATION\n");
348 break;
349 case EXC_SOFTWARE:
350 eprintf ("EXC_SOFTWARE\n");
351 break;
352 case EXC_BREAKPOINT:
353 kr = task_suspend (msg->task.name);
354 if (kr != KERN_SUCCESS) {
355 eprintf ("failed to suspend task breakpoint\n");
356 }
357 ret = R_DEBUG_REASON_BREAKPOINT;
358 break;
359 default:
360 eprintf ("UNKNOWN\n");
361 break;
362 }
363 kr = mach_port_deallocate (mach_task_self (), msg->task.name);
364 if (kr != KERN_SUCCESS) {
365 eprintf ("failed to deallocate task port\n");
366 }
367 kr = mach_port_deallocate (mach_task_self (), msg->thread.name);
368 if (kr != KERN_SUCCESS) {
369 eprintf ("failed to deallocated task port\n");
370 }
371 return ret;
372 }
373
374 //TODO improve this code
__xnu_wait(RDebug * dbg,int pid)375 static int __xnu_wait (RDebug *dbg, int pid) {
376 // here comes the important thing
377 kern_return_t kr;
378 int ret_code, reason = R_DEBUG_REASON_UNKNOWN;
379 mig_reply_error_t reply;
380 bool ret;
381 exc_msg msg;
382 if (!dbg) {
383 return reason;
384 }
385 msg.hdr.msgh_local_port = ex.exception_port;
386 msg.hdr.msgh_size = sizeof (exc_msg);
387 for (;;) {
388 kr = mach_msg (
389 &msg.hdr,
390 MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0,
391 sizeof (exc_msg), ex.exception_port,
392 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
393 if (kr == MACH_RCV_INTERRUPTED) {
394 reason = R_DEBUG_REASON_MACH_RCV_INTERRUPTED;
395 break;
396 } else if (kr != MACH_MSG_SUCCESS) {
397 eprintf ("message didn't succeeded\n");
398 break;
399 }
400 ret = validate_mach_message (dbg, &msg);
401 if (!ret) {
402 ret = handle_dead_notify (dbg, &msg);
403 if (ret) {
404 reason = R_DEBUG_REASON_DEAD;
405 break;
406 }
407 }
408 if (!ret) {
409 encode_reply (&reply, &msg.hdr, KERN_FAILURE);
410 kr = mach_msg (&reply.Head, MACH_SEND_MSG | MACH_SEND_INTERRUPT,
411 reply.Head.msgh_size, 0,
412 MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
413 MACH_PORT_NULL);
414 if (reply.Head.msgh_remote_port != 0 && kr != MACH_MSG_SUCCESS) {
415 kr = mach_port_deallocate(mach_task_self (), reply.Head.msgh_remote_port);
416 if (kr != KERN_SUCCESS) {
417 eprintf ("failed to deallocate reply port\n");
418 }
419 }
420 continue;
421 }
422
423 reason = handle_exception_message (dbg, &msg, &ret_code);
424 encode_reply (&reply, &msg.hdr, ret_code);
425 kr = mach_msg (&reply.Head, MACH_SEND_MSG | MACH_SEND_INTERRUPT,
426 reply.Head.msgh_size, 0,
427 MACH_PORT_NULL, 0,
428 MACH_PORT_NULL);
429 if (reply.Head.msgh_remote_port != 0 && kr != MACH_MSG_SUCCESS) {
430 kr = mach_port_deallocate(mach_task_self (), reply.Head.msgh_remote_port);
431 if (kr != KERN_SUCCESS)
432 eprintf ("failed to deallocate reply port\n");
433 }
434 break; // to avoid infinite loops
435 }
436 dbg->stopaddr = r_debug_reg_get (dbg, "PC");
437 return reason;
438 }
439
xnu_create_exception_thread(RDebug * dbg)440 bool xnu_create_exception_thread(RDebug *dbg) {
441 #if __POWERPC__
442 return false;
443 #else
444 kern_return_t kr;
445 mach_port_t exception_port = MACH_PORT_NULL;
446 mach_port_t req_port;
447 // Got the mach port for the current process
448 mach_port_t task_self = mach_task_self ();
449 task_t task = pid_to_task (dbg->pid);
450 if (!task) {
451 eprintf ("error to get task for the debuggee process"
452 " xnu_start_exception_thread\n");
453 return false;
454 }
455 r_debug_ptrace (dbg, PT_ATTACHEXC, dbg->pid, 0, 0);
456 if (!MACH_PORT_VALID (task_self)) {
457 eprintf ("error to get the task for the current process"
458 " xnu_start_exception_thread\n");
459 return false;
460 }
461 // Allocate an exception port that we will use to track our child process
462 kr = mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE,
463 &exception_port);
464 RETURN_ON_MACH_ERROR ("error to allocate mach_port exception\n", false);
465 // Add the ability to send messages on the new exception port
466 kr = mach_port_insert_right (task_self, exception_port, exception_port,
467 MACH_MSG_TYPE_MAKE_SEND);
468 RETURN_ON_MACH_ERROR ("error to allocate insert right\n", false);
469 // Atomically swap out (and save) the child process's exception ports
470 // for the one we just created. We'll want to receive all exceptions.
471 ex.count = (sizeof (ex.ports) / sizeof (*ex.ports));
472 kr = task_swap_exception_ports (task, EXC_MASK_ALL, exception_port,
473 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
474 ex.masks, &ex.count, ex.ports, ex.behaviors, ex.flavors);
475 RETURN_ON_MACH_ERROR ("failed to swap exception ports\n", false);
476 //get notification when process die
477 kr = mach_port_request_notification (task_self, task, MACH_NOTIFY_DEAD_NAME,
478 0, exception_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &req_port);
479 if (kr != KERN_SUCCESS) {
480 eprintf ("Termination notification request failed\n");
481 }
482 ex.exception_port = exception_port;
483 return true;
484 #endif
485 }
486