1 |
/* CoffeeCatch, a tiny native signal handler/catcher for JNI code.
|
2 |
* (especially for Android/Dalvik)
|
3 |
*
|
4 |
* Copyright (c) 2013, Xavier Roche (http://www.httrack.com/)
|
5 |
* All rights reserved.
|
6 |
*
|
7 |
* Redistribution and use in source and binary forms, with or without
|
8 |
* modification, are permitted provided that the following conditions are met:
|
9 |
*
|
10 |
* 1. Redistributions of source code must retain the above copyright notice, this
|
11 |
* list of conditions and the following disclaimer.
|
12 |
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
13 |
* this list of conditions and the following disclaimer in the documentation
|
14 |
* and/or other materials provided with the distribution.
|
15 |
*
|
16 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
17 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18 |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19 |
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
20 |
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
21 |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
22 |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
23 |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
24 |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
25 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26 |
*/
|
27 |
|
28 |
/*
|
29 |
*
|
30 |
* from revision:
|
31 |
* https://github.com/xroche/coffeecatch/blob/e64ca5a1be03795baecb3242f37a80da2f138ff1/coffeecatch.c
|
32 |
*
|
33 |
*/
|
34 |
|
35 |
#ifdef __ANDROID__
|
36 |
#define USE_UNWIND
|
37 |
#define USE_CORKSCREW
|
38 |
#define USE_LIBUNWIND
|
39 |
#endif
|
40 |
|
41 |
/* #undef NO_USE_SIGALTSTACK */
|
42 |
/* #undef USE_SILENT_SIGALTSTACK */
|
43 |
|
44 |
#include <stdio.h>
|
45 |
#include <stdlib.h>
|
46 |
#include <stdint.h>
|
47 |
#include <unistd.h>
|
48 |
#include <string.h>
|
49 |
#include <errno.h>
|
50 |
#include <sys/types.h>
|
51 |
#include <assert.h>
|
52 |
#include <signal.h>
|
53 |
#include <setjmp.h>
|
54 |
#if defined(__ANDROID__) && !defined(__BIONIC_HAVE_UCONTEXT_T) && \
|
55 |
defined(__arm__) && !defined(__BIONIC_HAVE_STRUCT_SIGCONTEXT)
|
56 |
#include <asm/sigcontext.h>
|
57 |
#endif
|
58 |
#if (defined(USE_UNWIND) && !defined(USE_CORKSCREW))
|
59 |
#include <unwind.h>
|
60 |
#endif
|
61 |
#include <pthread.h>
|
62 |
#include <dlfcn.h>
|
63 |
#include "coffeecatch.h"
|
64 |
|
65 |
/*#define NDK_DEBUG 1*/
|
66 |
#if ( defined(NDK_DEBUG) && ( NDK_DEBUG == 1 ) )
|
67 |
#define DEBUG(A) do { A; } while(0)
|
68 |
#define FD_ERRNO 2
|
69 |
static void print(const char *const s) {
|
70 |
size_t count;
|
71 |
for(count = 0; s[count] != '\0'; count++) ;
|
72 |
/* write() is async-signal-safe. */
|
73 |
(void) write(FD_ERRNO, s, count);
|
74 |
}
|
75 |
#else
|
76 |
#define DEBUG(A)
|
77 |
#endif
|
78 |
|
79 |
/* Alternative stack size. */
|
80 |
#define SIG_STACK_BUFFER_SIZE SIGSTKSZ
|
81 |
|
82 |
#ifdef USE_UNWIND
|
83 |
/* Number of backtraces to get. */
|
84 |
#define BACKTRACE_FRAMES_MAX 32
|
85 |
#endif
|
86 |
|
87 |
/* Signals to be caught. */
|
88 |
#define SIG_CATCH_COUNT 7
|
89 |
static const int native_sig_catch[SIG_CATCH_COUNT + 1]
|
90 |
= { SIGABRT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV
|
91 |
#ifdef SIGSTKFLT
|
92 |
, SIGSTKFLT
|
93 |
#endif
|
94 |
, 0 };
|
95 |
|
96 |
/* Maximum value of a caught signal. */
|
97 |
#define SIG_NUMBER_MAX 32
|
98 |
|
99 |
#if defined(__ANDROID__)
|
100 |
#ifndef ucontext_h_seen
|
101 |
#define ucontext_h_seen
|
102 |
|
103 |
/* stack_t definition */
|
104 |
#include <asm/signal.h>
|
105 |
|
106 |
#if defined(__arm__)
|
107 |
|
108 |
/* Taken from richard.quirk's header file. (Android does not have it) */
|
109 |
|
110 |
#if !defined(__BIONIC_HAVE_UCONTEXT_T)
|
111 |
typedef struct ucontext {
|
112 |
unsigned long uc_flags;
|
113 |
struct ucontext *uc_link;
|
114 |
stack_t uc_stack;
|
115 |
struct sigcontext uc_mcontext;
|
116 |
unsigned long uc_sigmask;
|
117 |
} ucontext_t;
|
118 |
#endif
|
119 |
|
120 |
#elif defined(__aarch64__)
|
121 |
|
122 |
#elif defined(__i386__)
|
123 |
|
124 |
/* Taken from Google Breakpad. */
|
125 |
|
126 |
|
127 |
|
128 |
// --- Zoff -----------------------------
|
129 |
// --- Zoff -----------------------------
|
130 |
#if 0
|
131 |
|
132 |
/* 80-bit floating-point register */
|
133 |
struct _libc_fpreg {
|
134 |
unsigned short significand[4];
|
135 |
unsigned short exponent;
|
136 |
};
|
137 |
|
138 |
/* Simple floating-point state, see FNSTENV instruction */
|
139 |
struct _libc_fpstate {
|
140 |
unsigned long cw;
|
141 |
unsigned long sw;
|
142 |
unsigned long tag;
|
143 |
unsigned long ipoff;
|
144 |
unsigned long cssel;
|
145 |
unsigned long dataoff;
|
146 |
unsigned long datasel;
|
147 |
struct _libc_fpreg _st[8];
|
148 |
unsigned long status;
|
149 |
};
|
150 |
|
151 |
typedef uint32_t greg_t;
|
152 |
|
153 |
typedef struct {
|
154 |
uint32_t gregs[19];
|
155 |
struct _libc_fpstate* fpregs;
|
156 |
uint32_t oldmask;
|
157 |
uint32_t cr2;
|
158 |
} mcontext_t;
|
159 |
|
160 |
enum {
|
161 |
REG_GS = 0,
|
162 |
REG_FS,
|
163 |
REG_ES,
|
164 |
REG_DS,
|
165 |
REG_EDI,
|
166 |
REG_ESI,
|
167 |
REG_EBP,
|
168 |
REG_ESP,
|
169 |
REG_EBX,
|
170 |
REG_EDX,
|
171 |
REG_ECX,
|
172 |
REG_EAX,
|
173 |
REG_TRAPNO,
|
174 |
REG_ERR,
|
175 |
REG_EIP,
|
176 |
REG_CS,
|
177 |
REG_EFL,
|
178 |
REG_UESP,
|
179 |
REG_SS,
|
180 |
};
|
181 |
|
182 |
#endif
|
183 |
// --- Zoff -----------------------------
|
184 |
// --- Zoff -----------------------------
|
185 |
|
186 |
|
187 |
#if !defined(__BIONIC_HAVE_UCONTEXT_T)
|
188 |
typedef struct ucontext {
|
189 |
uint32_t uc_flags;
|
190 |
struct ucontext* uc_link;
|
191 |
stack_t uc_stack;
|
192 |
mcontext_t uc_mcontext;
|
193 |
} ucontext_t;
|
194 |
#endif
|
195 |
|
196 |
#elif defined(__mips__)
|
197 |
|
198 |
/* Taken from Google Breakpad. */
|
199 |
|
200 |
|
201 |
|
202 |
|
203 |
// TODO: android studio ----------
|
204 |
// TODO: android studio ----------
|
205 |
#if 0
|
206 |
typedef struct {
|
207 |
uint32_t regmask;
|
208 |
uint32_t status;
|
209 |
uint64_t pc;
|
210 |
uint64_t gregs[32];
|
211 |
uint64_t fpregs[32];
|
212 |
uint32_t acx;
|
213 |
uint32_t fpc_csr;
|
214 |
uint32_t fpc_eir;
|
215 |
uint32_t used_math;
|
216 |
uint32_t dsp;
|
217 |
uint64_t mdhi;
|
218 |
uint64_t mdlo;
|
219 |
uint32_t hi1;
|
220 |
uint32_t lo1;
|
221 |
uint32_t hi2;
|
222 |
uint32_t lo2;
|
223 |
uint32_t hi3;
|
224 |
uint32_t lo3;
|
225 |
} mcontext_t;
|
226 |
#endif
|
227 |
// TODO: android studio ----------
|
228 |
// TODO: android studio ----------
|
229 |
|
230 |
|
231 |
|
232 |
// TODO: android studio ----------
|
233 |
// TODO: android studio ----------
|
234 |
#if 0
|
235 |
#if defined(__i386__)
|
236 |
#include <setjmp.h>
|
237 |
void siglongjmp(jmp_buf env, int val);
|
238 |
int sigsetjmp(jmp_buf env, int savemask);
|
239 |
void siglongjmp(jmp_buf env, int val)
|
240 |
{
|
241 |
longjmp(env, val);
|
242 |
}
|
243 |
int sigsetjmp(jmp_buf env, int savemask)
|
244 |
{
|
245 |
return setjmp(env);
|
246 |
}
|
247 |
#endif
|
248 |
#endif
|
249 |
// TODO: android studio ----------
|
250 |
// TODO: android studio ----------
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
#if !defined(__BIONIC_HAVE_UCONTEXT_T)
|
256 |
typedef struct ucontext {
|
257 |
uint32_t uc_flags;
|
258 |
struct ucontext* uc_link;
|
259 |
stack_t uc_stack;
|
260 |
mcontext_t uc_mcontext;
|
261 |
} ucontext_t;
|
262 |
#endif
|
263 |
|
264 |
#else
|
265 |
#error "Architecture is not supported (unknown ucontext layout)"
|
266 |
#endif
|
267 |
|
268 |
#endif
|
269 |
|
270 |
#ifdef USE_CORKSCREW
|
271 |
typedef struct map_info_t map_info_t;
|
272 |
/* Extracted from Android's include/corkscrew/backtrace.h */
|
273 |
typedef struct {
|
274 |
uintptr_t absolute_pc;
|
275 |
uintptr_t stack_top;
|
276 |
size_t stack_size;
|
277 |
} backtrace_frame_t;
|
278 |
typedef struct {
|
279 |
uintptr_t relative_pc;
|
280 |
uintptr_t relative_symbol_addr;
|
281 |
char* map_name;
|
282 |
char* symbol_name;
|
283 |
char* demangled_name;
|
284 |
} backtrace_symbol_t;
|
285 |
/* Extracted from Android's libcorkscrew/arch-arm/backtrace-arm.c */
|
286 |
typedef ssize_t (*t_unwind_backtrace_signal_arch)
|
287 |
(siginfo_t* si, void* sc, const map_info_t* lst, backtrace_frame_t* bt,
|
288 |
size_t ignore_depth, size_t max_depth);
|
289 |
typedef map_info_t* (*t_acquire_my_map_info_list)();
|
290 |
typedef void (*t_release_my_map_info_list)(map_info_t* milist);
|
291 |
typedef void (*t_get_backtrace_symbols)(const backtrace_frame_t* backtrace,
|
292 |
size_t frames,
|
293 |
backtrace_symbol_t* symbols);
|
294 |
typedef void (*t_free_backtrace_symbols)(backtrace_symbol_t* symbols,
|
295 |
size_t frames);
|
296 |
#endif
|
297 |
|
298 |
#endif
|
299 |
|
300 |
/* Process-wide crash handler structure. */
|
301 |
typedef struct native_code_global_struct {
|
302 |
/* Initialized. */
|
303 |
int initialized;
|
304 |
|
305 |
/* Lock. */
|
306 |
pthread_mutex_t mutex;
|
307 |
|
308 |
/* Backup of sigaction. */
|
309 |
struct sigaction *sa_old;
|
310 |
} native_code_global_struct;
|
311 |
#define NATIVE_CODE_GLOBAL_INITIALIZER { 0, PTHREAD_MUTEX_INITIALIZER, NULL }
|
312 |
|
313 |
/* Thread-specific crash handler structure. */
|
314 |
typedef struct native_code_handler_struct {
|
315 |
/* Restore point context. */
|
316 |
sigjmp_buf ctx;
|
317 |
int ctx_is_set;
|
318 |
int reenter;
|
319 |
|
320 |
/* Alternate stack. */
|
321 |
char *stack_buffer;
|
322 |
size_t stack_buffer_size;
|
323 |
stack_t stack_old;
|
324 |
|
325 |
/* Signal code and info. */
|
326 |
int code;
|
327 |
siginfo_t si;
|
328 |
ucontext_t uc;
|
329 |
|
330 |
/* Uwind context. */
|
331 |
#if (defined(USE_CORKSCREW))
|
332 |
backtrace_frame_t frames[BACKTRACE_FRAMES_MAX];
|
333 |
#elif (defined(USE_UNWIND))
|
334 |
uintptr_t frames[BACKTRACE_FRAMES_MAX];
|
335 |
#endif
|
336 |
#ifdef USE_LIBUNWIND
|
337 |
void* uframes[BACKTRACE_FRAMES_MAX];
|
338 |
#endif
|
339 |
size_t frames_size;
|
340 |
size_t frames_skip;
|
341 |
|
342 |
/* Custom assertion failures. */
|
343 |
const char *expression;
|
344 |
const char *file;
|
345 |
int line;
|
346 |
|
347 |
/* Alarm was fired. */
|
348 |
int alarm;
|
349 |
} native_code_handler_struct;
|
350 |
|
351 |
/* Global crash handler structure. */
|
352 |
static native_code_global_struct native_code_g =
|
353 |
NATIVE_CODE_GLOBAL_INITIALIZER;
|
354 |
|
355 |
/* Thread variable holding context. */
|
356 |
pthread_key_t native_code_thread;
|
357 |
|
358 |
#if (defined(USE_UNWIND) && !defined(USE_CORKSCREW))
|
359 |
/* Unwind callback */
|
360 |
static _Unwind_Reason_Code
|
361 |
coffeecatch_unwind_callback(struct _Unwind_Context* context, void* arg) {
|
362 |
native_code_handler_struct *const s = (native_code_handler_struct*) arg;
|
363 |
|
364 |
const uintptr_t ip = _Unwind_GetIP(context);
|
365 |
|
366 |
DEBUG(print("called unwind callback\n"));
|
367 |
|
368 |
if (ip != 0x0) {
|
369 |
if (s->frames_skip == 0) {
|
370 |
s->frames[s->frames_size] = ip;
|
371 |
s->frames_size++;
|
372 |
} else {
|
373 |
s->frames_skip--;
|
374 |
}
|
375 |
}
|
376 |
|
377 |
if (s->frames_size == BACKTRACE_FRAMES_MAX) {
|
378 |
return _URC_END_OF_STACK;
|
379 |
} else {
|
380 |
DEBUG(print("returned _URC_OK\n"));
|
381 |
return _URC_OK;
|
382 |
}
|
383 |
}
|
384 |
#endif
|
385 |
|
386 |
/* Use libcorkscrew to get a backtrace inside a signal handler.
|
387 |
Will only return a non-zero code on Android >= 4 (with libcorkscrew.so
|
388 |
being shipped) */
|
389 |
#ifdef USE_CORKSCREW
|
390 |
static size_t coffeecatch_backtrace_signal(siginfo_t* si, void* sc,
|
391 |
backtrace_frame_t* frames,
|
392 |
size_t ignore_depth,
|
393 |
size_t max_depth) {
|
394 |
void *const libcorkscrew = dlopen("libcorkscrew.so", RTLD_LAZY | RTLD_LOCAL);
|
395 |
if (libcorkscrew != NULL) {
|
396 |
t_unwind_backtrace_signal_arch unwind_backtrace_signal_arch
|
397 |
= (t_unwind_backtrace_signal_arch)
|
398 |
dlsym(libcorkscrew, "unwind_backtrace_signal_arch");
|
399 |
t_acquire_my_map_info_list acquire_my_map_info_list
|
400 |
= (t_acquire_my_map_info_list)
|
401 |
dlsym(libcorkscrew, "acquire_my_map_info_list");
|
402 |
t_release_my_map_info_list release_my_map_info_list
|
403 |
= (t_release_my_map_info_list)
|
404 |
dlsym(libcorkscrew, "release_my_map_info_list");
|
405 |
if (unwind_backtrace_signal_arch != NULL
|
406 |
&& acquire_my_map_info_list != NULL
|
407 |
&& release_my_map_info_list != NULL) {
|
408 |
map_info_t*const info = acquire_my_map_info_list();
|
409 |
const ssize_t size =
|
410 |
unwind_backtrace_signal_arch(si, sc, info, frames, ignore_depth,
|
411 |
max_depth);
|
412 |
release_my_map_info_list(info);
|
413 |
return size >= 0 ? size : 0;
|
414 |
} else {
|
415 |
DEBUG(print("symbols not found in libcorkscrew.so\n"));
|
416 |
}
|
417 |
dlclose(libcorkscrew);
|
418 |
} else {
|
419 |
DEBUG(print("libcorkscrew.so could not be loaded\n"));
|
420 |
}
|
421 |
return 0;
|
422 |
}
|
423 |
|
424 |
static int coffeecatch_backtrace_symbols(const backtrace_frame_t* backtrace,
|
425 |
size_t frames,
|
426 |
void (*fun)(void *arg,
|
427 |
const backtrace_symbol_t *sym),
|
428 |
void *arg) {
|
429 |
int success = 0;
|
430 |
void *const libcorkscrew = dlopen("libcorkscrew.so", RTLD_LAZY | RTLD_LOCAL);
|
431 |
if (libcorkscrew != NULL) {
|
432 |
t_get_backtrace_symbols get_backtrace_symbols
|
433 |
= (t_get_backtrace_symbols)
|
434 |
dlsym(libcorkscrew, "get_backtrace_symbols");
|
435 |
t_free_backtrace_symbols free_backtrace_symbols
|
436 |
= (t_free_backtrace_symbols)
|
437 |
dlsym(libcorkscrew, "free_backtrace_symbols");
|
438 |
if (get_backtrace_symbols != NULL
|
439 |
&& free_backtrace_symbols != NULL) {
|
440 |
backtrace_symbol_t symbols[BACKTRACE_FRAMES_MAX];
|
441 |
size_t i;
|
442 |
if (frames > BACKTRACE_FRAMES_MAX) {
|
443 |
frames = BACKTRACE_FRAMES_MAX;
|
444 |
}
|
445 |
get_backtrace_symbols(backtrace, frames, symbols);
|
446 |
for(i = 0; i < frames; i++) {
|
447 |
fun(arg, &symbols[i]);
|
448 |
}
|
449 |
free_backtrace_symbols(symbols, frames);
|
450 |
success = 1;
|
451 |
} else {
|
452 |
DEBUG(print("symbols not found in libcorkscrew.so\n"));
|
453 |
}
|
454 |
dlclose(libcorkscrew);
|
455 |
} else {
|
456 |
DEBUG(print("libcorkscrew.so could not be loaded\n"));
|
457 |
}
|
458 |
return success;
|
459 |
}
|
460 |
#endif
|
461 |
|
462 |
/* Use libunwind to get a backtrace inside a signal handler.
|
463 |
Will only return a non-zero code on Android >= 5 (with libunwind.so
|
464 |
being shipped) */
|
465 |
#ifdef USE_LIBUNWIND
|
466 |
static ssize_t coffeecatch_unwind_signal(siginfo_t* si, void* sc,
|
467 |
void** frames,
|
468 |
size_t ignore_depth,
|
469 |
size_t max_depth) {
|
470 |
void *libunwind = dlopen("libunwind.so", RTLD_LAZY | RTLD_LOCAL);
|
471 |
if (libunwind != NULL) {
|
472 |
int (*backtrace)(void **buffer, int size) =
|
473 |
dlsym(libunwind, "unw_backtrace");
|
474 |
if (backtrace != NULL) {
|
475 |
int nb = backtrace(frames, max_depth);
|
476 |
if (nb > 0) {
|
477 |
}
|
478 |
return nb;
|
479 |
} else {
|
480 |
DEBUG(print("symbols not found in libunwind.so\n"));
|
481 |
}
|
482 |
dlclose(libunwind);
|
483 |
} else {
|
484 |
DEBUG(print("libunwind.so could not be loaded\n"));
|
485 |
}
|
486 |
return -1;
|
487 |
}
|
488 |
#endif
|
489 |
|
490 |
/* Call the old handler. */
|
491 |
static void coffeecatch_call_old_signal_handler(const int code, siginfo_t *const si,
|
492 |
void * const sc) {
|
493 |
/* Call the "real" Java handler for JIT and internals. */
|
494 |
if (code >= 0 && code < SIG_NUMBER_MAX) {
|
495 |
if (native_code_g.sa_old[code].sa_sigaction != NULL) {
|
496 |
native_code_g.sa_old[code].sa_sigaction(code, si, sc);
|
497 |
} else if (native_code_g.sa_old[code].sa_handler != NULL) {
|
498 |
native_code_g.sa_old[code].sa_handler(code);
|
499 |
}
|
500 |
}
|
501 |
}
|
502 |
|
503 |
/* Unflag "on stack" */
|
504 |
static void coffeecatch_revert_alternate_stack(void) {
|
505 |
#ifndef NO_USE_SIGALTSTACK
|
506 |
stack_t ss;
|
507 |
if (sigaltstack(NULL, &ss) == 0) {
|
508 |
ss.ss_flags &= ~SS_ONSTACK;
|
509 |
sigaltstack (&ss, NULL);
|
510 |
}
|
511 |
#endif
|
512 |
}
|
513 |
|
514 |
/* Try to jump to userland. */
|
515 |
static void coffeecatch_try_jump_userland(native_code_handler_struct*
|
516 |
const t,
|
517 |
const int code,
|
518 |
siginfo_t *const si,
|
519 |
void * const sc) {
|
520 |
(void) si; /* UNUSED */
|
521 |
(void) sc; /* UNUSED */
|
522 |
|
523 |
/* Valid context ? */
|
524 |
if (t != NULL && t->ctx_is_set) {
|
525 |
DEBUG(print("calling siglongjmp()\n"));
|
526 |
|
527 |
/* Invalidate the context */
|
528 |
t->ctx_is_set = 0;
|
529 |
|
530 |
/* We need to revert the alternate stack before jumping. */
|
531 |
coffeecatch_revert_alternate_stack();
|
532 |
|
533 |
/*
|
534 |
* Note on async-signal-safety of siglongjmp() [POSIX] :
|
535 |
* "Note that longjmp() and siglongjmp() are not in the list of
|
536 |
* async-signal-safe functions. This is because the code executing after
|
537 |
* longjmp() and siglongjmp() can call any unsafe functions with the same
|
538 |
* danger as calling those unsafe functions directly from the signal
|
539 |
* handler. Applications that use longjmp() and siglongjmp() from within
|
540 |
* signal handlers require rigorous protection in order to be portable.
|
541 |
* Many of the other functions that are excluded from the list are
|
542 |
* traditionally implemented using either malloc() or free() functions or
|
543 |
* the standard I/O library, both of which traditionally use data
|
544 |
* structures in a non-async-signal-safe manner. Since any combination of
|
545 |
* different functions using a common data structure can cause
|
546 |
* async-signal-safety problems, this volume of POSIX.1-2008 does not
|
547 |
* define the behavior when any unsafe function is called in a signal
|
548 |
* handler that interrupts an unsafe function."
|
549 |
*/
|
550 |
siglongjmp(t->ctx, code);
|
551 |
}
|
552 |
}
|
553 |
|
554 |
static void coffeecatch_start_alarm(void) {
|
555 |
/* Ensure we do not deadlock. Default of ALRM is to die.
|
556 |
* (signal() and alarm() are signal-safe) */
|
557 |
(void) alarm(30);
|
558 |
}
|
559 |
|
560 |
static void coffeecatch_mark_alarm(native_code_handler_struct *const t) {
|
561 |
t->alarm = 1;
|
562 |
}
|
563 |
|
564 |
/* Copy context infos (signal code, etc.) */
|
565 |
static void coffeecatch_copy_context(native_code_handler_struct *const t,
|
566 |
const int code, siginfo_t *const si,
|
567 |
void *const sc) {
|
568 |
t->code = code;
|
569 |
t->si = *si;
|
570 |
if (sc != NULL) {
|
571 |
ucontext_t *const uc = (ucontext_t*) sc;
|
572 |
t->uc = *uc;
|
573 |
} else {
|
574 |
memset(&t->uc, 0, sizeof(t->uc));
|
575 |
}
|
576 |
|
577 |
#ifdef USE_UNWIND
|
578 |
/* Frame buffer initial position. */
|
579 |
t->frames_size = 0;
|
580 |
|
581 |
/* Skip us and the caller. */
|
582 |
t->frames_skip = 2;
|
583 |
|
584 |
/* Use the corkscrew library to extract the backtrace. */
|
585 |
#ifdef USE_CORKSCREW
|
586 |
t->frames_size = coffeecatch_backtrace_signal(si, sc, t->frames, 0,
|
587 |
BACKTRACE_FRAMES_MAX);
|
588 |
#else
|
589 |
/* Unwind frames (equivalent to backtrace()) */
|
590 |
_Unwind_Backtrace(coffeecatch_unwind_callback, t);
|
591 |
#endif
|
592 |
|
593 |
#ifdef USE_LIBUNWIND
|
594 |
if (t->frames_size == 0) {
|
595 |
size_t i;
|
596 |
t->frames_size = coffeecatch_unwind_signal(si, sc, t->uframes, 0,
|
597 |
BACKTRACE_FRAMES_MAX);
|
598 |
for(i = 0 ; i < t->frames_size ; i++) {
|
599 |
t->frames[i].absolute_pc = (uintptr_t) t->uframes[i];
|
600 |
t->frames[i].stack_top = 0;
|
601 |
t->frames[i].stack_size = 0;
|
602 |
}
|
603 |
}
|
604 |
#endif
|
605 |
|
606 |
if (t->frames_size != 0) {
|
607 |
DEBUG(print("called _Unwind_Backtrace()\n"));
|
608 |
} else {
|
609 |
DEBUG(print("called _Unwind_Backtrace(), but no traces\n"));
|
610 |
}
|
611 |
#endif
|
612 |
}
|
613 |
|
614 |
/* Return the thread-specific native_code_handler_struct structure, or
|
615 |
* @c null if no such structure is available. */
|
616 |
static native_code_handler_struct* coffeecatch_get() {
|
617 |
return (native_code_handler_struct*)
|
618 |
pthread_getspecific(native_code_thread);
|
619 |
}
|
620 |
|
621 |
int coffeecatch_cancel_pending_alarm() {
|
622 |
native_code_handler_struct *const t = coffeecatch_get();
|
623 |
if (t != NULL && t->alarm) {
|
624 |
t->alarm = 0;
|
625 |
/* "If seconds is 0, a pending alarm request, if any, is canceled." */
|
626 |
alarm(0);
|
627 |
return 0;
|
628 |
}
|
629 |
return -1;
|
630 |
}
|
631 |
|
632 |
/* Internal signal pass-through. Allows to peek the "real" crash before
|
633 |
* calling the Java handler. Remember than Java needs many of the signals
|
634 |
* (for the JIT, for test-free NullPointerException handling, etc.)
|
635 |
* We record the siginfo_t context in this function each time it is being
|
636 |
* called, to be able to know what error caused an issue.
|
637 |
*/
|
638 |
static void coffeecatch_signal_pass(const int code, siginfo_t *const si,
|
639 |
void *const sc) {
|
640 |
native_code_handler_struct *t;
|
641 |
|
642 |
DEBUG(print("caught signal\n"));
|
643 |
|
644 |
/* Call the "real" Java handler for JIT and internals. */
|
645 |
coffeecatch_call_old_signal_handler(code, si, sc);
|
646 |
|
647 |
/* Still here ?
|
648 |
* FIXME TODO: This is the Dalvik behavior - but is it the SunJVM one ? */
|
649 |
|
650 |
/* Ensure we do not deadlock. Default of ALRM is to die.
|
651 |
* (signal() and alarm() are signal-safe) */
|
652 |
signal(code, SIG_DFL);
|
653 |
coffeecatch_start_alarm();
|
654 |
|
655 |
/* Available context ? */
|
656 |
t = coffeecatch_get();
|
657 |
if (t != NULL) {
|
658 |
/* An alarm() call was triggered. */
|
659 |
coffeecatch_mark_alarm(t);
|
660 |
|
661 |
/* Take note of the signal. */
|
662 |
coffeecatch_copy_context(t, code, si, sc);
|
663 |
|
664 |
/* Back to the future. */
|
665 |
coffeecatch_try_jump_userland(t, code, si, sc);
|
666 |
}
|
667 |
|
668 |
/* Nope. (abort() is signal-safe) */
|
669 |
DEBUG(print("calling abort()\n"));
|
670 |
signal(SIGABRT, SIG_DFL);
|
671 |
abort();
|
672 |
}
|
673 |
|
674 |
/* Internal crash handler for abort(). Java calls abort() if its signal handler
|
675 |
* could not resolve the signal ; thus calling us through this handler. */
|
676 |
static void coffeecatch_signal_abort(const int code, siginfo_t *const si,
|
677 |
void *const sc) {
|
678 |
native_code_handler_struct *t;
|
679 |
|
680 |
(void) sc; /* UNUSED */
|
681 |
|
682 |
DEBUG(print("caught abort\n"));
|
683 |
|
684 |
/* Ensure we do not deadlock. Default of ALRM is to die.
|
685 |
* (signal() and alarm() are signal-safe) */
|
686 |
signal(code, SIG_DFL);
|
687 |
coffeecatch_start_alarm();
|
688 |
|
689 |
/* Available context ? */
|
690 |
t = coffeecatch_get();
|
691 |
if (t != NULL) {
|
692 |
/* An alarm() call was triggered. */
|
693 |
coffeecatch_mark_alarm(t);
|
694 |
|
695 |
/* Take note (real "abort()") */
|
696 |
coffeecatch_copy_context(t, code, si, sc);
|
697 |
|
698 |
/* Back to the future. */
|
699 |
coffeecatch_try_jump_userland(t, code, si, sc);
|
700 |
}
|
701 |
|
702 |
/* No such restore point, call old signal handler then. */
|
703 |
DEBUG(print("calling old signal handler\n"));
|
704 |
coffeecatch_call_old_signal_handler(code, si, sc);
|
705 |
|
706 |
/* Nope. (abort() is signal-safe) */
|
707 |
DEBUG(print("calling abort()\n"));
|
708 |
abort();
|
709 |
}
|
710 |
|
711 |
/* Internal globals initialization. */
|
712 |
static int coffeecatch_handler_setup_global(void) {
|
713 |
if (native_code_g.initialized++ == 0) {
|
714 |
size_t i;
|
715 |
struct sigaction sa_abort;
|
716 |
struct sigaction sa_pass;
|
717 |
|
718 |
DEBUG(print("installing global signal handlers\n"));
|
719 |
|
720 |
/* Setup handler structure. */
|
721 |
memset(&sa_abort, 0, sizeof(sa_abort));
|
722 |
sigemptyset(&sa_abort.sa_mask);
|
723 |
sa_abort.sa_sigaction = coffeecatch_signal_abort;
|
724 |
sa_abort.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
725 |
|
726 |
memset(&sa_pass, 0, sizeof(sa_pass));
|
727 |
sigemptyset(&sa_pass.sa_mask);
|
728 |
sa_pass.sa_sigaction = coffeecatch_signal_pass;
|
729 |
sa_pass.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
730 |
|
731 |
/* Allocate */
|
732 |
native_code_g.sa_old = calloc(sizeof(struct sigaction), SIG_NUMBER_MAX);
|
733 |
if (native_code_g.sa_old == NULL) {
|
734 |
return -1;
|
735 |
}
|
736 |
|
737 |
/* Setup signal handlers for SIGABRT (Java calls abort()) and others. **/
|
738 |
for (i = 0; native_sig_catch[i] != 0; i++) {
|
739 |
const int sig = native_sig_catch[i];
|
740 |
const struct sigaction * const action =
|
741 |
sig == SIGABRT ? &sa_abort : &sa_pass;
|
742 |
assert(sig < SIG_NUMBER_MAX);
|
743 |
if (sigaction(sig, action, &native_code_g.sa_old[sig]) != 0) {
|
744 |
return -1;
|
745 |
}
|
746 |
}
|
747 |
|
748 |
/* Initialize thread var. */
|
749 |
if (pthread_key_create(&native_code_thread, NULL) != 0) {
|
750 |
return -1;
|
751 |
}
|
752 |
|
753 |
DEBUG(print("installed global signal handlers\n"));
|
754 |
}
|
755 |
|
756 |
/* OK. */
|
757 |
return 0;
|
758 |
}
|
759 |
|
760 |
/**
|
761 |
* Free a native_code_handler_struct structure.
|
762 |
**/
|
763 |
static int coffeecatch_native_code_handler_struct_free(native_code_handler_struct *const t) {
|
764 |
int code = 0;
|
765 |
|
766 |
if (t == NULL) {
|
767 |
return -1;
|
768 |
}
|
769 |
|
770 |
#ifndef NO_USE_SIGALTSTACK
|
771 |
/* Restore previous alternative stack. */
|
772 |
if (t->stack_old.ss_sp != NULL && sigaltstack(&t->stack_old, NULL) != 0) {
|
773 |
#ifndef USE_SILENT_SIGALTSTACK
|
774 |
code = -1;
|
775 |
#endif
|
776 |
}
|
777 |
#endif
|
778 |
|
779 |
/* Free alternative stack */
|
780 |
if (t->stack_buffer != NULL) {
|
781 |
free(t->stack_buffer);
|
782 |
t->stack_buffer = NULL;
|
783 |
t->stack_buffer_size = 0;
|
784 |
}
|
785 |
|
786 |
/* Free structure. */
|
787 |
free(t);
|
788 |
|
789 |
return code;
|
790 |
}
|
791 |
|
792 |
/**
|
793 |
* Create a native_code_handler_struct structure.
|
794 |
**/
|
795 |
static native_code_handler_struct* coffeecatch_native_code_handler_struct_init(void) {
|
796 |
stack_t stack;
|
797 |
native_code_handler_struct *const t =
|
798 |
calloc(sizeof(native_code_handler_struct), 1);
|
799 |
|
800 |
if (t == NULL) {
|
801 |
return NULL;
|
802 |
}
|
803 |
|
804 |
DEBUG(print("installing thread alternative stack\n"));
|
805 |
|
806 |
/* Initialize structure */
|
807 |
t->stack_buffer_size = SIG_STACK_BUFFER_SIZE;
|
808 |
t->stack_buffer = malloc(t->stack_buffer_size);
|
809 |
if (t->stack_buffer == NULL) {
|
810 |
coffeecatch_native_code_handler_struct_free(t);
|
811 |
return NULL;
|
812 |
}
|
813 |
|
814 |
/* Setup alternative stack. */
|
815 |
memset(&stack, 0, sizeof(stack));
|
816 |
stack.ss_sp = t->stack_buffer;
|
817 |
stack.ss_size = t->stack_buffer_size;
|
818 |
stack.ss_flags = 0;
|
819 |
|
820 |
#ifndef NO_USE_SIGALTSTACK
|
821 |
/* Install alternative stack. This is thread-safe */
|
822 |
if (sigaltstack(&stack, &t->stack_old) != 0) {
|
823 |
#ifndef USE_SILENT_SIGALTSTACK
|
824 |
coffeecatch_native_code_handler_struct_free(t);
|
825 |
return NULL;
|
826 |
#endif
|
827 |
}
|
828 |
#endif
|
829 |
|
830 |
return t;
|
831 |
}
|
832 |
|
833 |
/**
|
834 |
* Acquire the crash handler for the current thread.
|
835 |
* The coffeecatch_handler_cleanup() must be called to release allocated
|
836 |
* resources.
|
837 |
**/
|
838 |
static int coffeecatch_handler_setup(int setup_thread) {
|
839 |
int code;
|
840 |
|
841 |
DEBUG(print("setup for a new handler\n"));
|
842 |
|
843 |
/* Initialize globals. */
|
844 |
if (pthread_mutex_lock(&native_code_g.mutex) != 0) {
|
845 |
return -1;
|
846 |
}
|
847 |
code = coffeecatch_handler_setup_global();
|
848 |
if (pthread_mutex_unlock(&native_code_g.mutex) != 0) {
|
849 |
return -1;
|
850 |
}
|
851 |
|
852 |
/* Global initialization failed. */
|
853 |
if (code != 0) {
|
854 |
return -1;
|
855 |
}
|
856 |
|
857 |
/* Initialize locals. */
|
858 |
if (setup_thread && coffeecatch_get() == NULL) {
|
859 |
native_code_handler_struct *const t =
|
860 |
coffeecatch_native_code_handler_struct_init();
|
861 |
|
862 |
if (t == NULL) {
|
863 |
return -1;
|
864 |
}
|
865 |
|
866 |
DEBUG(print("installing thread alternative stack\n"));
|
867 |
|
868 |
/* Set thread-specific value. */
|
869 |
if (pthread_setspecific(native_code_thread, t) != 0) {
|
870 |
coffeecatch_native_code_handler_struct_free(t);
|
871 |
return -1;
|
872 |
}
|
873 |
|
874 |
DEBUG(print("installed thread alternative stack\n"));
|
875 |
}
|
876 |
|
877 |
/* OK. */
|
878 |
return 0;
|
879 |
}
|
880 |
|
881 |
/**
|
882 |
* Release the resources allocated by a previous call to
|
883 |
* coffeecatch_handler_setup().
|
884 |
* This function must be called as many times as
|
885 |
* coffeecatch_handler_setup() was called to fully release allocated
|
886 |
* resources.
|
887 |
**/
|
888 |
static int coffeecatch_handler_cleanup() {
|
889 |
/* Cleanup locals. */
|
890 |
native_code_handler_struct *const t = coffeecatch_get();
|
891 |
if (t != NULL) {
|
892 |
DEBUG(print("removing thread alternative stack\n"));
|
893 |
|
894 |
/* Erase thread-specific value now (detach). */
|
895 |
if (pthread_setspecific(native_code_thread, NULL) != 0) {
|
896 |
assert(! "pthread_setspecific() failed");
|
897 |
}
|
898 |
|
899 |
/* Free handler and reset slternate stack */
|
900 |
if (coffeecatch_native_code_handler_struct_free(t) != 0) {
|
901 |
return -1;
|
902 |
}
|
903 |
|
904 |
DEBUG(print("removed thread alternative stack\n"));
|
905 |
}
|
906 |
|
907 |
/* Cleanup globals. */
|
908 |
if (pthread_mutex_lock(&native_code_g.mutex) != 0) {
|
909 |
assert(! "pthread_mutex_lock() failed");
|
910 |
}
|
911 |
assert(native_code_g.initialized != 0);
|
912 |
if (--native_code_g.initialized == 0) {
|
913 |
size_t i;
|
914 |
|
915 |
DEBUG(print("removing global signal handlers\n"));
|
916 |
|
917 |
/* Restore signal handler. */
|
918 |
for(i = 0; native_sig_catch[i] != 0; i++) {
|
919 |
const int sig = native_sig_catch[i];
|
920 |
assert(sig < SIG_NUMBER_MAX);
|
921 |
if (sigaction(sig, &native_code_g.sa_old[sig], NULL) != 0) {
|
922 |
return -1;
|
923 |
}
|
924 |
}
|
925 |
|
926 |
/* Free old structure. */
|
927 |
free(native_code_g.sa_old);
|
928 |
native_code_g.sa_old = NULL;
|
929 |
|
930 |
/* Delete thread var. */
|
931 |
if (pthread_key_delete(native_code_thread) != 0) {
|
932 |
assert(! "pthread_key_delete() failed");
|
933 |
}
|
934 |
|
935 |
DEBUG(print("removed global signal handlers\n"));
|
936 |
}
|
937 |
if (pthread_mutex_unlock(&native_code_g.mutex) != 0) {
|
938 |
assert(! "pthread_mutex_unlock() failed");
|
939 |
}
|
940 |
|
941 |
return 0;
|
942 |
}
|
943 |
|
944 |
/**
|
945 |
* Get the signal associated with the crash.
|
946 |
*/
|
947 |
int coffeecatch_get_signal() {
|
948 |
const native_code_handler_struct* const t = coffeecatch_get();
|
949 |
if (t != NULL) {
|
950 |
return t->code;
|
951 |
} else {
|
952 |
return -1;
|
953 |
}
|
954 |
}
|
955 |
|
956 |
/* Signal descriptions.
|
957 |
See <http://pubs.opengroup.org/onlinepubs/009696699/basedefs/signal.h.html>
|
958 |
*/
|
959 |
static const char* coffeecatch_desc_sig(int sig, int code) {
|
960 |
switch(sig) {
|
961 |
case SIGILL:
|
962 |
switch(code) {
|
963 |
case ILL_ILLOPC:
|
964 |
return "Illegal opcode";
|
965 |
case ILL_ILLOPN:
|
966 |
return "Illegal operand";
|
967 |
case ILL_ILLADR:
|
968 |
return "Illegal addressing mode";
|
969 |
case ILL_ILLTRP:
|
970 |
return "Illegal trap";
|
971 |
case ILL_PRVOPC:
|
972 |
return "Privileged opcode";
|
973 |
case ILL_PRVREG:
|
974 |
return "Privileged register";
|
975 |
case ILL_COPROC:
|
976 |
return "Coprocessor error";
|
977 |
case ILL_BADSTK:
|
978 |
return "Internal stack error";
|
979 |
default:
|
980 |
return "Illegal operation";
|
981 |
}
|
982 |
break;
|
983 |
case SIGFPE:
|
984 |
switch(code) {
|
985 |
case FPE_INTDIV:
|
986 |
return "Integer divide by zero";
|
987 |
case FPE_INTOVF:
|
988 |
return "Integer overflow";
|
989 |
case FPE_FLTDIV:
|
990 |
return "Floating-point divide by zero";
|
991 |
case FPE_FLTOVF:
|
992 |
return "Floating-point overflow";
|
993 |
case FPE_FLTUND:
|
994 |
return "Floating-point underflow";
|
995 |
case FPE_FLTRES:
|
996 |
return "Floating-point inexact result";
|
997 |
case FPE_FLTINV:
|
998 |
return "Invalid floating-point operation";
|
999 |
case FPE_FLTSUB:
|
1000 |
return "Subscript out of range";
|
1001 |
default:
|
1002 |
return "Floating-point";
|
1003 |
}
|
1004 |
break;
|
1005 |
case SIGSEGV:
|
1006 |
switch(code) {
|
1007 |
case SEGV_MAPERR:
|
1008 |
return "Address not mapped to object";
|
1009 |
case SEGV_ACCERR:
|
1010 |
return "Invalid permissions for mapped object";
|
1011 |
default:
|
1012 |
return "Segmentation violation";
|
1013 |
}
|
1014 |
break;
|
1015 |
case SIGBUS:
|
1016 |
switch(code) {
|
1017 |
case BUS_ADRALN:
|
1018 |
return "Invalid address alignment";
|
1019 |
case BUS_ADRERR:
|
1020 |
return "Nonexistent physical address";
|
1021 |
case BUS_OBJERR:
|
1022 |
return "Object-specific hardware error";
|
1023 |
default:
|
1024 |
return "Bus error";
|
1025 |
}
|
1026 |
break;
|
1027 |
case SIGTRAP:
|
1028 |
switch(code) {
|
1029 |
case TRAP_BRKPT:
|
1030 |
return "Process breakpoint";
|
1031 |
case TRAP_TRACE:
|
1032 |
return "Process trace trap";
|
1033 |
default:
|
1034 |
return "Trap";
|
1035 |
}
|
1036 |
break;
|
1037 |
case SIGCHLD:
|
1038 |
switch(code) {
|
1039 |
case CLD_EXITED:
|
1040 |
return "Child has exited";
|
1041 |
case CLD_KILLED:
|
1042 |
return "Child has terminated abnormally and did not create a core file";
|
1043 |
case CLD_DUMPED:
|
1044 |
return "Child has terminated abnormally and created a core file";
|
1045 |
case CLD_TRAPPED:
|
1046 |
return "Traced child has trapped";
|
1047 |
case CLD_STOPPED:
|
1048 |
return "Child has stopped";
|
1049 |
case CLD_CONTINUED:
|
1050 |
return "Stopped child has continued";
|
1051 |
default:
|
1052 |
return "Child";
|
1053 |
}
|
1054 |
break;
|
1055 |
case SIGPOLL:
|
1056 |
switch(code) {
|
1057 |
case POLL_IN:
|
1058 |
return "Data input available";
|
1059 |
case POLL_OUT:
|
1060 |
return "Output buffers available";
|
1061 |
case POLL_MSG:
|
1062 |
return "Input message available";
|
1063 |
case POLL_ERR:
|
1064 |
return "I/O error";
|
1065 |
case POLL_PRI:
|
1066 |
return "High priority input available";
|
1067 |
case POLL_HUP:
|
1068 |
return "Device disconnected";
|
1069 |
default:
|
1070 |
return "Pool";
|
1071 |
}
|
1072 |
break;
|
1073 |
case SIGABRT:
|
1074 |
return "Process abort signal";
|
1075 |
case SIGALRM:
|
1076 |
return "Alarm clock";
|
1077 |
case SIGCONT:
|
1078 |
return "Continue executing, if stopped";
|
1079 |
case SIGHUP:
|
1080 |
return "Hangup";
|
1081 |
case SIGINT:
|
1082 |
return "Terminal interrupt signal";
|
1083 |
case SIGKILL:
|
1084 |
return "Kill";
|
1085 |
case SIGPIPE:
|
1086 |
return "Write on a pipe with no one to read it";
|
1087 |
case SIGQUIT:
|
1088 |
return "Terminal quit signal";
|
1089 |
case SIGSTOP:
|
1090 |
return "Stop executing";
|
1091 |
case SIGTERM:
|
1092 |
return "Termination signal";
|
1093 |
case SIGTSTP:
|
1094 |
return "Terminal stop signal";
|
1095 |
case SIGTTIN:
|
1096 |
return "Background process attempting read";
|
1097 |
case SIGTTOU:
|
1098 |
return "Background process attempting write";
|
1099 |
case SIGUSR1:
|
1100 |
return "User-defined signal 1";
|
1101 |
case SIGUSR2:
|
1102 |
return "User-defined signal 2";
|
1103 |
case SIGPROF:
|
1104 |
return "Profiling timer expired";
|
1105 |
case SIGSYS:
|
1106 |
return "Bad system call";
|
1107 |
case SIGVTALRM:
|
1108 |
return "Virtual timer expired";
|
1109 |
case SIGURG:
|
1110 |
return "High bandwidth data is available at a socket";
|
1111 |
case SIGXCPU:
|
1112 |
return "CPU time limit exceeded";
|
1113 |
case SIGXFSZ:
|
1114 |
return "File size limit exceeded";
|
1115 |
default:
|
1116 |
switch(code) {
|
1117 |
case SI_USER:
|
1118 |
return "Signal sent by kill()";
|
1119 |
case SI_QUEUE:
|
1120 |
return "Signal sent by the sigqueue()";
|
1121 |
case SI_TIMER:
|
1122 |
return "Signal generated by expiration of a timer set by timer_settime()";
|
1123 |
case SI_ASYNCIO:
|
1124 |
return "Signal generated by completion of an asynchronous I/O request";
|
1125 |
case SI_MESGQ:
|
1126 |
return
|
1127 |
"Signal generated by arrival of a message on an empty message queue";
|
1128 |
default:
|
1129 |
return "Unknown signal";
|
1130 |
}
|
1131 |
break;
|
1132 |
}
|
1133 |
}
|
1134 |
|
1135 |
/**
|
1136 |
* Get the backtrace size. Returns 0 if no backtrace is available.
|
1137 |
*/
|
1138 |
size_t coffeecatch_get_backtrace_size(void) {
|
1139 |
#ifdef USE_UNWIND
|
1140 |
const native_code_handler_struct* const t = coffeecatch_get();
|
1141 |
if (t != NULL) {
|
1142 |
return t->frames_size;
|
1143 |
} else {
|
1144 |
return 0;
|
1145 |
}
|
1146 |
#else
|
1147 |
return 0;
|
1148 |
#endif
|
1149 |
}
|
1150 |
|
1151 |
/**
|
1152 |
* Get the <index>th element of the backtrace, or 0 upon error.
|
1153 |
*/
|
1154 |
uintptr_t coffeecatch_get_backtrace(ssize_t index) {
|
1155 |
#ifdef USE_UNWIND
|
1156 |
const native_code_handler_struct* const t = coffeecatch_get();
|
1157 |
if (t != NULL) {
|
1158 |
if (index < 0) {
|
1159 |
index = t->frames_size + index;
|
1160 |
}
|
1161 |
if (index >= 0 && (size_t) index < t->frames_size) {
|
1162 |
#ifdef USE_CORKSCREW
|
1163 |
return t->frames[index].absolute_pc;
|
1164 |
#else
|
1165 |
return t->frames[index];
|
1166 |
#endif
|
1167 |
}
|
1168 |
}
|
1169 |
#else
|
1170 |
(void) index;
|
1171 |
#endif
|
1172 |
return 0;
|
1173 |
}
|
1174 |
|
1175 |
/**
|
1176 |
* Get the program counter, given a pointer to a ucontext_t context.
|
1177 |
**/
|
1178 |
static uintptr_t coffeecatch_get_pc_from_ucontext(const ucontext_t *uc) {
|
1179 |
#if (defined(__arm__))
|
1180 |
return uc->uc_mcontext.arm_pc;
|
1181 |
#elif defined(__aarch64__)
|
1182 |
return uc->uc_mcontext.pc;
|
1183 |
#elif (defined(__x86_64__))
|
1184 |
return uc->uc_mcontext.gregs[REG_RIP];
|
1185 |
#elif (defined(__i386))
|
1186 |
return uc->uc_mcontext.gregs[REG_EIP];
|
1187 |
#elif (defined (__ppc__)) || (defined (__powerpc__))
|
1188 |
return uc->uc_mcontext.regs->nip;
|
1189 |
#elif (defined(__hppa__))
|
1190 |
return uc->uc_mcontext.sc_iaoq[0] & ~0x3UL;
|
1191 |
#elif (defined(__sparc__) && defined (__arch64__))
|
1192 |
return uc->uc_mcontext.mc_gregs[MC_PC];
|
1193 |
#elif (defined(__sparc__) && !defined (__arch64__))
|
1194 |
return uc->uc_mcontext.gregs[REG_PC];
|
1195 |
#elif (defined(__mips__))
|
1196 |
return uc->uc_mcontext.gregs[31];
|
1197 |
#else
|
1198 |
#error "Architecture is unknown, please report me!"
|
1199 |
#endif
|
1200 |
}
|
1201 |
|
1202 |
/* Is this module name look like a DLL ?
|
1203 |
FIXME: find a better way to do that... */
|
1204 |
static int coffeecatch_is_dll(const char *name) {
|
1205 |
size_t i;
|
1206 |
for(i = 0; name[i] != '\0'; i++) {
|
1207 |
if (name[i + 0] == '.' &&
|
1208 |
name[i + 1] == 's' &&
|
1209 |
name[i + 2] == 'o' &&
|
1210 |
( name[i + 3] == '\0' || name[i + 3] == '.') ) {
|
1211 |
return 1;
|
1212 |
}
|
1213 |
}
|
1214 |
return 0;
|
1215 |
}
|
1216 |
|
1217 |
/* Extract a line information on a PC address. */
|
1218 |
static void format_pc_address_cb(uintptr_t pc,
|
1219 |
void (*fun)(void *arg, const char *module,
|
1220 |
uintptr_t addr,
|
1221 |
const char *function,
|
1222 |
uintptr_t offset), void *arg) {
|
1223 |
if (pc != 0) {
|
1224 |
Dl_info info;
|
1225 |
void * const addr = (void*) pc;
|
1226 |
/* dladdr() returns 0 on error, and nonzero on success. */
|
1227 |
if (dladdr(addr, &info) != 0 && info.dli_fname != NULL) {
|
1228 |
const uintptr_t near = (uintptr_t) info.dli_saddr;
|
1229 |
const uintptr_t offs = pc - near;
|
1230 |
const uintptr_t addr_rel = pc - (uintptr_t) info.dli_fbase;
|
1231 |
/* We need the absolute address for the main module (?).
|
1232 |
TODO FIXME to be investigated. */
|
1233 |
const uintptr_t addr_to_use = coffeecatch_is_dll(info.dli_fname)
|
1234 |
? addr_rel : pc;
|
1235 |
fun(arg, info.dli_fname, addr_to_use, info.dli_sname, offs);
|
1236 |
} else {
|
1237 |
fun(arg, NULL, pc, NULL, 0);
|
1238 |
}
|
1239 |
}
|
1240 |
}
|
1241 |
|
1242 |
typedef struct t_print_fun {
|
1243 |
char *buffer;
|
1244 |
size_t buffer_size;
|
1245 |
} t_print_fun;
|
1246 |
|
1247 |
static void print_fun(void *arg, const char *module, uintptr_t uaddr,
|
1248 |
const char *function, uintptr_t offset) {
|
1249 |
t_print_fun *const t = (t_print_fun*) arg;
|
1250 |
char *const buffer = t->buffer;
|
1251 |
const size_t buffer_size = t->buffer_size;
|
1252 |
const void*const addr = (void*) uaddr;
|
1253 |
if (module == NULL) {
|
1254 |
snprintf(buffer, buffer_size, "[at %p]", addr);
|
1255 |
} else if (function != NULL) {
|
1256 |
snprintf(buffer, buffer_size, "[at %s:%p (%s+0x%x)]", module, addr,
|
1257 |
function, (int) offset);
|
1258 |
} else {
|
1259 |
snprintf(buffer, buffer_size, "[at %s:%p]", module, addr);
|
1260 |
}
|
1261 |
}
|
1262 |
|
1263 |
/* Format a line information on a PC address. */
|
1264 |
static void format_pc_address(char *buffer, size_t buffer_size, uintptr_t pc) {
|
1265 |
t_print_fun t;
|
1266 |
t.buffer = buffer;
|
1267 |
t.buffer_size = buffer_size;
|
1268 |
format_pc_address_cb(pc, print_fun, &t);
|
1269 |
}
|
1270 |
|
1271 |
/**
|
1272 |
* Get the full error message associated with the crash.
|
1273 |
*/
|
1274 |
const char* coffeecatch_get_message() {
|
1275 |
const int error = errno;
|
1276 |
const native_code_handler_struct* const t = coffeecatch_get();
|
1277 |
|
1278 |
/* Found valid handler. */
|
1279 |
if (t != NULL) {
|
1280 |
char * const buffer = t->stack_buffer;
|
1281 |
const size_t buffer_len = t->stack_buffer_size;
|
1282 |
size_t buffer_offs = 0;
|
1283 |
|
1284 |
const char* const posix_desc =
|
1285 |
coffeecatch_desc_sig(t->si.si_signo, t->si.si_code);
|
1286 |
|
1287 |
/* Assertion failure ? */
|
1288 |
if ((t->code == SIGABRT
|
1289 |
#ifdef __ANDROID__
|
1290 |
/* See Android BUG #16672:
|
1291 |
* "C assert() failure causes SIGSEGV when it should cause SIGABRT" */
|
1292 |
|| (t->code == SIGSEGV && (uintptr_t) t->si.si_addr == 0xdeadbaad)
|
1293 |
#endif
|
1294 |
) && t->expression != NULL) {
|
1295 |
snprintf(&buffer[buffer_offs], buffer_len - buffer_offs,
|
1296 |
"assertion '%s' failed at %s:%d",
|
1297 |
t->expression, t->file, t->line);
|
1298 |
buffer_offs += strlen(&buffer[buffer_offs]);
|
1299 |
}
|
1300 |
/* Signal */
|
1301 |
else {
|
1302 |
snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, "signal %d",
|
1303 |
t->si.si_signo);
|
1304 |
buffer_offs += strlen(&buffer[buffer_offs]);
|
1305 |
|
1306 |
/* Description */
|
1307 |
snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " (%s)",
|
1308 |
posix_desc);
|
1309 |
buffer_offs += strlen(&buffer[buffer_offs]);
|
1310 |
|
1311 |
/* Address of faulting instruction */
|
1312 |
if (t->si.si_signo == SIGILL || t->si.si_signo == SIGSEGV) {
|
1313 |
snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " at address %p",
|
1314 |
t->si.si_addr);
|
1315 |
buffer_offs += strlen(&buffer[buffer_offs]);
|
1316 |
}
|
1317 |
}
|
1318 |
|
1319 |
/* [POSIX] If non-zero, an errno value associated with this signal,
|
1320 |
as defined in <errno.h>. */
|
1321 |
if (t->si.si_errno != 0) {
|
1322 |
snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, ": ");
|
1323 |
buffer_offs += strlen(&buffer[buffer_offs]);
|
1324 |
if (strerror_r(t->si.si_errno, &buffer[buffer_offs],
|
1325 |
buffer_len - buffer_offs) == 0) {
|
1326 |
snprintf(&buffer[buffer_offs], buffer_len - buffer_offs,
|
1327 |
"unknown error");
|
1328 |
buffer_offs += strlen(&buffer[buffer_offs]);
|
1329 |
}
|
1330 |
}
|
1331 |
|
1332 |
/* Sending process ID. */
|
1333 |
if (t->si.si_signo == SIGCHLD && t->si.si_pid != 0) {
|
1334 |
snprintf(&buffer[buffer_offs], buffer_len - buffer_offs,
|
1335 |
" (sent by pid %d)", (int) t->si.si_pid);
|
1336 |
buffer_offs += strlen(&buffer[buffer_offs]);
|
1337 |
}
|
1338 |
|
1339 |
/* Faulting program counter location. */
|
1340 |
if (coffeecatch_get_pc_from_ucontext(&t->uc) != 0) {
|
1341 |
const uintptr_t pc = coffeecatch_get_pc_from_ucontext(&t->uc);
|
1342 |
snprintf(&buffer[buffer_offs], buffer_len - buffer_offs, " ");
|
1343 |
buffer_offs += strlen(&buffer[buffer_offs]);
|
1344 |
format_pc_address(&buffer[buffer_offs], buffer_len - buffer_offs, pc);
|
1345 |
buffer_offs += strlen(&buffer[buffer_offs]);
|
1346 |
}
|
1347 |
|
1348 |
/* Return string. */
|
1349 |
buffer[buffer_offs] = '\0';
|
1350 |
return t->stack_buffer;
|
1351 |
} else {
|
1352 |
/* Static buffer in case of emergency */
|
1353 |
static char buffer[256];
|
1354 |
#ifdef _GNU_SOURCE
|
1355 |
return strerror_r(error, &buffer[0], sizeof(buffer));
|
1356 |
#else
|
1357 |
const int code = strerror_r(error, &buffer[0], sizeof(buffer));
|
1358 |
errno = error;
|
1359 |
if (code == 0) {
|
1360 |
return buffer;
|
1361 |
} else {
|
1362 |
return "unknown error during crash handler setup";
|
1363 |
}
|
1364 |
#endif
|
1365 |
}
|
1366 |
}
|
1367 |
|
1368 |
#if (defined(USE_CORKSCREW))
|
1369 |
typedef struct t_coffeecatch_backtrace_symbols_fun {
|
1370 |
void (*fun)(void *arg, const char *module, uintptr_t addr,
|
1371 |
const char *function, uintptr_t offset);
|
1372 |
void *arg;
|
1373 |
} t_coffeecatch_backtrace_symbols_fun;
|
1374 |
|
1375 |
static void coffeecatch_backtrace_symbols_fun(void *arg, const backtrace_symbol_t *sym) {
|
1376 |
t_coffeecatch_backtrace_symbols_fun *const bt =
|
1377 |
(t_coffeecatch_backtrace_symbols_fun*) arg;
|
1378 |
const char *symbol = sym->demangled_name != NULL
|
1379 |
? sym->demangled_name : sym->symbol_name;
|
1380 |
const uintptr_t rel = sym->relative_pc - sym->relative_symbol_addr;
|
1381 |
bt->fun(bt->arg, sym->map_name, sym->relative_pc, symbol, rel);
|
1382 |
}
|
1383 |
#endif
|
1384 |
|
1385 |
/**
|
1386 |
* Enumerate backtrace information.
|
1387 |
*/
|
1388 |
void coffeecatch_get_backtrace_info(void (*fun)(void *arg,
|
1389 |
const char *module,
|
1390 |
uintptr_t addr,
|
1391 |
const char *function,
|
1392 |
uintptr_t offset), void *arg) {
|
1393 |
const native_code_handler_struct* const t = coffeecatch_get();
|
1394 |
if (t != NULL) {
|
1395 |
size_t i;
|
1396 |
#if (defined(USE_CORKSCREW))
|
1397 |
t_coffeecatch_backtrace_symbols_fun bt;
|
1398 |
bt.fun = fun;
|
1399 |
bt.arg = arg;
|
1400 |
if (coffeecatch_backtrace_symbols(t->frames, t->frames_size,
|
1401 |
coffeecatch_backtrace_symbols_fun,
|
1402 |
&bt)) {
|
1403 |
return;
|
1404 |
}
|
1405 |
#endif
|
1406 |
for(i = 0; i < t->frames_size; i++) {
|
1407 |
const uintptr_t pc = t->frames[i].absolute_pc;
|
1408 |
format_pc_address_cb(pc, fun, arg);
|
1409 |
}
|
1410 |
}
|
1411 |
}
|
1412 |
|
1413 |
/**
|
1414 |
* Returns 1 if we are already inside a coffeecatch block, 0 otherwise.
|
1415 |
*/
|
1416 |
int coffeecatch_inside() {
|
1417 |
native_code_handler_struct *const t = coffeecatch_get();
|
1418 |
if (t != NULL && t->reenter > 0) {
|
1419 |
t->reenter++;
|
1420 |
return 1;
|
1421 |
}
|
1422 |
return 0;
|
1423 |
}
|
1424 |
|
1425 |
/**
|
1426 |
* Calls coffeecatch_handler_setup(1) to setup a crash handler, mark the
|
1427 |
* context as valid, and return 0 upon success.
|
1428 |
*/
|
1429 |
int coffeecatch_setup() {
|
1430 |
if (coffeecatch_handler_setup(1) == 0) {
|
1431 |
native_code_handler_struct *const t = coffeecatch_get();
|
1432 |
assert(t != NULL);
|
1433 |
assert(t->reenter == 0);
|
1434 |
t->reenter = 1;
|
1435 |
t->ctx_is_set = 1;
|
1436 |
return 0;
|
1437 |
} else {
|
1438 |
return -1;
|
1439 |
}
|
1440 |
}
|
1441 |
|
1442 |
/**
|
1443 |
* Calls coffeecatch_handler_cleanup()
|
1444 |
*/
|
1445 |
void coffeecatch_cleanup() {
|
1446 |
native_code_handler_struct *const t = coffeecatch_get();
|
1447 |
assert(t != NULL);
|
1448 |
assert(t->reenter > 0);
|
1449 |
t->reenter--;
|
1450 |
if (t->reenter == 0) {
|
1451 |
t->ctx_is_set = 0;
|
1452 |
coffeecatch_handler_cleanup();
|
1453 |
}
|
1454 |
}
|
1455 |
|
1456 |
sigjmp_buf* coffeecatch_get_ctx() {
|
1457 |
native_code_handler_struct* t = coffeecatch_get();
|
1458 |
assert(t != NULL);
|
1459 |
return &t->ctx;
|
1460 |
}
|
1461 |
|
1462 |
void coffeecatch_abort(const char* exp, const char* file, int line) {
|
1463 |
native_code_handler_struct *const t = coffeecatch_get();
|
1464 |
if (t != NULL) {
|
1465 |
t->expression = exp;
|
1466 |
t->file = file;
|
1467 |
t->line = line;
|
1468 |
}
|
1469 |
abort();
|
1470 |
}
|