mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Improve backtraces
We're now able to rewind the instruction pointer in x86 backtraces. This helps ensure addr2line cannot print information about unrelated adjacent code. I've restored -fno-schedule-insns2 in most cases because it really does cause unpredictable breakage for backtraces.
This commit is contained in:
parent
cd672e251f
commit
9b6718ac99
11 changed files with 118 additions and 24 deletions
|
@ -60,6 +60,7 @@ TMPSAFE = $(TMPDIR)/
|
|||
endif
|
||||
|
||||
BACKTRACES = \
|
||||
-fno-schedule-insns2 \
|
||||
-fno-optimize-sibling-calls \
|
||||
-mno-omit-leaf-frame-pointer
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -26,6 +27,13 @@
|
|||
* o//examples/crashreport.com
|
||||
*/
|
||||
|
||||
int Divide(int x, int y) {
|
||||
volatile int z = 0; // force creation of stack frame
|
||||
return x / y + z;
|
||||
}
|
||||
|
||||
int (*pDivide)(int, int) = Divide;
|
||||
|
||||
dontubsan int main(int argc, char *argv[]) {
|
||||
kprintf("----------------\n");
|
||||
kprintf(" THIS IS A TEST \n");
|
||||
|
@ -34,12 +42,7 @@ dontubsan int main(int argc, char *argv[]) {
|
|||
|
||||
ShowCrashReports();
|
||||
|
||||
volatile double a = 0;
|
||||
volatile double b = 23;
|
||||
volatile double c = exp(b) / a;
|
||||
(void)c;
|
||||
|
||||
volatile int x = 0;
|
||||
volatile int y = 1 / x;
|
||||
return y;
|
||||
pDivide(1, 0);
|
||||
pDivide(2, 0);
|
||||
pDivide(3, 0);
|
||||
}
|
||||
|
|
|
@ -539,16 +539,13 @@ typedef struct {
|
|||
#ifdef __x86_64__
|
||||
#define notpossible \
|
||||
do { \
|
||||
__asm__("nop\n\t" \
|
||||
"ud2\n\t" \
|
||||
"nop"); \
|
||||
__asm__("ud2"); \
|
||||
__builtin_unreachable(); \
|
||||
} while (0)
|
||||
#elif defined(__aarch64__)
|
||||
#define notpossible \
|
||||
do { \
|
||||
__asm__("udf\t#0\n\t" \
|
||||
"nop"); \
|
||||
__asm__("udf\t#0"); \
|
||||
__builtin_unreachable(); \
|
||||
} while (0)
|
||||
#else
|
||||
|
|
|
@ -17,34 +17,66 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/iscall.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/nexgen32e/stackframe.h"
|
||||
|
||||
#define N 160
|
||||
|
||||
static bool IsDangerous(const void *ptr) {
|
||||
if (_weaken(kisdangerous))
|
||||
return _weaken(kisdangerous)(ptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
static char *FormatHex(char *p, unsigned long x) {
|
||||
int k = x ? (__builtin_clzl(x) ^ 63) + 1 : 1;
|
||||
k = (k + 3) & -4;
|
||||
while (k > 0)
|
||||
*p++ = "0123456789abcdef"[(x >> (k -= 4)) & 15];
|
||||
*p = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
dontinstrument const char *(DescribeBacktrace)(char buf[N],
|
||||
struct StackFrame *fr) {
|
||||
const struct StackFrame *fr) {
|
||||
char *p = buf;
|
||||
char *pe = p + N;
|
||||
bool gotsome = false;
|
||||
while (fr) {
|
||||
if (_weaken(kisdangerous) && _weaken(kisdangerous)(fr)) {
|
||||
if (IsDangerous(fr)) {
|
||||
if (p + 1 + 1 + 1 < pe) {
|
||||
if (gotsome)
|
||||
*p++ = ' ';
|
||||
*p = '!';
|
||||
if (p + 16 + 1 < pe) {
|
||||
*p++ = ' ';
|
||||
p = FormatHex(p, (long)fr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (p + 16 + 1 + 1 <= pe) {
|
||||
if (gotsome) {
|
||||
if (p + 16 + 1 < pe) {
|
||||
unsigned char *ip = (unsigned char *)fr->addr;
|
||||
#ifdef __x86_64__
|
||||
// x86 advances the progrem counter before an instruction
|
||||
// begins executing. return addresses in backtraces shall
|
||||
// point to code after the call, which means addr2line is
|
||||
// going to print unrelated code unless we fixup the addr
|
||||
if (!IsDangerous(ip))
|
||||
ip -= __is_call(ip);
|
||||
#endif
|
||||
if (gotsome)
|
||||
*p++ = ' ';
|
||||
} else {
|
||||
else
|
||||
gotsome = true;
|
||||
}
|
||||
p = __hexcpy(p, fr->addr);
|
||||
p = FormatHex(p, (long)ip);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
fr = fr->next;
|
||||
}
|
||||
*p = 0;
|
||||
*p = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "libc/nexgen32e/stackframe.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
const char *DescribeBacktrace(char[160], struct StackFrame *) libcesque;
|
||||
const char *DescribeBacktrace(char[160], const struct StackFrame *) libcesque;
|
||||
#define DescribeBacktrace(x) DescribeBacktrace(alloca(160), x)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
|
|
37
libc/intrin/iscall.c
Normal file
37
libc/intrin/iscall.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*-*- 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 2024 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/intrin/iscall.internal.h"
|
||||
|
||||
// returns true if `p` is preceded by x86 call instruction
|
||||
// this is actually impossible to do but we'll do our best
|
||||
dontinstrument int __is_call(const unsigned char *p) {
|
||||
if (p[-5] == 0xe8)
|
||||
return 5; // call Jvds
|
||||
if (p[-2] == 0xff && (p[-1] & 070) == 020)
|
||||
return 2; // call %reg
|
||||
if (p[-4] == 0xff && (p[-3] & 070) == 020)
|
||||
return 4; // call disp8(%reg,%reg)
|
||||
if (p[-3] == 0xff && (p[-2] & 070) == 020)
|
||||
return 3; // call disp8(%reg)
|
||||
if (p[-7] == 0xff && (p[-6] & 070) == 020)
|
||||
return 7; // call disp32(%reg,%reg)
|
||||
if (p[-6] == 0xff && (p[-5] & 070) == 020)
|
||||
return 6; // call disp32(%reg)
|
||||
return 0;
|
||||
}
|
10
libc/intrin/iscall.internal.h
Normal file
10
libc/intrin/iscall.internal.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_ISCALL_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_ISCALL_H_
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
// returns true if `p` is preceded by x86 call instruction
|
||||
// this is actually impossible to do but we'll do our best
|
||||
int __is_call(const unsigned char *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_ISCALL_H_ */
|
|
@ -24,6 +24,8 @@
|
|||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/iscall.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/promises.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
|
@ -112,6 +114,8 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
|
|||
--gi;
|
||||
} while ((addr = garbage->p[gi].ret) == (uintptr_t)_weaken(__gc));
|
||||
}
|
||||
if (!kisdangerous((const unsigned char *)addr))
|
||||
addr -= __is_call((const unsigned char *)addr);
|
||||
#endif
|
||||
argv[i++] = buf + j;
|
||||
buf[j++] = '0';
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/cosmo.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/iscall.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
|
@ -76,6 +77,8 @@ dontinstrument dontasan int PrintBacktraceUsingSymbols(
|
|||
--gi;
|
||||
} while ((addr = garbage->p[gi].ret) == (intptr_t)_weaken(__gc));
|
||||
}
|
||||
if (!kisdangerous((const unsigned char *)addr))
|
||||
addr -= __is_call((const unsigned char *)addr);
|
||||
#endif
|
||||
if (addr) {
|
||||
if ((symbol = __get_symbol(st, addr)) != -1) {
|
||||
|
|
|
@ -162,7 +162,7 @@ for x; do
|
|||
# tail calls — may opt not to create an entry in this list. As a
|
||||
# result, stack traces are always meaningful, even without debug
|
||||
# information."
|
||||
x="-momit-leaf-frame-pointer -foptimize-sibling-calls"
|
||||
x="-momit-leaf-frame-pointer -foptimize-sibling-calls -fschedule-insns2"
|
||||
elif [ x"$x" = x"-r" ] ||
|
||||
[ x"$x" = x"-S" ] ||
|
||||
[ x"$x" = x"-pie" ] ||
|
||||
|
@ -241,9 +241,13 @@ CFLAGS="-fportcosmo -fno-dwarf2-cfi-asm -fno-unwind-tables -fno-asynchronous-unw
|
|||
LDFLAGS="-static -nostdlib -no-pie -fuse-ld=bfd -Wl,-z,noexecstack -Wl,-z,norelro -Wl,--gc-sections"
|
||||
PRECIOUS="-fno-omit-frame-pointer"
|
||||
|
||||
# these features screw with backtraces so avoid them
|
||||
if [ x"$OPT" != x"-Os" ] && [ x"$MODE" != x"tiny" ]; then
|
||||
CFLAGS="$CFLAGS -fno-optimize-sibling-calls -mno-omit-leaf-frame-pointer"
|
||||
fi
|
||||
if [ x"$OPT" != x"-O3" ]; then
|
||||
CFLAGS="$CFLAGS -fno-schedule-insns2"
|
||||
fi
|
||||
|
||||
CC_X86_64="$BIN/x86_64-linux-cosmo-gcc"
|
||||
CC_AARCH64="$BIN/aarch64-linux-cosmo-gcc"
|
||||
|
|
|
@ -200,6 +200,9 @@ if [ x"$OPT" != x"-Os" ] && # $OPT != "-Os"
|
|||
[ x"$MODE" != x"${MODE%tiny}" ]; then # endswith($MODE, "tiny")
|
||||
CFLAGS="$CFLAGS -fno-optimize-sibling-calls -mno-omit-leaf-frame-pointer"
|
||||
fi
|
||||
if [ x"$OPT" != x"-O3" ]; then
|
||||
CFLAGS="$CFLAGS -fno-schedule-insns2"
|
||||
fi
|
||||
|
||||
if [ $INTENT = cpp ]; then
|
||||
set -- "$CC" $PLATFORM $CPPFLAGS "$@"
|
||||
|
|
Loading…
Reference in a new issue