diff --git a/ape/ape-m1.c b/ape/ape-m1.c index 9fdcc885e88..21869f2bd97 100644 --- a/ape/ape-m1.c +++ b/ape/ape-m1.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,6 @@ #include #include #include -#include #define pagesz 16384 #define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24) @@ -89,6 +89,8 @@ struct Syslib { long (*sem_post)(int *); long (*sem_wait)(int *); long (*sem_trywait)(int *); + long (*getrlimit)(int, struct rlimit *); + long (*setrlimit)(int, const struct rlimit *); }; #define ELFCLASS32 1 @@ -814,7 +816,8 @@ static long sys_getentropy(void *buf, size_t buflen) { return sysret(getentropy(buf, buflen)); } -static long sys_sem_open(const char *name, int oflags, mode_t mode, unsigned value) { +static long sys_sem_open(const char *name, int oflags, mode_t mode, + unsigned value) { return sysret((long)sem_open(name, oflags, mode, value)); } @@ -838,6 +841,14 @@ static long sys_sem_trywait(sem_t *sem) { return sysret(sem_trywait(sem)); } +static long sys_getrlimit(int which, struct rlimit *rlim) { + return sysret(getrlimit(which, rlim)); +} + +static long sys_setrlimit(int which, const struct rlimit *rlim) { + return sysret(setrlimit(which, rlim)); +} + static long sys_write(int fd, const void *data, size_t size) { return sysret(write(fd, data, size)); } @@ -930,6 +941,8 @@ int main(int argc, char **argv, char **envp) { M->lib.sem_post = sys_sem_post; M->lib.sem_wait = sys_sem_wait; M->lib.sem_trywait = sys_sem_trywait; + M->lib.getrlimit = sys_getrlimit; + M->lib.setrlimit = sys_setrlimit; /* getenv("_") is close enough to at_execfn */ execfn = argc > 0 ? argv[0] : 0; diff --git a/execve_test_prog1.com b/execve_test_prog1.com deleted file mode 100755 index 0a28489305e..00000000000 Binary files a/execve_test_prog1.com and /dev/null differ diff --git a/libc/calls/clock_nanosleep.c b/libc/calls/clock_nanosleep.c index 3db418207f6..987d0bbdb3d 100644 --- a/libc/calls/clock_nanosleep.c +++ b/libc/calls/clock_nanosleep.c @@ -100,7 +100,7 @@ static int cosmo_clock_nanosleep(int clock, int flags, if (timespec_cmp(remain, quantum) > 0) { waitfor = timespec_sub(remain, quantum); if (sys_clock_nanosleep(sleep_clock, 0, &waitfor, rem) == -1) { - if (rem && errno == EINTR) { + if (!flags && rem && errno == EINTR) { *rem = timespec_add(*rem, quantum); } return -1; diff --git a/libc/calls/diagnose_syscall.S b/libc/calls/diagnose_syscall.S deleted file mode 100644 index b8319e94405..00000000000 --- a/libc/calls/diagnose_syscall.S +++ /dev/null @@ -1,110 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2022 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/macros.internal.h" -.privileged - -diagnose_syscall: - push %rbp - mov %rsp,%rbp - push %rbx - push %r12 - push %r13 - push %r14 - push %r15 - - mov $0x7fffffff,%eax - add $4,%eax # set sf/of/pf - - mov %rdi,%rax # nr - mov %rsi,%rdi # arg 1 - mov %rdx,%rsi # arg 2 - mov %rcx,%rdx # arg 3 - mov %r8,%r10 # arg 4 - mov %r9,%r8 # arg 5 - mov 16(%rbp),%r9 # arg 6 - push 24(%rbp) # arg 7 - push %rax # fake ret addr - mov 32(%rbp),%r12 # ucontext before - mov 40(%rbp),%r13 # ucontext after - xor %ecx,%ecx - xor %r11d,%r11d - mov $0x5555555555555555,%r11 - mov $0x5555555555555555,%r14 - mov $0x5555555555555555,%r15 - mov $0x5555555555555555,%rbx - -// save machine state before system call - pushf - pop 176(%r12) - mov %r8,40(%r12) - mov %r9,48(%r12) - mov %r10,56(%r12) - mov %r11,64(%r12) - mov %r12,72(%r12) - mov %r13,80(%r12) - mov %r14,88(%r12) - mov %r15,96(%r12) - mov %rdi,104(%r12) - mov %rsi,112(%r12) - mov %rbp,120(%r12) - mov %rbx,128(%r12) - mov %rdx,136(%r12) - mov %rax,144(%r12) - mov %rcx,152(%r12) - push %rbx - lea 320(%r12),%rbx - mov %rbx,224(%r12) # set fpregs ptr - pop %rbx - - syscall - -// save machine state after system call - pushf - pop 176(%r13) - mov %r8,40(%r13) - mov %r9,48(%r13) - mov %r10,56(%r13) - mov %r11,64(%r13) - mov %r12,72(%r13) - mov %r13,80(%r13) - mov %r14,88(%r13) - mov %r15,96(%r13) - mov %rdi,104(%r13) - mov %rsi,112(%r13) - mov %rbp,120(%r13) - mov %rbx,128(%r13) - mov %rdx,136(%r13) - mov %rax,144(%r13) - mov %rcx,152(%r13) - push %rbx - lea 320(%r13),%rbx - mov %rbx,224(%r13) # set fpregs ptr - pop %rbx - - pop %r13 - pop %r13 - - pop %r15 - pop %r14 - pop %r13 - pop %r12 - pop %rbx - pop %rbp - ret - .endfn diagnose_syscall,globl diff --git a/libc/calls/getrlimit.c b/libc/calls/getrlimit.c index de1c8466858..4ba4ea41d7f 100644 --- a/libc/calls/getrlimit.c +++ b/libc/calls/getrlimit.c @@ -18,11 +18,13 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/struct/rlimit.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/runtime/stack.h" +#include "libc/runtime/syslib.internal.h" #include "libc/sysv/consts/rlimit.h" #include "libc/sysv/errfuns.h" @@ -40,6 +42,8 @@ int getrlimit(int resource, struct rlimit *rlim) { rc = einval(); } else if (!rlim || (IsAsan() && !__asan_is_valid(rlim, sizeof(*rlim)))) { rc = efault(); + } else if (IsXnuSilicon()) { + rc = _sysret(__syslib->__getrlimit(resource, rlim)); } else if (!IsWindows()) { rc = sys_getrlimit(resource, rlim); } else if (resource == RLIMIT_STACK) { diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index 929793a6845..245e2e2b75d 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -342,23 +342,26 @@ static textwindows int ProcessMouseEvent(const struct NtInputRecord *r, bs &= kNtFromLeft1stButtonPressed | kNtRightmostButtonPressed; if (ev & kNtMouseWheeled) { // scroll wheel (unnatural mode) - if (!(r->Event.MouseEvent.dwControlKeyState & - (kNtShiftPressed | kNtLeftCtrlPressed | kNtRightCtrlPressed | - kNtLeftAltPressed | kNtRightAltPressed))) { - bool isup = ((int)r->Event.MouseEvent.dwButtonState >> 16) > 0; - if (__ttyconf.magic & kTtyXtMouse) { + bool isup = ((int)r->Event.MouseEvent.dwButtonState >> 16) > 0; + if (__ttyconf.magic & kTtyXtMouse) { + if (r->Event.MouseEvent.dwControlKeyState & + (kNtLeftCtrlPressed | kNtRightCtrlPressed)) { e = isup ? 80 : 81; - goto OutputXtermMouseEvent; } else { - // we disable mouse highlighting when the tty is put in raw mode - // to mouse wheel events with widely understood vt100 arrow keys - *p++ = 033; - *p++ = !__keystroke.ohno_decckm ? '[' : 'O'; - if (isup) { - *p++ = 'A'; - } else { - *p++ = 'B'; - } + e = isup ? 64 : 65; + } + goto OutputXtermMouseEvent; + } else if (!(r->Event.MouseEvent.dwControlKeyState & + (kNtShiftPressed | kNtLeftCtrlPressed | kNtRightCtrlPressed | + kNtLeftAltPressed | kNtRightAltPressed))) { + // we disable mouse highlighting when the tty is put in raw mode + // to mouse wheel events with widely understood vt100 arrow keys + *p++ = 033; + *p++ = !__keystroke.ohno_decckm ? '[' : 'O'; + if (isup) { + *p++ = 'A'; + } else { + *p++ = 'B'; } } } else if ((bs || currentbs) && (__ttyconf.magic & kTtyXtMouse)) { diff --git a/libc/calls/setrlimit.c b/libc/calls/setrlimit.c index 91886c7f351..2883c5f72ae 100644 --- a/libc/calls/setrlimit.c +++ b/libc/calls/setrlimit.c @@ -19,11 +19,13 @@ #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/struct/rlimit.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/macros.internal.h" +#include "libc/runtime/syslib.internal.h" #include "libc/sysv/consts/rlimit.h" #include "libc/sysv/errfuns.h" @@ -79,6 +81,8 @@ int setrlimit(int resource, const struct rlimit *rlim) { rc = einval(); } else if (!rlim || (IsAsan() && !__asan_is_valid(rlim, sizeof(*rlim)))) { rc = efault(); + } else if (IsXnuSilicon()) { + rc = _sysret(__syslib->__setrlimit(resource, rlim)); } else if (!IsWindows()) { rc = sys_setrlimit(resource, rlim); if (IsXnu() && !rc && resource == RLIMIT_AS) { diff --git a/libc/runtime/syslib.internal.h b/libc/runtime/syslib.internal.h index 3f3053b24e8..53b7051ff7c 100644 --- a/libc/runtime/syslib.internal.h +++ b/libc/runtime/syslib.internal.h @@ -68,6 +68,8 @@ struct Syslib { long (*__sem_post)(int *); long (*__sem_wait)(int *); long (*__sem_trywait)(int *); + long (*__getrlimit)(int, void *); + long (*__setrlimit)(int, const void *); }; extern struct Syslib *__syslib; diff --git a/libc/thread/tls.h b/libc/thread/tls.h index 28fba51e838..bc94e78f270 100644 --- a/libc/thread/tls.h +++ b/libc/thread/tls.h @@ -3,8 +3,7 @@ #define TLS_ALIGNMENT 64 -#define TIB_FLAG_VFORKED 1 -#define TIB_FLAG_WINCRASHING 2 +#define TIB_FLAG_VFORKED 1 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/test/libc/calls/clock_nanosleep_test.c b/test/libc/calls/clock_nanosleep_test.c index 775fe15ed41..7ae79174b67 100644 --- a/test/libc/calls/clock_nanosleep_test.c +++ b/test/libc/calls/clock_nanosleep_test.c @@ -32,10 +32,6 @@ #include "libc/testlib/testlib.h" #include "libc/time/time.h" -void SetUpOnce(void) { - if (!IsWindows()) _Exit(0); -} - void OnAlrm(int sig) { // do nothing STRACE("OnAlrm()"); diff --git a/test/libc/calls/stackoverflow1_test.c b/test/libc/calls/stackoverflow1_test.c new file mode 100644 index 00000000000..d0205797897 --- /dev/null +++ b/test/libc/calls/stackoverflow1_test.c @@ -0,0 +1,108 @@ +/*-*- 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/rlimit.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigaltstack.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/dce.h" +#include "libc/intrin/kprintf.h" +#include "libc/limits.h" +#include "libc/mem/gc.internal.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/sysconf.h" +#include "libc/sysv/consts/rlimit.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/ss.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" + +/** + * stack overflow recovery technique #1 + * overflow the gigantic main process stack + * simple but it can upset kernels / libraries + */ + +jmp_buf recover; +volatile bool smashed_stack; + +void CrashHandler(int sig, siginfo_t *si, void *ctx) { + struct sigaltstack ss; + ASSERT_SYS(0, 0, sigaltstack(0, &ss)); + ASSERT_EQ(SS_ONSTACK, ss.ss_flags); + kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr); + smashed_stack = true; + ASSERT_TRUE(__is_stack_overflow(si, ctx)); + longjmp(recover, 123); +} + +void SetUp(void) { + + // tune down the main process's stack size to a reasonable amount + // some operating systems, e.g. freebsd, will do things like have + // 500mb RLIMIT_STACK by default, even on machines with 400mb RAM + struct rlimit rl = {2 * 1024 * 1024, 2 * 1024 * 1024}; + if (!IsWindows() && !IsXnu()) { + ASSERT_SYS(0, 0, setrlimit(RLIMIT_STACK, &rl)); + } + + // set up the signal handler and alternative stack + struct sigaction sa; + struct sigaltstack ss; + ss.ss_flags = 0; + ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 8192; + ss.ss_sp = _mapanon(ss.ss_size); + ASSERT_SYS(0, 0, sigaltstack(&ss, 0)); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = CrashHandler; + sigaction(SIGBUS, &sa, 0); + sigaction(SIGSEGV, &sa, 0); +} + +int StackOverflow(int f(), int n) { + if (n < INT_MAX) { + return f(f, n + 1) - 1; + } else { + return INT_MAX; + } +} + +int (*pStackOverflow)(int (*)(), int) = StackOverflow; + +TEST(stackoverflow, standardStack_altStack_process_longjmp) { + if (IsTiny()) return; // TODO(jart): why? + + int jumpcode; + if (!(jumpcode = setjmp(recover))) { + exit(pStackOverflow(pStackOverflow, 0)); + } + ASSERT_EQ(123, jumpcode); + ASSERT_TRUE(smashed_stack); + + // here's where longjmp() gets us into trouble + struct sigaltstack ss; + ASSERT_SYS(0, 0, sigaltstack(0, &ss)); + if (IsXnu() || IsNetbsd()) { + ASSERT_EQ(SS_ONSTACK, ss.ss_flags); // wut + } else { + ASSERT_EQ(0, ss.ss_flags); + } +} diff --git a/test/libc/calls/stackoverflow2_test.c b/test/libc/calls/stackoverflow2_test.c new file mode 100644 index 00000000000..807f59f3a0f --- /dev/null +++ b/test/libc/calls/stackoverflow2_test.c @@ -0,0 +1,105 @@ +/*-*- 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. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigaltstack.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/dce.h" +#include "libc/intrin/kprintf.h" +#include "libc/limits.h" +#include "libc/mem/gc.internal.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/runtime/sysconf.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/ss.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" + +/** + * stack overflow recovery technique #2 + * longjmp out of signal back into thread + * simple but it can upset kernels / libraries + */ + +jmp_buf recover; +volatile bool smashed_stack; + +void CrashHandler(int sig, siginfo_t *si, void *ctx) { + struct sigaltstack ss; + ASSERT_SYS(0, 0, sigaltstack(0, &ss)); + ASSERT_EQ(SS_ONSTACK, ss.ss_flags); + kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr); + smashed_stack = true; + ASSERT_TRUE(__is_stack_overflow(si, ctx)); + longjmp(recover, 123); +} + +int StackOverflow(int f(), int n) { + if (n < INT_MAX) { + return f(f, n + 1) - 1; + } else { + return INT_MAX; + } +} + +int (*pStackOverflow)(int (*)(), int) = StackOverflow; + +void *MyPosixThread(void *arg) { + int jumpcode; + struct sigaction sa, o1, o2; + struct sigaltstack ss; + ss.ss_flags = 0; + ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 4096; + ss.ss_sp = gc(malloc(ss.ss_size)); + ASSERT_SYS(0, 0, sigaltstack(&ss, 0)); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = CrashHandler; + sigaction(SIGBUS, &sa, &o1); + sigaction(SIGSEGV, &sa, &o2); + if (!(jumpcode = setjmp(recover))) { + exit(pStackOverflow(pStackOverflow, 0)); + } + ASSERT_EQ(123, jumpcode); + sigaction(SIGSEGV, &o2, 0); + sigaction(SIGBUS, &o1, 0); + // here's where longjmp() gets us into trouble + ASSERT_SYS(0, 0, sigaltstack(0, &ss)); + if (IsXnu() || IsNetbsd()) { + ASSERT_EQ(SS_ONSTACK, ss.ss_flags); // wut + } else { + ASSERT_EQ(0, ss.ss_flags); + } + return 0; +} + +TEST(stackoverflow, standardStack_altStack_thread_longjmp) { + pthread_t th; + struct sigaltstack ss; + for (int i = 0; i < 2; ++i) { + smashed_stack = false; + pthread_create(&th, 0, MyPosixThread, 0); + pthread_join(th, 0); + ASSERT_TRUE(smashed_stack); + // this should be SS_DISABLE but ShowCrashReports() creates an alt stack + ASSERT_SYS(0, 0, sigaltstack(0, &ss)); + ASSERT_EQ(0, ss.ss_flags); + } +} diff --git a/test/libc/thread/makecontext_test.c b/test/libc/thread/makecontext_test.c index 0af39770ffe..db1cb4a4065 100644 --- a/test/libc/thread/makecontext_test.c +++ b/test/libc/thread/makecontext_test.c @@ -31,10 +31,9 @@ #include "libc/x/x.h" #include "third_party/libcxx/math.h" -#if 0 // TODO(jart): fix me - bool gotsome; ucontext_t uc, goback; +extern long __klog_handle; void SetUpOnce(void) { testlib_enable_tmp_setup_teardown(); @@ -90,18 +89,17 @@ TEST(makecontext, backtrace) { SPAWN(fork); ASSERT_SYS(0, 0, close(2)); ASSERT_SYS(0, 2, creat("log", 0644)); + __klog_handle = 2; getcontext(&uc); uc.uc_link = 0; uc.uc_stack.ss_sp = NewCosmoStack(); uc.uc_stack.ss_size = GetStackSize(); makecontext(&uc, itsatrap, 2, 123, 456); setcontext(&uc); - EXITS(128 + SIGSEGV); + TERMS(SIGSEGV); if (!GetSymbolTable()) return; char *log = gc(xslurp("log", 0)); EXPECT_NE(0, strstr(log, "itsatrap")); EXPECT_NE(0, strstr(log, "runcontext")); EXPECT_NE(0, strstr(log, "makecontext_backtrace")); } - -#endif