1 | //===-- MachException.cpp ---------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Created by Greg Clayton on 6/18/07. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "MachException.h" |
14 | #include "DNB.h" |
15 | #include "DNBError.h" |
16 | #include "DNBLog.h" |
17 | #include "MachProcess.h" |
18 | #include "PThreadMutex.h" |
19 | #include "SysSignal.h" |
20 | #include <cerrno> |
21 | #include <sys/ptrace.h> |
22 | #include <sys/types.h> |
23 | |
24 | // Routine mach_exception_raise |
25 | extern "C" kern_return_t |
26 | catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread, |
27 | mach_port_t task, exception_type_t exception, |
28 | mach_exception_data_t code, |
29 | mach_msg_type_number_t codeCnt); |
30 | |
31 | extern "C" kern_return_t catch_mach_exception_raise_state( |
32 | mach_port_t exception_port, exception_type_t exception, |
33 | const mach_exception_data_t code, mach_msg_type_number_t codeCnt, |
34 | int *flavor, const thread_state_t old_state, |
35 | mach_msg_type_number_t old_stateCnt, thread_state_t new_state, |
36 | mach_msg_type_number_t *new_stateCnt); |
37 | |
38 | // Routine mach_exception_raise_state_identity |
39 | extern "C" kern_return_t catch_mach_exception_raise_state_identity( |
40 | mach_port_t exception_port, mach_port_t thread, mach_port_t task, |
41 | exception_type_t exception, mach_exception_data_t code, |
42 | mach_msg_type_number_t codeCnt, int *flavor, thread_state_t old_state, |
43 | mach_msg_type_number_t old_stateCnt, thread_state_t new_state, |
44 | mach_msg_type_number_t *new_stateCnt); |
45 | |
46 | extern "C" boolean_t mach_exc_server(mach_msg_header_t *InHeadP, |
47 | mach_msg_header_t *OutHeadP); |
48 | |
49 | // Note: g_message points to the storage allocated to catch the data from |
50 | // catching the current exception raise. It's populated when we catch a raised |
51 | // exception which can't immediately be replied to. |
52 | // |
53 | // If it becomes possible to catch exceptions from multiple threads |
54 | // simultaneously, accesses to g_message would need to be mutually exclusive. |
55 | static MachException::Data *g_message = NULL; |
56 | |
57 | extern "C" kern_return_t catch_mach_exception_raise_state( |
58 | mach_port_t exc_port, exception_type_t exc_type, |
59 | const mach_exception_data_t exc_data, mach_msg_type_number_t exc_data_count, |
60 | int *flavor, const thread_state_t old_state, |
61 | mach_msg_type_number_t old_stateCnt, thread_state_t new_state, |
62 | mach_msg_type_number_t *new_stateCnt) { |
63 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { |
64 | DNBLogThreaded("::%s ( exc_port = 0x%4.4x, exc_type = %d ( %s ), exc_data " |
65 | "= 0x%llx, exc_data_count = %d)" , |
66 | __FUNCTION__, exc_port, exc_type, |
67 | MachException::Name(exc_type), (uint64_t)exc_data, |
68 | exc_data_count); |
69 | } |
70 | return KERN_FAILURE; |
71 | } |
72 | |
73 | extern "C" kern_return_t catch_mach_exception_raise_state_identity( |
74 | mach_port_t exc_port, mach_port_t thread_port, mach_port_t task_port, |
75 | exception_type_t exc_type, mach_exception_data_t exc_data, |
76 | mach_msg_type_number_t exc_data_count, int *flavor, |
77 | thread_state_t old_state, mach_msg_type_number_t old_stateCnt, |
78 | thread_state_t new_state, mach_msg_type_number_t *new_stateCnt) { |
79 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { |
80 | DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = " |
81 | "0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%llx, " |
82 | "0x%llx })" , |
83 | __FUNCTION__, exc_port, thread_port, task_port, exc_type, |
84 | MachException::Name(exc_type), exc_data_count, |
85 | (uint64_t)(exc_data_count > 0 ? exc_data[0] : 0xBADDBADD), |
86 | (uint64_t)(exc_data_count > 1 ? exc_data[1] : 0xBADDBADD)); |
87 | } |
88 | |
89 | return KERN_FAILURE; |
90 | } |
91 | |
92 | extern "C" kern_return_t |
93 | catch_mach_exception_raise(mach_port_t exc_port, mach_port_t thread_port, |
94 | mach_port_t task_port, exception_type_t exc_type, |
95 | mach_exception_data_t exc_data, |
96 | mach_msg_type_number_t exc_data_count) { |
97 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { |
98 | std::vector<uint64_t> exc_datas; |
99 | uint64_t tmp; |
100 | for (unsigned i = 0; i < exc_data_count; ++i) { |
101 | // Perform an unaligned copy. |
102 | memcpy(&tmp, &exc_data[i], sizeof(uint64_t)); |
103 | exc_datas.push_back(x: tmp); |
104 | } |
105 | DNBLogThreaded("::%s ( exc_port = 0x%4.4x, thd_port = 0x%4.4x, tsk_port = " |
106 | "0x%4.4x, exc_type = %d ( %s ), exc_data[%d] = { 0x%llx, " |
107 | "0x%llx })" , |
108 | __FUNCTION__, exc_port, thread_port, task_port, exc_type, |
109 | MachException::Name(exc_type), exc_data_count, |
110 | (uint64_t)(exc_data_count > 0 ? exc_datas[0] : 0xBADDBADD), |
111 | (uint64_t)(exc_data_count > 1 ? exc_datas[1] : 0xBADDBADD)); |
112 | } |
113 | g_message->exc_type = 0; |
114 | g_message->exc_data.clear(); |
115 | |
116 | if (task_port == g_message->task_port) { |
117 | g_message->task_port = task_port; |
118 | g_message->thread_port = thread_port; |
119 | g_message->exc_type = exc_type; |
120 | g_message->AppendExceptionData(exc_data, exc_data_count); |
121 | return KERN_SUCCESS; |
122 | } else if (!MachTask::IsValid(g_message->task_port)) { |
123 | // Our original exception port isn't valid anymore check for a SIGTRAP |
124 | if (exc_type == EXC_SOFTWARE && exc_data_count == 2 && |
125 | exc_data[0] == EXC_SOFT_SIGNAL && exc_data[1] == SIGTRAP) { |
126 | // We got a SIGTRAP which indicates we might have exec'ed and possibly |
127 | // lost our old task port during the exec, so we just need to switch over |
128 | // to using this new task port |
129 | g_message->task_port = task_port; |
130 | g_message->thread_port = thread_port; |
131 | g_message->exc_type = exc_type; |
132 | g_message->AppendExceptionData(exc_data, exc_data_count); |
133 | return KERN_SUCCESS; |
134 | } |
135 | } |
136 | return KERN_FAILURE; |
137 | } |
138 | |
139 | void MachException::Message::Dump() const { |
140 | DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_msg { bits = 0x%8.8x size = 0x%8.8x " |
141 | "remote-port = 0x%8.8x local-port = 0x%8.8x " |
142 | "reserved = 0x%8.8x id = 0x%8.8x } " , |
143 | exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, |
144 | exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, |
145 | exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id); |
146 | |
147 | DNBLogThreadedIf(LOG_EXCEPTIONS, "reply_msg { bits = 0x%8.8x size = 0x%8.8x " |
148 | "remote-port = 0x%8.8x local-port = 0x%8.8x " |
149 | "reserved = 0x%8.8x id = 0x%8.8x }" , |
150 | reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size, |
151 | reply_msg.hdr.msgh_remote_port, |
152 | reply_msg.hdr.msgh_local_port, reply_msg.hdr.msgh_reserved, |
153 | reply_msg.hdr.msgh_id); |
154 | |
155 | state.Dump(); |
156 | } |
157 | |
158 | bool MachException::Data::GetStopInfo( |
159 | struct DNBThreadStopInfo *stop_info) const { |
160 | // Zero out the structure. |
161 | memset(stop_info, 0, sizeof(struct DNBThreadStopInfo)); |
162 | |
163 | if (exc_type == 0) { |
164 | stop_info->reason = eStopTypeInvalid; |
165 | return true; |
166 | } |
167 | |
168 | #if defined(__arm64__) || defined(__aarch64__) |
169 | if (exc_type == EXC_BREAKPOINT && exc_data[0] == EXC_ARM_DA_DEBUG && |
170 | exc_data.size() > 1) { |
171 | stop_info->reason = eStopTypeWatchpoint; |
172 | stop_info->details.watchpoint.mach_exception_addr = exc_data[1]; |
173 | stop_info->details.watchpoint.addr = INVALID_NUB_ADDRESS; |
174 | if (exc_data.size() > 2) { |
175 | stop_info->details.watchpoint.hw_idx = exc_data[2]; |
176 | } |
177 | return true; |
178 | } |
179 | #endif |
180 | |
181 | // We always stop with a mach exceptions |
182 | stop_info->reason = eStopTypeException; |
183 | // Save the EXC_XXXX exception type |
184 | stop_info->details.exception.type = exc_type; |
185 | |
186 | // Fill in a text description |
187 | const char *exc_name = MachException::Name(exc_type); |
188 | char *desc = stop_info->description; |
189 | const char *end_desc = desc + DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH; |
190 | if (exc_name) |
191 | desc += |
192 | snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%s" , exc_name); |
193 | else |
194 | desc += |
195 | snprintf(desc, DNB_THREAD_STOP_INFO_MAX_DESC_LENGTH, "%i" , exc_type); |
196 | |
197 | stop_info->details.exception.data_count = exc_data.size(); |
198 | |
199 | int soft_signal = SoftSignal(); |
200 | if (soft_signal) { |
201 | if (desc < end_desc) { |
202 | const char *sig_str = SysSignal::Name(soft_signal); |
203 | snprintf(s: desc, maxlen: end_desc - desc, format: " EXC_SOFT_SIGNAL( %i ( %s ))" , |
204 | soft_signal, sig_str ? sig_str : "unknown signal" ); |
205 | } |
206 | } else { |
207 | // No special disassembly for exception data, just |
208 | size_t idx; |
209 | if (desc < end_desc) { |
210 | desc += snprintf(s: desc, maxlen: end_desc - desc, format: " data[%llu] = {" , |
211 | (uint64_t)stop_info->details.exception.data_count); |
212 | |
213 | for (idx = 0; |
214 | desc < end_desc && idx < stop_info->details.exception.data_count; |
215 | ++idx) |
216 | desc += snprintf( |
217 | desc, end_desc - desc, "0x%llx%c" , (uint64_t)exc_data[idx], |
218 | ((idx + 1 == stop_info->details.exception.data_count) ? '}' : ',')); |
219 | } |
220 | } |
221 | |
222 | // Copy the exception data |
223 | size_t i; |
224 | for (i = 0; i < stop_info->details.exception.data_count; i++) |
225 | stop_info->details.exception.data[i] = exc_data[i]; |
226 | |
227 | return true; |
228 | } |
229 | |
230 | void MachException::Data::DumpStopReason() const { |
231 | int soft_signal = SoftSignal(); |
232 | if (soft_signal) { |
233 | const char *signal_str = SysSignal::Name(soft_signal); |
234 | if (signal_str) |
235 | DNBLog("signal(%s)" , signal_str); |
236 | else |
237 | DNBLog("signal(%i)" , soft_signal); |
238 | return; |
239 | } |
240 | DNBLog("%s" , Name(exc_type)); |
241 | } |
242 | |
243 | kern_return_t MachException::Message::Receive(mach_port_t port, |
244 | mach_msg_option_t options, |
245 | mach_msg_timeout_t timeout, |
246 | mach_port_t notify_port) { |
247 | DNBError err; |
248 | const bool log_exceptions = DNBLogCheckLogBit(LOG_EXCEPTIONS); |
249 | mach_msg_timeout_t mach_msg_timeout = |
250 | options & MACH_RCV_TIMEOUT ? timeout : 0; |
251 | if (log_exceptions && ((options & MACH_RCV_TIMEOUT) == 0)) { |
252 | // Dump this log message if we have no timeout in case it never returns |
253 | DNBLogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = " |
254 | "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, option " |
255 | "= %#x, send_size = 0, rcv_size = %llu, rcv_name = %#x, " |
256 | "timeout = %u, notify = %#x)" , |
257 | exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, |
258 | exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, |
259 | exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, |
260 | (uint64_t)sizeof(exc_msg.data), port, mach_msg_timeout, |
261 | notify_port); |
262 | } |
263 | |
264 | err = ::mach_msg(&exc_msg.hdr, |
265 | options, // options |
266 | 0, // Send size |
267 | sizeof(exc_msg.data), // Receive size |
268 | port, // exception port to watch for exception on |
269 | mach_msg_timeout, // timeout in msec (obeyed only if |
270 | // MACH_RCV_TIMEOUT is ORed into the |
271 | // options parameter) |
272 | notify_port); |
273 | |
274 | // Dump any errors we get |
275 | if (log_exceptions) { |
276 | err.LogThreaded("::mach_msg ( msg->{bits = %#x, size = %u remote_port = " |
277 | "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, " |
278 | "option = %#x, send_size = %u, rcv_size = %u, rcv_name = " |
279 | "%#x, timeout = %u, notify = %#x)" , |
280 | exc_msg.hdr.msgh_bits, exc_msg.hdr.msgh_size, |
281 | exc_msg.hdr.msgh_remote_port, exc_msg.hdr.msgh_local_port, |
282 | exc_msg.hdr.msgh_reserved, exc_msg.hdr.msgh_id, options, 0, |
283 | sizeof(exc_msg.data), port, mach_msg_timeout, notify_port); |
284 | } |
285 | return err.Status(); |
286 | } |
287 | |
288 | bool MachException::Message::CatchExceptionRaise(task_t task) { |
289 | bool success = false; |
290 | state.task_port = task; |
291 | g_message = &state; |
292 | // The exc_server function is the MIG generated server handling function |
293 | // to handle messages from the kernel relating to the occurrence of an |
294 | // exception in a thread. Such messages are delivered to the exception port |
295 | // set via thread_set_exception_ports or task_set_exception_ports. When an |
296 | // exception occurs in a thread, the thread sends an exception message to |
297 | // its exception port, blocking in the kernel waiting for the receipt of a |
298 | // reply. The exc_server function performs all necessary argument handling |
299 | // for this kernel message and calls catch_exception_raise, |
300 | // catch_exception_raise_state or catch_exception_raise_state_identity, |
301 | // which should handle the exception. If the called routine returns |
302 | // KERN_SUCCESS, a reply message will be sent, allowing the thread to |
303 | // continue from the point of the exception; otherwise, no reply message |
304 | // is sent and the called routine must have dealt with the exception |
305 | // thread directly. |
306 | if (mach_exc_server(&exc_msg.hdr, &reply_msg.hdr)) { |
307 | success = true; |
308 | } else if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) { |
309 | DNBLogThreaded("mach_exc_server returned zero..." ); |
310 | } |
311 | g_message = NULL; |
312 | return success; |
313 | } |
314 | |
315 | kern_return_t MachException::Message::Reply(MachProcess *process, int signal) { |
316 | // Reply to the exception... |
317 | DNBError err; |
318 | |
319 | // If we had a soft signal, we need to update the thread first so it can |
320 | // continue without signaling |
321 | int soft_signal = state.SoftSignal(); |
322 | if (soft_signal) { |
323 | int state_pid = -1; |
324 | if (process->Task().TaskPort() == state.task_port) { |
325 | // This is our task, so we can update the signal to send to it |
326 | state_pid = process->ProcessID(); |
327 | soft_signal = signal; |
328 | } else { |
329 | err = ::pid_for_task(state.task_port, &state_pid); |
330 | } |
331 | |
332 | assert(state_pid != -1); |
333 | if (state_pid != -1) { |
334 | errno = 0; |
335 | if (::ptrace(PT_THUPDATE, state_pid, |
336 | (caddr_t)((uintptr_t)state.thread_port), soft_signal) != 0) |
337 | err.SetError(errno, DNBError::POSIX); |
338 | else |
339 | err.Clear(); |
340 | |
341 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) |
342 | err.LogThreaded("::ptrace (request = PT_THUPDATE, pid = 0x%4.4x, tid = " |
343 | "0x%4.4x, signal = %i)" , |
344 | state_pid, state.thread_port, soft_signal); |
345 | } |
346 | } |
347 | |
348 | DNBLogThreadedIf( |
349 | LOG_EXCEPTIONS, "::mach_msg ( msg->{bits = %#x, size = %u, remote_port = " |
350 | "%#x, local_port = %#x, reserved = 0x%x, id = 0x%x}, " |
351 | "option = %#x, send_size = %u, rcv_size = %u, rcv_name = " |
352 | "%#x, timeout = %u, notify = %#x)" , |
353 | reply_msg.hdr.msgh_bits, reply_msg.hdr.msgh_size, |
354 | reply_msg.hdr.msgh_remote_port, reply_msg.hdr.msgh_local_port, |
355 | reply_msg.hdr.msgh_reserved, reply_msg.hdr.msgh_id, |
356 | MACH_SEND_MSG | MACH_SEND_INTERRUPT, reply_msg.hdr.msgh_size, 0, |
357 | MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
358 | |
359 | err = ::mach_msg(&reply_msg.hdr, MACH_SEND_MSG | MACH_SEND_INTERRUPT, |
360 | reply_msg.hdr.msgh_size, 0, MACH_PORT_NULL, |
361 | MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
362 | |
363 | if (err.Fail()) { |
364 | if (err.Status() == MACH_SEND_INTERRUPTED) { |
365 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) |
366 | err.LogThreaded("::mach_msg() - send interrupted" ); |
367 | // TODO: keep retrying to reply??? |
368 | } else { |
369 | if (state.task_port == process->Task().TaskPort()) { |
370 | DNBLogThreaded("error: mach_msg() returned an error when replying to a " |
371 | "mach exception: error = %u" , |
372 | err.Status()); |
373 | } else { |
374 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS)) |
375 | err.LogThreaded("::mach_msg() - failed (child of task)" ); |
376 | } |
377 | } |
378 | } |
379 | |
380 | return err.Status(); |
381 | } |
382 | |
383 | void MachException::Data::Dump() const { |
384 | const char *exc_type_name = MachException::Name(exc_type); |
385 | DNBLogThreadedIf( |
386 | LOG_EXCEPTIONS, " state { task_port = 0x%4.4x, thread_port = " |
387 | "0x%4.4x, exc_type = %i (%s) ..." , |
388 | task_port, thread_port, exc_type, exc_type_name ? exc_type_name : "???" ); |
389 | |
390 | const size_t exc_data_count = exc_data.size(); |
391 | // Dump any special exception data contents |
392 | int soft_signal = SoftSignal(); |
393 | if (soft_signal != 0) { |
394 | const char *sig_str = SysSignal::Name(soft_signal); |
395 | DNBLogThreadedIf(LOG_EXCEPTIONS, |
396 | " exc_data: EXC_SOFT_SIGNAL (%i (%s))" , |
397 | soft_signal, sig_str ? sig_str : "unknown signal" ); |
398 | } else { |
399 | // No special disassembly for this data, just dump the data |
400 | size_t idx; |
401 | for (idx = 0; idx < exc_data_count; ++idx) { |
402 | DNBLogThreadedIf(LOG_EXCEPTIONS, " exc_data[%llu]: 0x%llx" , |
403 | (uint64_t)idx, (uint64_t)exc_data[idx]); |
404 | } |
405 | } |
406 | } |
407 | |
408 | // The EXC_MASK_ALL value hard-coded here so that lldb can be built |
409 | // on a new OS with an older deployment target . The new OS may have |
410 | // an addition to its EXC_MASK_ALL that the old OS will not recognize - |
411 | // <mach/exception_types.h> doesn't vary the value based on the deployment |
412 | // target. So we need a known set of masks that can be assumed to be |
413 | // valid when running on an older OS. We'll fall back to trying |
414 | // PREV_EXC_MASK_ALL if the EXC_MASK_ALL value lldb was compiled with is |
415 | // not recognized. |
416 | |
417 | #define PREV_EXC_MASK_ALL (EXC_MASK_BAD_ACCESS | \ |
418 | EXC_MASK_BAD_INSTRUCTION | \ |
419 | EXC_MASK_ARITHMETIC | \ |
420 | EXC_MASK_EMULATION | \ |
421 | EXC_MASK_SOFTWARE | \ |
422 | EXC_MASK_BREAKPOINT | \ |
423 | EXC_MASK_SYSCALL | \ |
424 | EXC_MASK_MACH_SYSCALL | \ |
425 | EXC_MASK_RPC_ALERT | \ |
426 | EXC_MASK_RESOURCE | \ |
427 | EXC_MASK_GUARD | \ |
428 | EXC_MASK_MACHINE) |
429 | |
430 | #define LLDB_EXC_MASK EXC_MASK_ALL |
431 | |
432 | kern_return_t MachException::PortInfo::Save(task_t task) { |
433 | DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, |
434 | "MachException::PortInfo::Save ( task = 0x%4.4x )" , task); |
435 | // Be careful to be able to have debugserver built on a newer OS than what |
436 | // it is currently running on by being able to start with all exceptions |
437 | // and back off to just what is supported on the current system |
438 | DNBError err; |
439 | |
440 | mask = LLDB_EXC_MASK; |
441 | |
442 | count = (sizeof(ports) / sizeof(ports[0])); |
443 | err = ::task_get_exception_ports(task, mask, masks, &count, ports, behaviors, |
444 | flavors); |
445 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) |
446 | err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = 0x%x, " |
447 | "maskCnt => %u, ports, behaviors, flavors )" , |
448 | task, mask, count); |
449 | |
450 | if (err.Status() == KERN_INVALID_ARGUMENT && mask != PREV_EXC_MASK_ALL) { |
451 | mask = PREV_EXC_MASK_ALL; |
452 | count = (sizeof(ports) / sizeof(ports[0])); |
453 | err = ::task_get_exception_ports(task, mask, masks, &count, ports, |
454 | behaviors, flavors); |
455 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) |
456 | err.LogThreaded("::task_get_exception_ports ( task = 0x%4.4x, mask = " |
457 | "0x%x, maskCnt => %u, ports, behaviors, flavors )" , |
458 | task, mask, count); |
459 | } |
460 | if (err.Fail()) { |
461 | mask = 0; |
462 | count = 0; |
463 | } |
464 | return err.Status(); |
465 | } |
466 | |
467 | kern_return_t MachException::PortInfo::Restore(task_t task) { |
468 | DNBLogThreadedIf(LOG_EXCEPTIONS | LOG_VERBOSE, |
469 | "MachException::PortInfo::Restore( task = 0x%4.4x )" , task); |
470 | uint32_t i = 0; |
471 | DNBError err; |
472 | if (count > 0) { |
473 | for (i = 0; i < count; i++) { |
474 | err = ::task_set_exception_ports(task, masks[i], ports[i], behaviors[i], |
475 | flavors[i]); |
476 | if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) { |
477 | err.LogThreaded("::task_set_exception_ports ( task = 0x%4.4x, " |
478 | "exception_mask = 0x%8.8x, new_port = 0x%4.4x, " |
479 | "behavior = 0x%8.8x, new_flavor = 0x%8.8x )" , |
480 | task, masks[i], ports[i], behaviors[i], flavors[i]); |
481 | // Bail if we encounter any errors |
482 | } |
483 | |
484 | if (err.Fail()) |
485 | break; |
486 | } |
487 | } |
488 | count = 0; |
489 | return err.Status(); |
490 | } |
491 | |
492 | const char *MachException::Name(exception_type_t exc_type) { |
493 | switch (exc_type) { |
494 | case EXC_BAD_ACCESS: |
495 | return "EXC_BAD_ACCESS" ; |
496 | case EXC_BAD_INSTRUCTION: |
497 | return "EXC_BAD_INSTRUCTION" ; |
498 | case EXC_ARITHMETIC: |
499 | return "EXC_ARITHMETIC" ; |
500 | case EXC_EMULATION: |
501 | return "EXC_EMULATION" ; |
502 | case EXC_SOFTWARE: |
503 | return "EXC_SOFTWARE" ; |
504 | case EXC_BREAKPOINT: |
505 | return "EXC_BREAKPOINT" ; |
506 | case EXC_SYSCALL: |
507 | return "EXC_SYSCALL" ; |
508 | case EXC_MACH_SYSCALL: |
509 | return "EXC_MACH_SYSCALL" ; |
510 | case EXC_RPC_ALERT: |
511 | return "EXC_RPC_ALERT" ; |
512 | #ifdef EXC_CRASH |
513 | case EXC_CRASH: |
514 | return "EXC_CRASH" ; |
515 | #endif |
516 | case EXC_RESOURCE: |
517 | return "EXC_RESOURCE" ; |
518 | #ifdef EXC_GUARD |
519 | case EXC_GUARD: |
520 | return "EXC_GUARD" ; |
521 | #endif |
522 | #ifdef EXC_CORPSE_NOTIFY |
523 | case EXC_CORPSE_NOTIFY: |
524 | return "EXC_CORPSE_NOTIFY" ; |
525 | #endif |
526 | #ifdef EXC_CORPSE_VARIANT_BIT |
527 | case EXC_CORPSE_VARIANT_BIT: |
528 | return "EXC_CORPSE_VARIANT_BIT" ; |
529 | #endif |
530 | default: |
531 | break; |
532 | } |
533 | return NULL; |
534 | } |
535 | |
536 | // Returns the exception mask for a given exception name. |
537 | // 0 is not a legit mask, so we return that in the case of an error. |
538 | exception_mask_t MachException::ExceptionMask(const char *name) { |
539 | static const char *exception_prefix = "EXC_" ; |
540 | static const int prefix_len = strlen(exception_prefix); |
541 | |
542 | // All mach exceptions start with this prefix: |
543 | if (strstr(name, exception_prefix) != name) |
544 | return 0; |
545 | |
546 | name += prefix_len; |
547 | std::string name_str = name; |
548 | if (name_str == "BAD_ACCESS" ) |
549 | return EXC_MASK_BAD_ACCESS; |
550 | if (name_str == "BAD_INSTRUCTION" ) |
551 | return EXC_MASK_BAD_INSTRUCTION; |
552 | if (name_str == "ARITHMETIC" ) |
553 | return EXC_MASK_ARITHMETIC; |
554 | if (name_str == "EMULATION" ) |
555 | return EXC_MASK_EMULATION; |
556 | if (name_str == "SOFTWARE" ) |
557 | return EXC_MASK_SOFTWARE; |
558 | if (name_str == "BREAKPOINT" ) |
559 | return EXC_MASK_BREAKPOINT; |
560 | if (name_str == "SYSCALL" ) |
561 | return EXC_MASK_SYSCALL; |
562 | if (name_str == "MACH_SYSCALL" ) |
563 | return EXC_MASK_MACH_SYSCALL; |
564 | if (name_str == "RPC_ALERT" ) |
565 | return EXC_MASK_RPC_ALERT; |
566 | #ifdef EXC_CRASH |
567 | if (name_str == "CRASH" ) |
568 | return EXC_MASK_CRASH; |
569 | #endif |
570 | if (name_str == "RESOURCE" ) |
571 | return EXC_MASK_RESOURCE; |
572 | #ifdef EXC_GUARD |
573 | if (name_str == "GUARD" ) |
574 | return EXC_MASK_GUARD; |
575 | #endif |
576 | #ifdef EXC_CORPSE_NOTIFY |
577 | if (name_str == "CORPSE_NOTIFY" ) |
578 | return EXC_MASK_CORPSE_NOTIFY; |
579 | #endif |
580 | return 0; |
581 | } |
582 | |