mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-14 10:18:02 +00:00
Actually Portable Executable now supports Android. Cosmo's old mmap code required a 47 bit address space. The new implementation is very agnostic and supports both smaller address spaces (e.g. embedded) and even modern 56-bit PML5T paging for x86 which finally came true on Zen4 Threadripper Cosmopolitan no longer requires UNIX systems to observe the Windows 64kb granularity; i.e. sysconf(_SC_PAGE_SIZE) will now report the host native page size. This fixes a longstanding POSIX conformance issue, concerning file mappings that overlap the end of file. Other aspects of conformance have been improved too, such as the subtleties of address assignment and and the various subtleties surrounding MAP_FIXED and MAP_FIXED_NOREPLACE On Windows, mappings larger than 100 megabytes won't be broken down into thousands of independent 64kb mappings. Support for MAP_STACK is removed by this change; please use NewCosmoStack() instead. Stack overflow avoidance is now being implemented using the POSIX thread APIs. Please use GetStackBottom() and GetStackAddr(), instead of the old error-prone GetStackAddr() and HaveStackMemory() APIs which are removed.
424 lines
14 KiB
C
424 lines
14 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│ vi: set et 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. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "ape/sections.internal.h"
|
|
#include "libc/assert.h"
|
|
#include "libc/atomic.h"
|
|
#include "libc/calls/blockcancel.internal.h"
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/struct/aarch64.internal.h"
|
|
#include "libc/calls/struct/rusage.internal.h"
|
|
#include "libc/calls/struct/sigaction.h"
|
|
#include "libc/calls/struct/siginfo.h"
|
|
#include "libc/calls/struct/sigset.h"
|
|
#include "libc/calls/struct/sigset.internal.h"
|
|
#include "libc/calls/struct/ucontext.internal.h"
|
|
#include "libc/calls/struct/utsname.h"
|
|
#include "libc/calls/syscall-sysv.internal.h"
|
|
#include "libc/calls/ucontext.h"
|
|
#include "libc/cosmo.h"
|
|
#include "libc/cxxabi.h"
|
|
#include "libc/dce.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/intrin/atomic.h"
|
|
#include "libc/intrin/describebacktrace.internal.h"
|
|
#include "libc/intrin/describeflags.internal.h"
|
|
#include "libc/intrin/kprintf.h"
|
|
#include "libc/log/internal.h"
|
|
#include "libc/log/log.h"
|
|
#include "libc/macros.internal.h"
|
|
#include "libc/nexgen32e/stackframe.h"
|
|
#include "libc/runtime/memtrack.internal.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/runtime/stack.h"
|
|
#include "libc/runtime/symbols.internal.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/sysv/consts/auxv.h"
|
|
#include "libc/sysv/consts/sig.h"
|
|
#include "libc/thread/thread.h"
|
|
#ifdef __aarch64__
|
|
|
|
#define STACK_ERROR "error: not enough room on stack to print crash report\n"
|
|
|
|
#define RESET "\e[0m"
|
|
#define BOLD "\e[1m"
|
|
#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;
|
|
};
|
|
|
|
static relegated bool IsCode(uintptr_t p) {
|
|
return __executable_start <= (uint8_t *)p && (uint8_t *)p < _etext;
|
|
}
|
|
|
|
static relegated 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 relegated 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;
|
|
case 18: // platform register
|
|
case 28: // our tls register
|
|
case 29: // frame pointer
|
|
case 30: // return pointer
|
|
case 31: // stack pointer
|
|
return RED;
|
|
default: // miscellaneous registers
|
|
return YELLOW;
|
|
}
|
|
}
|
|
|
|
static relegated bool AppendFileLine(struct Buffer *b, const char *addr2line,
|
|
const char *debugbin, long addr) {
|
|
ssize_t rc;
|
|
char *p, *q, buf[128];
|
|
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[]){(char *)addr2line, "-pifCe", (char *)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);
|
|
}
|
|
// 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
|
|
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;
|
|
}
|
|
}
|
|
|
|
static relegated char *GetSymbolName(struct SymbolTable *st, int symbol) {
|
|
char *str;
|
|
static char buf[8192];
|
|
if (!(str = __get_symbol_name(st, symbol)))
|
|
return str;
|
|
if (!__is_mangled(str))
|
|
return str;
|
|
__demangle(buf, str, sizeof(buf));
|
|
return buf;
|
|
}
|
|
|
|
static relegated void __oncrash_impl(int sig, siginfo_t *si, ucontext_t *ctx) {
|
|
#pragma GCC push_options
|
|
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
|
long size = __get_safe_size(10000, 4096);
|
|
if (size < 80) {
|
|
// almost certainly guaranteed to succeed
|
|
klog(STACK_ERROR, sizeof(STACK_ERROR) - 1);
|
|
__restore_tty();
|
|
__minicrash(sig, si, ctx);
|
|
return;
|
|
}
|
|
char *buf = alloca(size);
|
|
CheckLargeStackAllocation(buf, size);
|
|
#pragma GCC pop_options
|
|
int i, j;
|
|
const char *kind;
|
|
const char *reset;
|
|
const char *strong;
|
|
char host[64] = "unknown";
|
|
struct utsname names = {0};
|
|
struct Buffer b[1] = {{buf, size}};
|
|
b->p[b->i++] = '\n';
|
|
__restore_tty();
|
|
uname(&names);
|
|
gethostname(host, sizeof(host));
|
|
reset = !__nocolor ? RESET : "";
|
|
strong = !__nocolor ? STRONG : "";
|
|
if (__is_stack_overflow(si, ctx)) {
|
|
kind = "\e[7mStack Overflow\e[0m";
|
|
} else {
|
|
kind = DescribeSiCode(sig, si->si_code);
|
|
}
|
|
Append(b, "%serror%s: Uncaught %G (%s) on %s pid %d tid %d\n", strong, reset,
|
|
sig, kind, host, getpid(), gettid());
|
|
if (program_invocation_name) {
|
|
Append(b, " %s\n", __program_executable_name);
|
|
}
|
|
if (errno) {
|
|
Append(b, " %s\n", strerror(errno));
|
|
}
|
|
Append(b, " %s %s %s %s\n", names.sysname, names.version, names.nodename,
|
|
names.release);
|
|
Append(
|
|
b, " cosmoaddr2line %s %lx %s\n", FindDebugBinary(),
|
|
ctx ? ctx->uc_mcontext.PC : 0,
|
|
DescribeBacktrace(ctx ? (struct StackFrame *)ctx->uc_mcontext.BP
|
|
: (struct StackFrame *)__builtin_frame_address(0)));
|
|
if (ctx) {
|
|
long pc;
|
|
int addend, symbol;
|
|
const char *debugbin;
|
|
const char *addr2line;
|
|
struct StackFrame *fp;
|
|
struct SymbolTable *st;
|
|
struct fpsimd_context *vc;
|
|
|
|
st = GetSymbolTable();
|
|
debugbin = FindDebugBinary();
|
|
addr2line = GetAddr2linePath();
|
|
|
|
if (sig == SIGFPE || //
|
|
sig == SIGILL || //
|
|
sig == SIGBUS || //
|
|
sig == SIGSEGV || //
|
|
sig == SIGTRAP) {
|
|
Append(b, " faulting address is %016lx\n", si->si_addr);
|
|
}
|
|
|
|
// 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, " ");
|
|
Append(b, "%s%016lx%s x%d%s", ColorRegister(r),
|
|
ctx->uc_mcontext.regs[r], reset, r, r == 8 || r == 9 ? " " : "");
|
|
}
|
|
Append(b, "\n");
|
|
}
|
|
|
|
// 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");
|
|
}
|
|
}
|
|
|
|
// PRINT CURRENT LOCATION
|
|
//
|
|
// We can get the address of the currently executing function by
|
|
// simply examining the program counter.
|
|
pc = ctx->uc_mcontext.pc;
|
|
Append(b, " %016lx sp %lx pc", ctx->uc_mcontext.sp, pc);
|
|
if (pc && st && (symbol = __get_symbol(st, pc))) {
|
|
addend = pc - st->addr_base;
|
|
addend -= st->symbols[symbol].x;
|
|
Append(b, " ");
|
|
if (!AppendFileLine(b, addr2line, debugbin, pc)) {
|
|
Append(b, "%s", GetSymbolName(st, symbol));
|
|
if (addend)
|
|
Append(b, "%+d", addend);
|
|
}
|
|
}
|
|
Append(b, "\n");
|
|
|
|
// PRINT LINKED LOCATION
|
|
//
|
|
// 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];
|
|
if (IsCode((pc = ctx->uc_mcontext.regs[30]))) {
|
|
Append(b, " %016lx sp %lx lr", ctx->uc_mcontext.sp, pc);
|
|
if (pc && st && (symbol = __get_symbol(st, pc))) {
|
|
addend = pc - st->addr_base;
|
|
addend -= st->symbols[symbol].x;
|
|
Append(b, " ");
|
|
if (!AppendFileLine(b, addr2line, debugbin, pc)) {
|
|
Append(b, "%s", GetSymbolName(st, symbol));
|
|
if (addend)
|
|
Append(b, "%+d", addend);
|
|
}
|
|
}
|
|
Append(b, "\n");
|
|
if (fp && !kisdangerous(fp) && pc == fp->addr) {
|
|
fp = fp->next;
|
|
}
|
|
}
|
|
|
|
// PRINT FRAME POINTERS
|
|
//
|
|
// 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().
|
|
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;
|
|
}
|
|
if (st && (pc = fp->addr)) {
|
|
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);
|
|
if (!AppendFileLine(b, addr2line, debugbin, pc) && st) {
|
|
Append(b, "%s", GetSymbolName(st, symbol));
|
|
if (addend)
|
|
Append(b, "%+d", addend);
|
|
}
|
|
Append(b, "\n");
|
|
}
|
|
}
|
|
b->p[b->n - 1] = '\n';
|
|
klog(b->p, MIN(b->i, b->n));
|
|
}
|
|
|
|
static inline void SpinLock(atomic_uint *lock) {
|
|
int x;
|
|
for (;;) {
|
|
x = atomic_exchange_explicit(lock, 1, memory_order_acquire);
|
|
if (!x)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline void SpinUnlock(atomic_uint *lock) {
|
|
atomic_store_explicit(lock, 0, memory_order_release);
|
|
}
|
|
|
|
relegated void __oncrash(int sig, siginfo_t *si, void *arg) {
|
|
static atomic_uint lock;
|
|
ftrace_enabled(-1);
|
|
strace_enabled(-1);
|
|
BLOCK_CANCELATION;
|
|
SpinLock(&lock);
|
|
__oncrash_impl(sig, si, arg);
|
|
|
|
// unlike amd64, the instruction pointer on arm64 isn't advanced past
|
|
// the debugger breakpoint instruction automatically. we need this so
|
|
// execution can resume after __builtin_trap().
|
|
if (arg && sig == SIGTRAP)
|
|
((ucontext_t *)arg)->uc_mcontext.PC += 4;
|
|
|
|
// ensure execution doesn't resume for anything but SIGTRAP / SIGQUIT
|
|
if (arg && sig != SIGTRAP && sig != SIGQUIT) {
|
|
if (!IsXnu()) {
|
|
sigaddset(&((ucontext_t *)arg)->uc_sigmask, sig);
|
|
} else {
|
|
sigdelset(&((ucontext_t *)arg)->uc_sigmask, sig);
|
|
struct sigaction sa;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_handler = SIG_DFL;
|
|
sa.sa_flags = 0;
|
|
sigaction(sig, &sa, 0);
|
|
}
|
|
}
|
|
|
|
SpinUnlock(&lock);
|
|
ALLOW_CANCELATION;
|
|
strace_enabled(+1);
|
|
ftrace_enabled(+1);
|
|
}
|
|
|
|
#endif /* __aarch64__ */
|