MachException.cpp revision 311128
1//===-- MachException.cpp ---------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10//  Created by Greg Clayton on 6/18/07.
11//
12//===----------------------------------------------------------------------===//
13
14#include "MachException.h"
15
16// C includes
17#include <errno.h>
18#include <sys/ptrace.h>
19#include <sys/types.h>
20
21// C++ includes
22#include <mutex>
23
24// LLDB includes
25#include "lldb/Core/Error.h"
26#include "lldb/Core/Log.h"
27#include "lldb/Core/Stream.h"
28#include "lldb/Target/UnixSignals.h"
29#include "lldb/Utility/LLDBAssert.h"
30
31using namespace lldb;
32using namespace lldb_private;
33using namespace lldb_private::process_darwin;
34
35// Routine mach_exception_raise
36extern "C" kern_return_t
37catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread,
38                           mach_port_t task, exception_type_t exception,
39                           mach_exception_data_t code,
40                           mach_msg_type_number_t codeCnt);
41
42extern "C" kern_return_t catch_mach_exception_raise_state(
43    mach_port_t exception_port, exception_type_t exception,
44    const mach_exception_data_t code, mach_msg_type_number_t codeCnt,
45    int *flavor, const thread_state_t old_state,
46    mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
47    mach_msg_type_number_t *new_stateCnt);
48
49// Routine mach_exception_raise_state_identity
50extern "C" kern_return_t catch_mach_exception_raise_state_identity(
51    mach_port_t exception_port, mach_port_t thread, mach_port_t task,
52    exception_type_t exception, mach_exception_data_t code,
53    mach_msg_type_number_t codeCnt, int *flavor, thread_state_t old_state,
54    mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
55    mach_msg_type_number_t *new_stateCnt);
56
57extern "C" boolean_t mach_exc_server(mach_msg_header_t *InHeadP,
58                                     mach_msg_header_t *OutHeadP);
59
60// Any access to the g_message variable should be done by locking the
61// g_message_mutex first, using the g_message variable, then unlocking
62// the g_message_mutex. See MachException::Message::CatchExceptionRaise()
63// for sample code.
64
65static MachException::Data *g_message = NULL;
66
67extern "C" kern_return_t catch_mach_exception_raise_state(
68    mach_port_t exc_port, exception_type_t exc_type,
69    const mach_exception_data_t exc_data, mach_msg_type_number_t exc_data_count,
70    int *flavor, const thread_state_t old_state,
71    mach_msg_type_number_t old_stateCnt, thread_state_t new_state,
72    mach_msg_type_number_t *new_stateCnt) {
73  // TODO change to LIBLLDB_LOG_EXCEPTION
74  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
75  if (log) {
76    log->Printf("::%s(exc_port = 0x%4.4x, exc_type = %d (%s), "
77                "exc_data = 0x%llx, exc_data_count = %d)",
78                __FUNCTION__, exc_port, exc_type, MachException::Name(exc_type),
79                (uint64_t)exc_data, exc_data_count);
80  }
81  return KERN_FAILURE;
82}
83
84extern "C" kern_return_t catch_mach_exception_raise_state_identity(
85    mach_port_t exc_port, mach_port_t thread_port, mach_port_t task_port,
86    exception_type_t exc_type, mach_exception_data_t exc_data,
87    mach_msg_type_number_t exc_data_count, int *flavor,
88    thread_state_t old_state, mach_msg_type_number_t old_stateCnt,
89    thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) {
90  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
91  if (log) {
92    log->Printf("::%s(exc_port = 0x%4.4x, thd_port = 0x%4.4x, "
93                "tsk_port = 0x%4.4x, exc_type = %d (%s), exc_data[%d] = "
94                "{ 0x%llx, 0x%llx })",
95                __FUNCTION__, exc_port, thread_port, task_port, exc_type,
96                MachException::Name(exc_type), exc_data_count,
97                (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
98                (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
99  }
100  mach_port_deallocate(mach_task_self(), task_port);
101  mach_port_deallocate(mach_task_self(), thread_port);
102
103  return KERN_FAILURE;
104}
105
106extern "C" kern_return_t
107catch_mach_exception_raise(mach_port_t exc_port, mach_port_t thread_port,
108                           mach_port_t task_port, exception_type_t exc_type,
109                           mach_exception_data_t exc_data,
110                           mach_msg_type_number_t exc_data_count) {
111  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
112  if (log) {
113    log->Printf("::%s(exc_port = 0x%4.4x, thd_port = 0x%4.4x, "
114                "tsk_port = 0x%4.4x, exc_type = %d (%s), exc_data[%d] "
115                "= { 0x%llx, 0x%llx })",
116                __FUNCTION__, exc_port, thread_port, task_port, exc_type,
117                MachException::Name(exc_type), exc_data_count,
118                (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD),
119                (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD));
120  }
121
122  if (task_port == g_message->task_port) {
123    g_message->task_port = task_port;
124    g_message->thread_port = thread_port;
125    g_message->exc_type = exc_type;
126    g_message->exc_data.resize(exc_data_count);
127    ::memcpy(&g_message->exc_data[0], exc_data,
128             g_message->exc_data.size() * sizeof(mach_exception_data_type_t));
129    return KERN_SUCCESS;
130  }
131  return KERN_FAILURE;
132}
133
134#if 0
135void
136MachException::Message::Dump(Stream &stream) const
137{
138    stream.Printf("exc_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = "
139                  "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x "
140                  "id = 0x%8.8x }\n",
141        exc_msg.hdr.msgh_bits,
142        exc_msg.hdr.msgh_size,
143        exc_msg.hdr.msgh_remote_port,
144        exc_msg.hdr.msgh_local_port,
145        exc_msg.hdr.msgh_reserved,
146        exc_msg.hdr.msgh_id);
147
148    stream.Printf("reply_msg { bits = 0x%8.8x size = 0x%8.8x remote-port "
149                  "= 0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x "
150                  "id = 0x%8.8x }",
151                  reply_msg.hdr.msgh_bits,
152                  reply_msg.hdr.msgh_size,
153                  reply_msg.hdr.msgh_remote_port,
154                  reply_msg.hdr.msgh_local_port,
155                  reply_msg.hdr.msgh_reserved,
156                  reply_msg.hdr.msgh_id);
157    stream.Flush();
158}
159#endif
160
161bool MachException::Data::GetStopInfo(struct ThreadStopInfo *stop_info,
162                                      const UnixSignals &signals,
163                                      Stream &stream) const {
164  if (!stop_info)
165    return false;
166
167  // Zero out the structure.
168  memset(stop_info, 0, sizeof(struct ThreadStopInfo));
169
170  if (exc_type == 0) {
171    stop_info->reason = eStopReasonInvalid;
172    return true;
173  }
174
175  // We always stop with a mach exception.
176  stop_info->reason = eStopReasonException;
177  // Save the EXC_XXXX exception type.
178  stop_info->details.exception.type = exc_type;
179
180  // Fill in a text description
181  const char *exc_name = MachException::Name(exc_type);
182  if (exc_name)
183    stream.Printf("%s", exc_name);
184  else
185    stream.Printf("%i", exc_type);
186
187  stop_info->details.exception.data_count = exc_data.size();
188
189  int soft_signal = SoftSignal();
190  if (soft_signal) {
191    const char *sig_str = signals.GetSignalAsCString(soft_signal);
192    stream.Printf(" EXC_SOFT_SIGNAL( %i ( %s ))", soft_signal,
193                  sig_str ? sig_str : "unknown signal");
194  } else {
195    // No special disassembly for exception data, just print it.
196    size_t idx;
197    stream.Printf(" data[%llu] = {",
198                  (uint64_t)stop_info->details.exception.data_count);
199
200    for (idx = 0; idx < stop_info->details.exception.data_count; ++idx) {
201      stream.Printf(
202          "0x%llx%c", (uint64_t)exc_data[idx],
203          ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ','));
204    }
205  }
206
207  // Copy the exception data
208  for (size_t i = 0; i < stop_info->details.exception.data_count; i++)
209    stop_info->details.exception.data[i] = exc_data[i];
210
211  return true;
212}
213
214Error MachException::Message::Receive(mach_port_t port,
215                                      mach_msg_option_t options,
216                                      mach_msg_timeout_t timeout,
217                                      mach_port_t notify_port) {
218  Error error;
219  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
220
221  mach_msg_timeout_t mach_msg_timeout =
222      options & MACH_RCV_TIMEOUT ? timeout : 0;
223  if (log && ((options & MACH_RCV_TIMEOUT) == 0)) {
224    // Dump this log message if we have no timeout in case it never returns
225    log->Printf("::mach_msg(msg->{bits = %#x, size = %u remote_port = %#x, "
226                "local_port = %#x, reserved = 0x%x, id = 0x%x}, "
227                "option = %#x, send_size = 0, rcv_size = %llu, "
228                "rcv_name = %#x, timeout = %u, notify = %#x)",
229                exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
230                exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
231                exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options,
232                (uint64_t)sizeof(exc_msg.data), port, mach_msg_timeout,
233                notify_port);
234  }
235
236  mach_msg_return_t mach_err =
237      ::mach_msg(&exc_msg.hdr,
238                 options,              // options
239                 0,                    // Send size
240                 sizeof(exc_msg.data), // Receive size
241                 port,                 // exception port to watch for
242                                       // exception on
243                 mach_msg_timeout,     // timeout in msec (obeyed only
244                                       // if MACH_RCV_TIMEOUT is ORed
245                                       // into the options parameter)
246                 notify_port);
247  error.SetError(mach_err, eErrorTypeMachKernel);
248
249  // Dump any errors we get
250  if (error.Fail() && log) {
251    log->Printf("::mach_msg(msg->{bits = %#x, size = %u remote_port = %#x, "
252                "local_port = %#x, reserved = 0x%x, id = 0x%x}, "
253                "option = %#x, send_size = %u, rcv_size = %lu, rcv_name "
254                "= %#x, timeout = %u, notify = %#x) failed: %s",
255                exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
256                exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
257                exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 0,
258                sizeof(exc_msg.data), port, mach_msg_timeout, notify_port,
259                error.AsCString());
260  }
261  return error;
262}
263
264void MachException::Message::Dump(Stream &stream) const {
265  stream.Printf("  exc_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = "
266                "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = "
267                "0x%8.8x }\n",
268                exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size,
269                exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port,
270                exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id);
271
272  stream.Printf("  reply_msg { bits = 0x%8.8x size = 0x%8.8x remote-port = "
273                "0x%8.8x local-port = 0x%8.8x reserved = 0x%8.8x id = "
274                "0x%8.8x }",
275                reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size,
276                reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port,
277                reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id);
278}
279
280bool MachException::Message::CatchExceptionRaise(task_t task) {
281  bool success = false;
282  // locker will keep a mutex locked until it goes out of scope
283  //    PThreadMutex::Locker locker(&g_message_mutex);
284  //    DNBLogThreaded("calling  mach_exc_server");
285  state.task_port = task;
286  g_message = &state;
287  // The exc_server function is the MIG generated server handling function
288  // to handle messages from the kernel relating to the occurrence of an
289  // exception in a thread. Such messages are delivered to the exception port
290  // set via thread_set_exception_ports or task_set_exception_ports. When an
291  // exception occurs in a thread, the thread sends an exception message to
292  // its exception port, blocking in the kernel waiting for the receipt of a
293  // reply. The exc_server function performs all necessary argument handling
294  // for this kernel message and calls catch_exception_raise,
295  // catch_exception_raise_state or catch_exception_raise_state_identity,
296  // which should handle the exception. If the called routine returns
297  // KERN_SUCCESS, a reply message will be sent, allowing the thread to
298  // continue from the point of the exception; otherwise, no reply message
299  // is sent and the called routine must have dealt with the exception
300  // thread directly.
301  if (mach_exc_server(&exc_msg.hdr, &reply_msg.hdr)) {
302    success = true;
303  } else {
304    Log *log(
305        GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
306    if (log)
307      log->Printf("MachException::Message::%s(): mach_exc_server "
308                  "returned zero...",
309                  __FUNCTION__);
310  }
311  g_message = NULL;
312  return success;
313}
314
315Error MachException::Message::Reply(::pid_t inferior_pid, task_t inferior_task,
316                                    int signal) {
317  // Reply to the exception...
318  Error error;
319
320  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
321
322  // If we had a soft signal, we need to update the thread first so it can
323  // continue without signaling
324  int soft_signal = state.SoftSignal();
325  if (soft_signal) {
326    int state_pid = -1;
327    if (inferior_task == state.task_port) {
328      // This is our task, so we can update the signal to send to it
329      state_pid = inferior_pid;
330      soft_signal = signal;
331    } else {
332      auto mach_err = ::pid_for_task(state.task_port, &state_pid);
333      if (mach_err) {
334        error.SetError(mach_err, eErrorTypeMachKernel);
335        if (log)
336          log->Printf("MachException::Message::%s(): pid_for_task() "
337                      "failed: %s",
338                      __FUNCTION__, error.AsCString());
339        return error;
340      }
341    }
342
343    lldbassert(state_pid != -1);
344    if (state_pid != -1) {
345      errno = 0;
346      caddr_t thread_port_caddr = (caddr_t)(uintptr_t)state.thread_port;
347      if (::ptrace(PT_THUPDATE, state_pid, thread_port_caddr, soft_signal) != 0)
348        error.SetError(errno, eErrorTypePOSIX);
349
350      if (!error.Success()) {
351        if (log)
352          log->Printf("::ptrace(request = PT_THUPDATE, pid = "
353                      "0x%4.4x, tid = 0x%4.4x, signal = %i)",
354                      state_pid, state.thread_port, soft_signal);
355        return error;
356      }
357    }
358  }
359
360  if (log)
361    log->Printf("::mach_msg ( msg->{bits = %#x, size = %u, remote_port "
362                "= %#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, "
363                "option = %#x, send_size = %u, rcv_size = %u, rcv_name "
364                "= %#x, timeout = %u, notify = %#x)",
365                reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size,
366                reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port,
367                reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id,
368                MACH_SEND_MSG | MACH_SEND_INTERRUPT, reply_msg.hdr.msgh_size, 0,
369                MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
370
371  auto mach_err =
372      ::mach_msg(&reply_msg.hdr, MACH_SEND_MSG | MACH_SEND_INTERRUPT,
373                 reply_msg.hdr.msgh_size, 0, MACH_PORT_NULL,
374                 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
375  if (mach_err)
376    error.SetError(mach_err, eErrorTypeMachKernel);
377
378  // Log our error if we have one.
379  if (error.Fail() && log) {
380    if (error.GetError() == MACH_SEND_INTERRUPTED) {
381      log->PutCString("::mach_msg() - send interrupted");
382      // TODO: keep retrying to reply???
383    } else if (state.task_port == inferior_task) {
384      log->Printf("mach_msg(): returned an error when replying "
385                  "to a mach exception: error = %u (%s)",
386                  error.GetError(), error.AsCString());
387    } else {
388      log->Printf("::mach_msg() - failed (child of task): %u (%s)",
389                  error.GetError(), error.AsCString());
390    }
391  }
392
393  return error;
394}
395
396#define PREV_EXC_MASK_ALL                                                      \
397  (EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC |      \
398   EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT |              \
399   EXC_MASK_SYSCALL | EXC_MASK_MACH_SYSCALL | EXC_MASK_RPC_ALERT |             \
400   EXC_MASK_MACHINE)
401
402// Don't listen for EXC_RESOURCE, it should really get handled by the system
403// handler.
404
405#ifndef EXC_RESOURCE
406#define EXC_RESOURCE 11
407#endif
408
409#ifndef EXC_MASK_RESOURCE
410#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE)
411#endif
412
413#define LLDB_EXC_MASK (EXC_MASK_ALL & ~EXC_MASK_RESOURCE)
414
415Error MachException::PortInfo::Save(task_t task) {
416  Error error;
417  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
418
419  if (log)
420    log->Printf("MachException::PortInfo::%s(task = 0x%4.4x)", __FUNCTION__,
421                task);
422
423  // Be careful to be able to have debugserver built on a newer OS than what
424  // it is currently running on by being able to start with all exceptions
425  // and back off to just what is supported on the current system
426  mask = LLDB_EXC_MASK;
427
428  count = (sizeof(ports) / sizeof(ports[0]));
429  auto mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports,
430                                             behaviors, flavors);
431  if (mach_err)
432    error.SetError(mach_err, eErrorTypeMachKernel);
433
434  if (log) {
435    if (error.Success()) {
436      log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = "
437                  "0x%x, maskCnt => %u, ports, behaviors, flavors)",
438                  task, mask, count);
439    } else {
440      log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = 0x%x, "
441                  "maskCnt => %u, ports, behaviors, flavors) error: %u (%s)",
442                  task, mask, count, error.GetError(), error.AsCString());
443    }
444  }
445
446  if ((error.GetError() == KERN_INVALID_ARGUMENT) &&
447      (mask != PREV_EXC_MASK_ALL)) {
448    mask = PREV_EXC_MASK_ALL;
449    count = (sizeof(ports) / sizeof(ports[0]));
450    mach_err = ::task_get_exception_ports(task, mask, masks, &count, ports,
451                                          behaviors, flavors);
452    error.SetError(mach_err, eErrorTypeMachKernel);
453    if (log) {
454      if (error.Success()) {
455        log->Printf("::task_get_exception_ports(task = 0x%4.4x, "
456                    "mask = 0x%x, maskCnt => %u, ports, behaviors, "
457                    "flavors)",
458                    task, mask, count);
459      } else {
460        log->Printf("::task_get_exception_ports(task = 0x%4.4x, mask = "
461                    "0x%x, maskCnt => %u, ports, behaviors, flavors) "
462                    "error: %u (%s)",
463                    task, mask, count, error.GetError(), error.AsCString());
464      }
465    }
466  }
467  if (error.Fail()) {
468    mask = 0;
469    count = 0;
470  }
471  return error;
472}
473
474Error MachException::PortInfo::Restore(task_t task) {
475  Error error;
476
477  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
478
479  if (log)
480    log->Printf("MachException::PortInfo::Restore(task = 0x%4.4x)", task);
481
482  uint32_t i = 0;
483  if (count > 0) {
484    for (i = 0; i < count; i++) {
485      auto mach_err = ::task_set_exception_ports(task, masks[i], ports[i],
486                                                 behaviors[i], flavors[i]);
487      if (mach_err)
488        error.SetError(mach_err, eErrorTypeMachKernel);
489      if (log) {
490        if (error.Success()) {
491          log->Printf("::task_set_exception_ports(task = 0x%4.4x, "
492                      "exception_mask = 0x%8.8x, new_port = 0x%4.4x, "
493                      "behavior = 0x%8.8x, new_flavor = 0x%8.8x)",
494                      task, masks[i], ports[i], behaviors[i], flavors[i]);
495        } else {
496          log->Printf("::task_set_exception_ports(task = 0x%4.4x, "
497                      "exception_mask = 0x%8.8x, new_port = 0x%4.4x, "
498                      "behavior = 0x%8.8x, new_flavor = 0x%8.8x): "
499                      "error %u (%s)",
500                      task, masks[i], ports[i], behaviors[i], flavors[i],
501                      error.GetError(), error.AsCString());
502        }
503      }
504
505      // Bail if we encounter any errors
506      if (error.Fail())
507        break;
508    }
509  }
510
511  count = 0;
512  return error;
513}
514
515const char *MachException::Name(exception_type_t exc_type) {
516  switch (exc_type) {
517  case EXC_BAD_ACCESS:
518    return "EXC_BAD_ACCESS";
519  case EXC_BAD_INSTRUCTION:
520    return "EXC_BAD_INSTRUCTION";
521  case EXC_ARITHMETIC:
522    return "EXC_ARITHMETIC";
523  case EXC_EMULATION:
524    return "EXC_EMULATION";
525  case EXC_SOFTWARE:
526    return "EXC_SOFTWARE";
527  case EXC_BREAKPOINT:
528    return "EXC_BREAKPOINT";
529  case EXC_SYSCALL:
530    return "EXC_SYSCALL";
531  case EXC_MACH_SYSCALL:
532    return "EXC_MACH_SYSCALL";
533  case EXC_RPC_ALERT:
534    return "EXC_RPC_ALERT";
535#ifdef EXC_CRASH
536  case EXC_CRASH:
537    return "EXC_CRASH";
538#endif
539  default:
540    break;
541  }
542  return NULL;
543}
544