2023-05-12 12:47:54 +00:00
|
|
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
|
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
|
|
|
│ │
|
|
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
|
|
│ │
|
|
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
2023-05-12 13:24:26 +00:00
|
|
|
#include "ape/sections.internal.h"
|
2023-05-13 09:41:41 +00:00
|
|
|
#include "libc/assert.h"
|
2023-05-12 12:47:54 +00:00
|
|
|
#include "libc/calls/calls.h"
|
2023-06-04 16:33:48 +00:00
|
|
|
#include "libc/calls/struct/aarch64.internal.h"
|
2023-05-12 12:47:54 +00:00
|
|
|
#include "libc/calls/struct/rusage.internal.h"
|
|
|
|
#include "libc/calls/struct/siginfo.h"
|
|
|
|
#include "libc/calls/struct/sigset.h"
|
|
|
|
#include "libc/calls/struct/sigset.internal.h"
|
|
|
|
#include "libc/calls/struct/utsname.h"
|
|
|
|
#include "libc/calls/syscall-sysv.internal.h"
|
|
|
|
#include "libc/calls/ucontext.h"
|
|
|
|
#include "libc/errno.h"
|
|
|
|
#include "libc/intrin/kprintf.h"
|
|
|
|
#include "libc/log/internal.h"
|
|
|
|
#include "libc/log/log.h"
|
|
|
|
#include "libc/macros.internal.h"
|
2023-07-25 12:43:04 +00:00
|
|
|
#include "libc/mem/mem.h"
|
2023-05-12 12:47:54 +00:00
|
|
|
#include "libc/nexgen32e/stackframe.h"
|
|
|
|
#include "libc/runtime/runtime.h"
|
|
|
|
#include "libc/runtime/stack.h"
|
|
|
|
#include "libc/runtime/symbols.internal.h"
|
2023-07-25 12:43:04 +00:00
|
|
|
#include "libc/stdio/stdio.h"
|
2023-05-12 12:47:54 +00:00
|
|
|
#include "libc/str/str.h"
|
|
|
|
#include "libc/sysv/consts/sig.h"
|
|
|
|
#include "libc/thread/thread.h"
|
|
|
|
#ifdef __aarch64__
|
|
|
|
|
|
|
|
STATIC_YOINK("strerror_wr"); // for kprintf %m
|
|
|
|
STATIC_YOINK("strsignal_r"); // for kprintf %G
|
|
|
|
|
|
|
|
#define RESET "\e[0m"
|
2023-06-04 15:19:45 +00:00
|
|
|
#define BOLD "\e[1m"
|
2023-05-12 12:47:54 +00:00
|
|
|
#define STRONG "\e[30;101m"
|
|
|
|
#define RED "\e[31;1m"
|
|
|
|
#define GREEN "\e[32;1m"
|
|
|
|
#define BLUE "\e[34;1m"
|
|
|
|
#define YELLOW "\e[33;1m"
|
|
|
|
#define MAGENTA "\e[35;1m"
|
|
|
|
|
|
|
|
struct Buffer {
|
|
|
|
char *p;
|
|
|
|
int n;
|
|
|
|
int i;
|
|
|
|
};
|
|
|
|
|
2023-05-12 13:24:26 +00:00
|
|
|
static bool IsCode(uintptr_t p) {
|
2023-05-19 02:05:08 +00:00
|
|
|
return __executable_start <= (uint8_t *)p && (uint8_t *)p < _etext;
|
2023-05-12 13:24:26 +00:00
|
|
|
}
|
|
|
|
|
2023-05-12 12:47:54 +00:00
|
|
|
static void Append(struct Buffer *b, const char *fmt, ...) {
|
|
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
|
|
b->i += kvsnprintf(b->p + b->i, b->n - b->i, fmt, va);
|
|
|
|
va_end(va);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *ColorRegister(int r) {
|
|
|
|
if (__nocolor) return "";
|
|
|
|
switch (r) {
|
|
|
|
case 0: // arg / res
|
|
|
|
case 1: // arg / res
|
|
|
|
case 2: // arg / res
|
|
|
|
case 3: // arg / res
|
|
|
|
case 4: // arg / res
|
|
|
|
case 5: // arg / res
|
|
|
|
case 6: // arg / res
|
|
|
|
case 7: // arg / res
|
|
|
|
return GREEN;
|
|
|
|
case 9: // volatile
|
|
|
|
case 10: // volatile
|
|
|
|
case 11: // volatile
|
|
|
|
case 12: // volatile
|
|
|
|
case 13: // volatile
|
|
|
|
case 14: // volatile
|
|
|
|
case 15: // volatile
|
|
|
|
return BLUE;
|
|
|
|
case 19: // saved
|
|
|
|
case 20: // saved
|
|
|
|
case 21: // saved
|
|
|
|
case 22: // saved
|
|
|
|
case 23: // saved
|
|
|
|
case 24: // saved
|
|
|
|
case 25: // saved
|
|
|
|
case 26: // saved
|
|
|
|
case 27: // saved
|
|
|
|
return MAGENTA;
|
2023-05-19 02:05:08 +00:00
|
|
|
case 18: // platform register
|
|
|
|
case 28: // our tls register
|
2023-05-12 12:47:54 +00:00
|
|
|
case 29: // frame pointer
|
|
|
|
case 30: // return pointer
|
|
|
|
case 31: // stack pointer
|
|
|
|
return RED;
|
|
|
|
default: // miscellaneous registers
|
|
|
|
return YELLOW;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool AppendFileLine(struct Buffer *b, const char *addr2line,
|
|
|
|
const char *debugbin, long addr) {
|
|
|
|
ssize_t rc;
|
2023-05-13 09:41:41 +00:00
|
|
|
char *p, *q, buf[128];
|
2023-05-12 12:47:54 +00:00
|
|
|
int j, k, ws, pid, pfd[2];
|
|
|
|
if (!debugbin || !*debugbin) return false;
|
|
|
|
if (!addr2line || !*addr2line) return false;
|
|
|
|
if (sys_pipe(pfd)) return false;
|
|
|
|
ksnprintf(buf, sizeof(buf), "%lx", addr);
|
|
|
|
if ((pid = vfork()) == -1) {
|
|
|
|
sys_close(pfd[1]);
|
|
|
|
sys_close(pfd[0]);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!pid) {
|
|
|
|
sys_close(pfd[0]);
|
|
|
|
sys_dup2(pfd[1], 1, 0);
|
|
|
|
sys_close(2);
|
|
|
|
__sys_execve(addr2line,
|
|
|
|
(char *const[]){addr2line, "-pifCe", debugbin, buf, 0},
|
|
|
|
(char *const[]){0});
|
|
|
|
_Exit(127);
|
|
|
|
}
|
|
|
|
sys_close(pfd[1]);
|
|
|
|
// copy addr2line stdout to buffer. normally it is "file:line\n".
|
|
|
|
// however additional lines can get created for inline functions.
|
|
|
|
j = b->i;
|
|
|
|
Append(b, "in ");
|
|
|
|
k = b->i;
|
|
|
|
while ((rc = sys_read(pfd[0], buf, sizeof(buf))) > 0) {
|
|
|
|
Append(b, "%.*s", (int)rc, buf);
|
|
|
|
}
|
2023-05-13 09:41:41 +00:00
|
|
|
// remove the annoying `foo.c:123 (discriminator 3)` suffixes, because
|
|
|
|
// they break emacs, and who on earth knows what those things mean lol
|
|
|
|
while ((p = memmem(b->p + k, b->i - k, " (discriminator ", 16)) &&
|
|
|
|
(q = memchr(p + 16, '\n', (b->p + b->i) - (p + 16)))) {
|
|
|
|
memmove(p, q, (b->p + b->i) - q);
|
|
|
|
b->i -= q - p;
|
|
|
|
}
|
|
|
|
// mop up after the process and sanity check captured text
|
2023-05-12 12:47:54 +00:00
|
|
|
sys_close(pfd[0]);
|
|
|
|
if (sys_wait4(pid, &ws, 0, 0) != -1 && !ws && b->p[k] != ':' &&
|
|
|
|
b->p[k] != '?' && b->p[b->i - 1] == '\n') {
|
|
|
|
--b->i; // chomp last newline
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
b->i = j; // otherwise reset the buffer
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-25 12:43:04 +00:00
|
|
|
static char *GetSymbolName(struct SymbolTable *st, int symbol, char **mem,
|
|
|
|
size_t *memsz) {
|
|
|
|
char *s, *t;
|
|
|
|
if ((s = __get_symbol_name(st, symbol)) && //
|
|
|
|
s[0] == '_' && s[1] == 'Z' && //
|
|
|
|
(t = __cxa_demangle(s, *mem, memsz, 0))) {
|
|
|
|
*mem = s = t;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2023-05-12 12:47:54 +00:00
|
|
|
relegated void __oncrash_arm64(int sig, struct siginfo *si, void *arg) {
|
|
|
|
char buf[10000];
|
2023-06-07 10:34:45 +00:00
|
|
|
ucontext_t *ctx = arg;
|
2023-05-12 12:47:54 +00:00
|
|
|
static _Thread_local bool once;
|
|
|
|
struct Buffer b[1] = {{buf, sizeof(buf)}};
|
|
|
|
b->p[b->i++] = '\n';
|
|
|
|
if (!once) {
|
|
|
|
int i, j;
|
|
|
|
const char *kind;
|
|
|
|
const char *reset;
|
|
|
|
const char *strong;
|
|
|
|
char host[64] = "unknown";
|
|
|
|
struct utsname names = {0};
|
|
|
|
once = true;
|
|
|
|
ftrace_enabled(-1);
|
|
|
|
strace_enabled(-1);
|
|
|
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
|
|
|
|
__restore_tty();
|
|
|
|
uname(&names);
|
|
|
|
gethostname(host, sizeof(host));
|
|
|
|
reset = !__nocolor ? RESET : "";
|
|
|
|
strong = !__nocolor ? STRONG : "";
|
2023-06-10 01:02:06 +00:00
|
|
|
if (ctx && (ctx->uc_mcontext.sp & (GetStackSize() - 1)) <= APE_GUARDSIZE) {
|
2023-05-12 12:47:54 +00:00
|
|
|
kind = "Stack Overflow";
|
|
|
|
} else {
|
|
|
|
kind = GetSiCodeName(sig, si->si_code);
|
|
|
|
}
|
|
|
|
Append(b,
|
|
|
|
"%serror%s: Uncaught %G (%s) on %s pid %d tid %d\n"
|
|
|
|
" %s\n"
|
|
|
|
" %m\n"
|
|
|
|
" %s %s %s %s\n",
|
|
|
|
strong, reset, sig, kind, host, getpid(), gettid(),
|
|
|
|
program_invocation_name, names.sysname, names.version,
|
2023-05-13 01:09:23 +00:00
|
|
|
names.nodename, names.release);
|
2023-05-12 12:47:54 +00:00
|
|
|
if (ctx) {
|
|
|
|
long pc;
|
|
|
|
char line[256];
|
2023-07-25 12:43:04 +00:00
|
|
|
char *mem = 0;
|
|
|
|
size_t memsz = 0;
|
2023-05-12 12:47:54 +00:00
|
|
|
int addend, symbol;
|
|
|
|
const char *debugbin;
|
|
|
|
const char *addr2line;
|
|
|
|
struct StackFrame *fp;
|
|
|
|
struct SymbolTable *st;
|
2023-06-04 15:19:45 +00:00
|
|
|
struct fpsimd_context *vc;
|
2023-05-12 12:47:54 +00:00
|
|
|
st = GetSymbolTable();
|
|
|
|
debugbin = FindDebugBinary();
|
|
|
|
addr2line = GetAddr2linePath();
|
|
|
|
|
|
|
|
if (ctx->uc_mcontext.fault_address) {
|
|
|
|
Append(b, " fault_address = %#lx\n", ctx->uc_mcontext.fault_address);
|
|
|
|
}
|
|
|
|
|
|
|
|
// PRINT REGISTERS
|
|
|
|
for (i = 0; i < 8; ++i) {
|
|
|
|
Append(b, " ");
|
|
|
|
for (j = 0; j < 4; ++j) {
|
|
|
|
int r = 8 * j + i;
|
|
|
|
if (j) Append(b, " ");
|
2023-06-04 15:19:45 +00:00
|
|
|
Append(b, "%s%016lx%s x%d%s", ColorRegister(r),
|
2023-06-03 15:12:13 +00:00
|
|
|
ctx->uc_mcontext.regs[r], reset, r,
|
|
|
|
r == 8 || r == 9 ? " " : "");
|
2023-05-12 12:47:54 +00:00
|
|
|
}
|
|
|
|
Append(b, "\n");
|
|
|
|
}
|
|
|
|
|
2023-06-04 15:19:45 +00:00
|
|
|
// PRINT VECTORS
|
|
|
|
vc = (struct fpsimd_context *)ctx->uc_mcontext.__reserved;
|
|
|
|
if (vc->head.magic == FPSIMD_MAGIC) {
|
|
|
|
int n = 16;
|
|
|
|
while (n && !vc->vregs[n - 1] && !vc->vregs[n - 2]) n -= 2;
|
|
|
|
for (i = 0; i * 2 < n; ++i) {
|
|
|
|
Append(b, " ");
|
|
|
|
for (j = 0; j < 2; ++j) {
|
|
|
|
int r = j + 2 * i;
|
|
|
|
if (j) Append(b, " ");
|
|
|
|
Append(b, "%016lx ..%s %016lx v%d%s", (long)(vc->vregs[r] >> 64),
|
|
|
|
!j ? "" : ".", (long)vc->vregs[r], r, r < 10 ? " " : "");
|
|
|
|
}
|
|
|
|
Append(b, "\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-12 12:47:54 +00:00
|
|
|
// PRINT CURRENT LOCATION
|
2023-06-03 15:12:13 +00:00
|
|
|
//
|
|
|
|
// We can get the address of the currently executing function by
|
|
|
|
// simply examining the program counter.
|
2023-05-12 12:47:54 +00:00
|
|
|
pc = ctx->uc_mcontext.pc;
|
|
|
|
Append(b, " %016lx sp %lx pc", ctx->uc_mcontext.sp, pc);
|
2023-06-03 20:48:52 +00:00
|
|
|
if (pc && st && (symbol = __get_symbol(st, pc))) {
|
2023-05-12 12:47:54 +00:00
|
|
|
addend = pc - st->addr_base;
|
|
|
|
addend -= st->symbols[symbol].x;
|
2023-07-25 12:43:04 +00:00
|
|
|
Append(b, " %s", GetSymbolName(st, symbol, &mem, &memsz));
|
2023-06-06 07:11:41 +00:00
|
|
|
if (addend) Append(b, "%+d", addend);
|
2023-05-12 12:47:54 +00:00
|
|
|
}
|
|
|
|
Append(b, "\n");
|
|
|
|
|
|
|
|
// PRINT LINKED LOCATION
|
2023-06-03 15:12:13 +00:00
|
|
|
//
|
|
|
|
// The x30 register can usually tell us the address of the parent
|
|
|
|
// function. This can help us determine the caller in cases where
|
|
|
|
// stack frames aren't being generated by the compiler; but if we
|
|
|
|
// have stack frames, then we need to ensure this won't duplicate
|
|
|
|
// the first element of the frame pointer backtrace below.
|
|
|
|
fp = (struct StackFrame *)ctx->uc_mcontext.regs[29];
|
2023-05-12 13:24:26 +00:00
|
|
|
if (IsCode((pc = ctx->uc_mcontext.regs[30]))) {
|
|
|
|
Append(b, " %016lx sp %lx lr", ctx->uc_mcontext.sp, pc);
|
2023-06-03 20:48:52 +00:00
|
|
|
if (pc && st && (symbol = __get_symbol(st, pc))) {
|
2023-05-12 13:24:26 +00:00
|
|
|
addend = pc - st->addr_base;
|
|
|
|
addend -= st->symbols[symbol].x;
|
|
|
|
Append(b, " ");
|
|
|
|
if (!AppendFileLine(b, addr2line, debugbin, pc)) {
|
2023-07-25 12:43:04 +00:00
|
|
|
Append(b, "%s", GetSymbolName(st, symbol, &mem, &memsz));
|
2023-05-12 13:24:26 +00:00
|
|
|
if (addend) Append(b, "%+d", addend);
|
|
|
|
}
|
2023-05-12 12:47:54 +00:00
|
|
|
}
|
2023-05-12 13:24:26 +00:00
|
|
|
Append(b, "\n");
|
2023-06-03 15:12:13 +00:00
|
|
|
if (fp && !kisdangerous(fp) && pc == fp->addr) {
|
|
|
|
fp = fp->next;
|
|
|
|
}
|
2023-05-12 12:47:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// PRINT FRAME POINTERS
|
2023-06-03 15:12:13 +00:00
|
|
|
//
|
|
|
|
// The prologues and epilogues of non-leaf functions should save
|
|
|
|
// the frame pointer (x29) and return address (x30) to the stack
|
|
|
|
// and then set x29 to sp, which is the address of the new frame
|
|
|
|
// effectively creating a daisy chain letting us trace back into
|
|
|
|
// the origin of execution, e.g. _start(), or sys_clone_linux().
|
2023-05-12 12:47:54 +00:00
|
|
|
for (i = 0; fp; fp = fp->next) {
|
|
|
|
if (kisdangerous(fp)) {
|
|
|
|
Append(b, " %016lx <dangerous fp>\n", fp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (++i == 100) {
|
|
|
|
Append(b, " <truncated backtrace>\n");
|
|
|
|
break;
|
|
|
|
}
|
2023-06-03 20:48:52 +00:00
|
|
|
if (st && (pc = fp->addr)) {
|
2023-05-12 12:47:54 +00:00
|
|
|
if ((symbol = __get_symbol(st, pc))) {
|
|
|
|
addend = pc - st->addr_base;
|
|
|
|
addend -= st->symbols[symbol].x;
|
|
|
|
} else {
|
|
|
|
addend = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
symbol = 0;
|
|
|
|
addend = 0;
|
|
|
|
}
|
|
|
|
Append(b, " %016lx fp %lx lr ", fp, pc);
|
2023-06-03 20:48:52 +00:00
|
|
|
if (!AppendFileLine(b, addr2line, debugbin, pc) && st) {
|
2023-07-25 12:43:04 +00:00
|
|
|
Append(b, "%s", GetSymbolName(st, symbol, &mem, &memsz));
|
2023-05-12 12:47:54 +00:00
|
|
|
if (addend) Append(b, "%+d", addend);
|
|
|
|
}
|
|
|
|
Append(b, "\n");
|
|
|
|
}
|
2023-07-25 12:43:04 +00:00
|
|
|
free(mem);
|
2023-05-12 12:47:54 +00:00
|
|
|
}
|
|
|
|
} else {
|
2023-06-07 10:34:45 +00:00
|
|
|
Append(b, "got %G while crashing! pc %lx lr %lx\n", sig,
|
|
|
|
ctx->uc_mcontext.pc, ctx->uc_mcontext.regs[30]);
|
2023-05-12 12:47:54 +00:00
|
|
|
}
|
|
|
|
sys_write(2, b->p, MIN(b->i, b->n));
|
2023-06-03 15:12:13 +00:00
|
|
|
__print_maps();
|
2023-05-12 12:47:54 +00:00
|
|
|
_Exit(128 + sig);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* __aarch64__ */
|