cosmopolitan/libc/log/backtrace2.c
Justine Tunney ae0ee59614
Get aarch64 hello world working
$ m=aarch64-tiny
    $ make -j8 m=$m o/$m/tool/hello/hello.com o/third_party/qemu/qemu-aarch64
    $ o/third_party/qemu/qemu-aarch64 o/$m/tool/hello/hello.com
    hello world
    $ ls -hal o/$m/tool/hello/hello.com
    -rwxr-xr-x 1 jart jart 4.0K May  9 05:04 o/aarch64-tiny/tool/hello/hello.com
2023-05-10 04:20:47 -07:00

200 lines
6.7 KiB
C

/*-*- 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 2020 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. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/blockcancel.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/rusage.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/promises.internal.h"
#include "libc/intrin/weaken.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/color.internal.h"
#include "libc/log/log.h"
#include "libc/nexgen32e/gc.internal.h"
#include "libc/nexgen32e/stackframe.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/thread/tls.h"
#define kBacktraceMaxFrames 128
#define kBacktraceBufSize ((kBacktraceMaxFrames - 1) * (18 + 1))
static void ShowHint(const char *s) {
kprintf("%snote: %s%s\n", SUBTLE, s, RESET);
}
static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
ssize_t got;
intptr_t addr;
size_t i, j, gi;
int ws, pid, pipefds[2];
struct Garbages *garbage;
const struct StackFrame *frame;
char *debugbin, *p1, *p2, *p3, *addr2line;
char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames];
// DWARF is a weak standard. Platforms that use LLVM or old GNU
// usually can't be counted upon to print backtraces correctly.
if (!IsLinux() && !IsWindows()) {
ShowHint("won't print addr2line backtrace because probably llvm");
return -1;
}
if (!PLEDGED(STDIO) || !PLEDGED(EXEC) || !PLEDGED(EXEC)) {
ShowHint("won't print addr2line backtrace because pledge");
return -1;
}
if (!(debugbin = FindDebugBinary())) {
ShowHint("won't print addr2line backtrace because no debug binary");
return -1;
}
if (!(addr2line = GetAddr2linePath())) {
if (IsLinux()) {
ShowHint("can't find addr2line on path or in ADDR2LINE");
}
return -1;
}
// backtrace_test.com failing on windows for some reason via runitd
// don't want to pull in the high-level syscalls here anyway
if (IsWindows()) {
return -1;
}
// doesn't work on rhel5
if (IsLinux() && !__is_linux_2_6_23()) {
return -1;
}
i = 0;
j = 0;
argv[i++] = "addr2line";
argv[i++] = "-a"; /* filter out w/ shell script wrapper for old versions */
argv[i++] = "-pCife";
argv[i++] = debugbin;
garbage = __tls_enabled ? __get_tls()->tib_garbages : 0;
gi = garbage ? garbage->i : 0;
for (frame = bp; frame && i < kBacktraceMaxFrames - 1; frame = frame->next) {
if (kisdangerous(frame)) {
return -1;
}
addr = frame->addr;
if (addr == (uintptr_t)_weaken(__gc)) {
do {
--gi;
} while ((addr = garbage->p[gi].ret) == (uintptr_t)_weaken(__gc));
}
argv[i++] = buf + j;
buf[j++] = '0';
buf[j++] = 'x';
j += uint64toarray_radix16(addr - 1, buf + j) + 1;
}
argv[i++] = NULL;
if (sys_pipe2(pipefds, O_CLOEXEC) == -1) {
return -1;
}
if ((pid = __sys_fork().ax) == -1) {
sys_close(pipefds[0]);
sys_close(pipefds[1]);
return -1;
}
if (!pid) {
sys_dup2(pipefds[1], 1);
sys_execve(addr2line, argv, environ);
_Exit(127);
}
sys_close(pipefds[1]);
for (;;) {
got = sys_read(pipefds[0], buf, kBacktraceBufSize);
if (!got) break;
if (got == -1 && errno == EINTR) {
errno = 0;
continue;
}
if (got == -1) {
kprintf("error reading backtrace %m\n");
break;
}
p1 = buf;
p3 = p1 + got;
for (got = p3 - buf, p1 = buf; got;) {
if ((p2 = memmem(p1, got, " (discriminator ",
strlen(" (discriminator ") - 1)) &&
(p3 = memchr(p2, '\n', got - (p2 - p1)))) {
if (p3 > p2 && p3[-1] == '\r') --p3;
sys_write(2, p1, p2 - p1);
got -= p3 - p1;
p1 += p3 - p1;
} else {
sys_write(2, p1, got);
break;
}
}
}
sys_close(pipefds[0]);
while (sys_wait4(pid, &ws, 0, 0) == -1) {
if (errno == EINTR) continue;
return -1;
}
if (WIFEXITED(ws) && !WEXITSTATUS(ws)) {
return 0;
} else {
return -1;
}
}
static int PrintBacktrace(int fd, const struct StackFrame *bp) {
#if !defined(DWARFLESS)
if (!IsTiny() && !__isworker) {
if (PrintBacktraceUsingAddr2line(fd, bp) != -1) {
return 0;
}
}
#else
ShowHint("won't print addr2line backtrace because no dwarf");
#endif
return PrintBacktraceUsingSymbols(fd, bp, GetSymbolTable());
}
void ShowBacktrace(int fd, const struct StackFrame *bp) {
BLOCK_CANCELLATIONS;
#ifdef __FNO_OMIT_FRAME_POINTER__
/* asan runtime depends on this function */
ftrace_enabled(-1);
strace_enabled(-1);
if (!bp) bp = __builtin_frame_address(0);
PrintBacktrace(fd, bp);
strace_enabled(+1);
ftrace_enabled(+1);
#else
(fprintf)(stderr, "ShowBacktrace() needs these flags to show C backtrace:\n"
"\t-D__FNO_OMIT_FRAME_POINTER__\n"
"\t-fno-omit-frame-pointer\n");
#endif
ALLOW_CANCELLATIONS;
}