From c5c4dfcd218d834b94d4a73ab952f65c1a8b93d4 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 3 Sep 2022 18:12:01 -0700 Subject: [PATCH] Improve quality of raise(), abort(), and tkill() This change fixes a nasty bug where SIG_IGN and SIG_DFL weren't working as advertised on BSDs. This change also fixes the tkill() definition on MacOS so it maps to __pthread_kill(). --- build/rules.mk | 2 +- libc/calls/raise.c | 57 ++++++------- libc/calls/sigaction.c | 25 ++++-- libc/runtime/{abort.greg.c => abort.c} | 27 +++--- libc/runtime/runtime.h | 2 +- libc/sysv/calls/sys_tkill.s | 2 +- libc/sysv/consts.sh | 2 +- libc/sysv/syscalls.sh | 2 +- libc/thread/xnu.internal.h | 1 + test/libc/calls/raise_test.c | 111 +++++++++++++++++++++++++ test/libc/runtime/abort_test.c | 107 ++++++++++++++++++++++++ tool/emacs/cosmo-stuff.el | 18 ++-- 12 files changed, 293 insertions(+), 63 deletions(-) rename libc/runtime/{abort.greg.c => abort.c} (83%) create mode 100644 test/libc/calls/raise_test.c create mode 100644 test/libc/runtime/abort_test.c diff --git a/build/rules.mk b/build/rules.mk index 1fc646402..917b24da6 100644 --- a/build/rules.mk +++ b/build/rules.mk @@ -109,7 +109,7 @@ o/$(MODE)/%: o/$(MODE)/%.com o/$(MODE)/tool/build/cp.com o/$(MODE)/tool/build/as # May be specified in your ~/.cosmo.mk file. You can also use this to # enable things like function tracing. For example: # -# TESTARGS = --ftrace +# TESTARGS = ----ftrace # .PLEDGE += prot_exec # # You could then run a command like: diff --git a/libc/calls/raise.c b/libc/calls/raise.c index 54665886e..d2fb4927b 100644 --- a/libc/calls/raise.c +++ b/libc/calls/raise.c @@ -17,32 +17,45 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/getconsolectrlevent.internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/strace.internal.h" -#include "libc/calls/struct/sigset.h" #include "libc/calls/syscall-sysv.internal.h" -#include "libc/calls/syscall_support-nt.internal.h" -#include "libc/nt/console.h" -#include "libc/nt/errors.h" -#include "libc/nt/process.h" -#include "libc/nt/runtime.h" -#include "libc/nt/synchronization.h" +#include "libc/dce.h" +#include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" -#include "libc/runtime/runtime.h" -#include "libc/str/str.h" #include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" +#include "libc/thread/xnu.internal.h" static textwindows inline bool HasWorkingConsole(void) { return !!(__ntconsolemode[0] | __ntconsolemode[1] | __ntconsolemode[2]); } +static noubsan void RaiseSigFpe(void) { + volatile int x = 0; + x = 1 / x; +} + /** - * Sends signal to this thread. + * Sends signal to self. + * + * This is basically the same as: + * + * tkill(gettid(), sig); + * + * Note `SIG_DFL` still results in process death for most signals. + * + * This function is not entirely equivalent to kill() or tkill(). For + * example, we raise `SIGTRAP` and `SIGFPE` the natural way, since that + * helps us support Windows. So if the raised signal has a signal + * handler, then the reported `si_code` might not be `SI_TKILL`. + * + * On Windows, if a signal results in the termination of the process + * then we use the convention `_Exit(128 + sig)` to notify the parent of + * the signal number. * * @param sig can be SIGALRM, SIGINT, SIGTERM, SIGKILL, etc. - * @return 0 on success or -1 w/ errno + * @return 0 if signal was delivered and returned, or -1 w/ errno * @asyncsignalsafe */ int raise(int sig) { @@ -52,28 +65,12 @@ int raise(int sig) { DebugBreak(); rc = 0; } else if (sig == SIGFPE) { - volatile int x = 0; - x = 1 / x; + RaiseSigFpe(); rc = 0; } else if (!IsWindows()) { rc = sys_tkill(gettid(), sig, 0); } else { - if (HasWorkingConsole() && (event = GetConsoleCtrlEvent(sig)) != -1) { - // XXX: MSDN says "If this parameter is zero, the signal is - // generated in all processes that share the console of the - // calling process." which seems to imply multiple process - // groups potentially. We just shouldn't use this because it - // doesn't make any sense and it's so evil. - if (GenerateConsoleCtrlEvent(event, 0)) { - SleepEx(100, true); - __sig_check(false); - rc = 0; - } else { - rc = __winerr(); - } - } else { - rc = __sig_raise(sig, SI_USER); - } + rc = __sig_raise(sig, SI_TKILL); } STRACE("...raise(%G) → %d% m", sig, rc); return rc; diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index 4a647aac8..27cd0f162 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -182,8 +182,11 @@ static int __sigaction(int sig, const struct sigaction *act, ap = © if (IsXnu()) { ap->sa_restorer = (void *)&__sigenter_xnu; - ap->sa_handler = (void *)&__sigenter_xnu; - + if (rva < kSigactionMinRva) { + ap->sa_sigaction = (void *)(intptr_t)rva; + } else { + ap->sa_sigaction = (void *)&__sigenter_xnu; + } // mitigate Rosetta signal handling strangeness // https://github.com/jart/cosmopolitan/issues/455 ap->sa_flags |= SA_SIGINFO; @@ -193,11 +196,23 @@ static int __sigaction(int sig, const struct sigaction *act, ap->sa_restorer = &__restore_rt; } } else if (IsNetbsd()) { - ap->sa_sigaction = (sigaction_f)__sigenter_netbsd; + if (rva < kSigactionMinRva) { + ap->sa_sigaction = (void *)(intptr_t)rva; + } else { + ap->sa_sigaction = (sigaction_f)__sigenter_netbsd; + } } else if (IsFreebsd()) { - ap->sa_sigaction = (sigaction_f)__sigenter_freebsd; + if (rva < kSigactionMinRva) { + ap->sa_sigaction = (void *)(intptr_t)rva; + } else { + ap->sa_sigaction = (sigaction_f)__sigenter_freebsd; + } } else if (IsOpenbsd()) { - ap->sa_sigaction = (sigaction_f)__sigenter_openbsd; + if (rva < kSigactionMinRva) { + ap->sa_sigaction = (void *)(intptr_t)rva; + } else { + ap->sa_sigaction = (sigaction_f)__sigenter_openbsd; + } } else { return enosys(); } diff --git a/libc/runtime/abort.greg.c b/libc/runtime/abort.c similarity index 83% rename from libc/runtime/abort.greg.c rename to libc/runtime/abort.c index ebef9c846..59a4e975f 100644 --- a/libc/runtime/abort.greg.c +++ b/libc/runtime/abort.c @@ -17,30 +17,29 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/calls/sig.internal.h" +#include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigset.h" -#include "libc/dce.h" -#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/sig.h" /** * Terminates program abnormally. * - * This function first tries to trigger your SIGABRT handler. If - * there isn't one or execution resumes, then abort() terminates - * the program using an escalating variety methods of increasing - * brutality. + * This function first tries to trigger your SIGABRT handler. If the + * signal handler returns, then `signal(SIGABRT, SIG_DFL)` is called + * before SIGABRT is raised again. * * @asyncsignalsafe * @noreturn */ -privileged void abort(void) { - sigset_t sm; - sigfillset(&sm); - sigdelset(&sm, SIGABRT); - sigprocmask(SIG_SETMASK, &sm, 0); +wontreturn void abort(void) { + sigset_t m; + sigemptyset(&m); + sigaddset(&m, SIGABRT); + sigprocmask(SIG_UNBLOCK, &m, 0); raise(SIGABRT); - __restorewintty(); - _Exit(128 + SIGABRT); + signal(SIGABRT, SIG_DFL); + raise(SIGABRT); + asm("hlt"); + unreachable; } diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index bd4275f61..5cb64241e 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -65,7 +65,7 @@ void _exit(int) libcesque wontreturn; void _Exit(int) libcesque wontreturn; void _Exit1(int) libcesque wontreturn; void quick_exit(int) wontreturn; -void abort(void) wontreturn noinstrument; +void abort(void) wontreturn; int __cxa_atexit(void *, void *, void *) libcesque; int atfork(void *, void *) libcesque; int atexit(void (*)(void)) libcesque; diff --git a/libc/sysv/calls/sys_tkill.s b/libc/sysv/calls/sys_tkill.s index 47e2c444f..776b24ab1 100644 --- a/libc/sysv/calls/sys_tkill.s +++ b/libc/sysv/calls/sys_tkill.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/macros.internal.inc" -.scall sys_tkill,0x13e0771b121690c8,globl,hidden +.scall sys_tkill,0x13e0771b121480c8,globl,hidden diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index f0f790521..55e17602a 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -533,7 +533,7 @@ syscon sicode SI_TKILL -6 0x80000000 0x010007 -1 -5 -6 # sent by tk syscon sicode SI_MESGQ -3 0x010005 0x010005 0x80000000 -4 -3 # sent by mq_notify(2); lool syscon sicode SI_ASYNCIO -4 0x010004 0x010004 0x80000000 -3 -4 # aio completion; no thank you syscon sicode SI_ASYNCNL -60 0x80000000 0x80000000 0x80000000 0x80000000 0x80000000 # aio completion for dns; the horror -syscon sicode SI_KERNEL 0x80 0x80000000 0x010006 0x80000000 0x80000000 0x80 # wut; openbsd defines as si_code>0 +syscon sicode SI_KERNEL 128 0x80000000 0x010006 0x80000000 0x80000000 0x80 # wut; openbsd defines as si_code>0 syscon sicode SI_NOINFO 32767 0x80000000 0 32767 32767 32767 # no signal specific info available syscon sicode CLD_EXITED 1 1 1 1 1 1 # SIGCHLD; child exited; unix consensus syscon sicode CLD_KILLED 2 2 2 2 2 2 # SIGCHLD; child terminated w/o core; unix consensus diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 871182ff0..3e9c39c27 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -98,7 +98,7 @@ scall __sys_wait4 0x1c100b007200703d globl hidden scall sys_kill 0x02507a025202503e globl hidden # kill(pid, sig, 1) b/c xnu scall sys_killpg 0x092fff092fffffff globl hidden scall sys_clone 0x11fffffffffff038 globl hidden -scall sys_tkill 0x13e0771b121690c8 globl hidden # thr_kill() on freebsd; _lwp_kill() on netbsd; thrkill() on openbsd where arg3 should be 0; bsdthread_terminate() on XNU which only has 1 arg +scall sys_tkill 0x13e0771b121480c8 globl hidden # thr_kill() on freebsd; _lwp_kill() on netbsd; thrkill() on openbsd where arg3 should be 0; __pthread_kill() on XNU scall sys_futex 0x0a6053fffffff0ca globl hidden # raises SIGSYS on NetBSD scall set_robust_list 0x0a7ffffffffff111 globl scall get_robust_list 0x0a8ffffffffff112 globl diff --git a/libc/thread/xnu.internal.h b/libc/thread/xnu.internal.h index ff23f8c8e..8177f4e75 100644 --- a/libc/thread/xnu.internal.h +++ b/libc/thread/xnu.internal.h @@ -12,6 +12,7 @@ int bsdthread_create(void *func, void *func_arg, void *stack, void *pthread, uint32_t flags); int bsdthread_terminate(void *stackaddr, size_t freesize, uint32_t port, uint32_t sem); +int __pthread_kill(uint32_t port, int sig); int bsdthread_register( void (*threadstart)(void *pthread, int machport, void *(*func)(void *), void *arg, intptr_t *, unsigned), diff --git a/test/libc/calls/raise_test.c b/test/libc/calls/raise_test.c new file mode 100644 index 000000000..c80ffd252 --- /dev/null +++ b/test/libc/calls/raise_test.c @@ -0,0 +1,111 @@ +/*-*- 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 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/calls/calls.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/dce.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sicode.h" +#include "libc/sysv/consts/sig.h" +#include "libc/testlib/subprocess.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/spawn.h" + +//////////////////////////////////////////////////////////////////////////////// +// SIGTRAP + +TEST(raise, trap_sysv) { + if (IsWindows()) return; + signal(SIGTRAP, SIG_DFL); + SPAWN(fork); + raise(SIGTRAP); + TERMS(SIGTRAP); +} + +TEST(raise, trap_windows) { + if (!IsWindows()) return; + signal(SIGTRAP, SIG_DFL); + SPAWN(fork); + raise(SIGTRAP); + EXITS(128 + SIGTRAP); +} + +//////////////////////////////////////////////////////////////////////////////// +// SIGFPE + +TEST(raise, fpe_sysv) { + if (IsWindows()) return; + signal(SIGFPE, SIG_DFL); + SPAWN(fork); + raise(SIGFPE); + TERMS(SIGFPE); +} + +TEST(raise, fpe_windows) { + if (!IsWindows()) return; + signal(SIGFPE, SIG_DFL); + SPAWN(fork); + raise(SIGFPE); + EXITS(128 + SIGFPE); +} + +//////////////////////////////////////////////////////////////////////////////// +// SIGUSR1 + +TEST(raise, usr1_sysv) { + if (IsWindows()) return; + SPAWN(fork); + raise(SIGUSR1); + TERMS(SIGUSR1); +} + +TEST(raise, usr1_windows) { + if (!IsWindows()) return; + SPAWN(fork); + raise(SIGUSR1); + EXITS(128 + SIGUSR1); +} + +//////////////////////////////////////////////////////////////////////////////// +// THREADS + +int threadid; + +void WorkerQuit(int sig, siginfo_t *si, void *ctx) { + ASSERT_EQ(SIGILL, sig); + if (!IsXnu() && !IsOpenbsd()) { + ASSERT_EQ(SI_TKILL, si->si_code); + } + ASSERT_EQ(threadid, gettid()); +} + +int Worker(void *arg, int tid) { + struct sigaction sa = {.sa_sigaction = WorkerQuit, .sa_flags = SA_SIGINFO}; + ASSERT_EQ(0, sigaction(SIGILL, &sa, 0)); + threadid = tid; + ASSERT_EQ(0, raise(SIGILL)); + return 0; +} + +TEST(raise, threaded) { + signal(SIGILL, SIG_DFL); + struct spawn worker; + ASSERT_SYS(0, 0, _spawn(Worker, 0, &worker)); + ASSERT_SYS(0, 0, _join(&worker)); +} diff --git a/test/libc/runtime/abort_test.c b/test/libc/runtime/abort_test.c new file mode 100644 index 000000000..931a853a1 --- /dev/null +++ b/test/libc/runtime/abort_test.c @@ -0,0 +1,107 @@ +/*-*- 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 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/calls/calls.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigset.h" +#include "libc/dce.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/sig.h" +#include "libc/testlib/subprocess.h" +#include "libc/testlib/testlib.h" + +#if 0 +TEST(abort, sysv) { + if (IsWindows()) return; + SPAWN(fork); + ASSERT_NE(SIG_ERR, signal(SIGABRT, SIG_DFL)); + abort(); + TERMS(SIGABRT); +} + +TEST(abort, windows) { + if (!IsWindows()) return; + SPAWN(fork); + ASSERT_NE(SIG_ERR, signal(SIGABRT, SIG_DFL)); + abort(); + EXITS(128 + SIGABRT); +} + +//////////////////////////////////////////////////////////////////////////////// + +TEST(abort, blocked_stillTerminates_sysv) { + sigset_t ss; + if (IsWindows()) return; + SPAWN(fork); + ASSERT_NE(SIG_ERR, signal(SIGABRT, SIG_DFL)); + sigfillset(&ss); + sigprocmask(SIG_SETMASK, &ss, 0); + abort(); + TERMS(SIGABRT); +} + +TEST(abort, blocked_stillTerminates_windows) { + sigset_t ss; + if (!IsWindows()) return; + SPAWN(fork); + ASSERT_NE(SIG_ERR, signal(SIGABRT, SIG_DFL)); + sigfillset(&ss); + sigprocmask(SIG_SETMASK, &ss, 0); + abort(); + EXITS(128 + SIGABRT); +} + +//////////////////////////////////////////////////////////////////////////////// + +TEST(abort, ign_stillTerminates_sysv) { + if (IsWindows()) return; + SPAWN(fork); + ASSERT_NE(SIG_ERR, signal(SIGABRT, SIG_IGN)); + abort(); + TERMS(SIGABRT); +} + +TEST(abort, ign_stillTerminates_windows) { + if (!IsWindows()) return; + SPAWN(fork); + ASSERT_NE(SIG_ERR, signal(SIGABRT, SIG_IGN)); + abort(); + EXITS(128 + SIGABRT); +} + +//////////////////////////////////////////////////////////////////////////////// +#endif + +void Ignore(int sig) { +} + +TEST(abort, handled_stillTerminates_sysv) { + if (IsWindows()) return; + SPAWN(fork); + ASSERT_NE(SIG_ERR, signal(SIGABRT, Ignore)); + abort(); + TERMS(SIGABRT); +} + +TEST(abort, handled_stillTerminates_windows) { + if (!IsWindows()) return; + SPAWN(fork); + ASSERT_NE(SIG_ERR, signal(SIGABRT, Ignore)); + abort(); + EXITS(128 + SIGABRT); +} diff --git a/tool/emacs/cosmo-stuff.el b/tool/emacs/cosmo-stuff.el index 9fef9e89a..c02f40531 100644 --- a/tool/emacs/cosmo-stuff.el +++ b/tool/emacs/cosmo-stuff.el @@ -189,7 +189,7 @@ (runs (format "o/$m/%s.com%s V=5 TESTARGS=-b" name runsuffix)) (buns (format "o/$m/test/%s_test.com%s V=5 TESTARGS=-b" name runsuffix))) (cond ((not (member ext '("c" "cc" "s" "S" "rl" "f"))) - (format "m=%s; make -j12 -O MODE=$m o/$m/%s" + (format "m=%s; make -j12 MODE=$m o/$m/%s" mode (directory-file-name (or (file-name-directory @@ -200,7 +200,7 @@ (cosmo-join " && " `("m=%s; f=o/$m/%s.com" - ,(concat "make -j12 -O $f MODE=$m") + ,(concat "make -j12 $f MODE=$m") "scp $f $f.dbg win10:; ssh win10 ./%s.com")) mode name (file-name-nondirectory name))) ((eq kind 'run-xnu) @@ -208,19 +208,19 @@ (cosmo-join " && " `("m=%s; f=o/$m/%s.com" - ,(concat "make -j12 -O $f MODE=$m") + ,(concat "make -j12 $f MODE=$m") "scp $f $f.dbg xnu:" "ssh xnu ./%s.com")) mode name (file-name-nondirectory name))) ((and (equal suffix "") (cosmo-contains "_test." (buffer-file-name))) - (format "m=%s; make -j12 -O MODE=$m %s" + (format "m=%s; make -j12 MODE=$m %s" mode runs)) ((and (equal suffix "") (file-exists-p (format "%s" buddy))) (format (cosmo-join " && " - '("m=%s; n=%s; make -j12 -O o/$m/$n%s.o MODE=$m" + '("m=%s; n=%s; make -j12 o/$m/$n%s.o MODE=$m" ;; "bloat o/$m/%s.o | head" ;; "nm -C --size o/$m/%s.o | sort -r" "echo" @@ -232,11 +232,11 @@ (cosmo-join " && " `("m=%s; f=o/$m/%s.com" - ,(concat "make -j12 -O $f MODE=$m") + ,(concat "make -j12 $f MODE=$m") "./$f")) mode name)) ((eq kind 'test) - (format `"m=%s; f=o/$m/%s.com.ok && make -j12 -O $f MODE=$m" mode name)) + (format `"m=%s; f=o/$m/%s.com.ok && make -j12 $f MODE=$m" mode name)) ((and (file-regular-p this) (file-executable-p this)) (format "./%s" file)) @@ -245,7 +245,7 @@ (cosmo-join " && " `("m=%s; f=o/$m/%s%s.o" - ,(concat "make -j12 -O $f MODE=$m") + ,(concat "make -j12 $f MODE=$m") ;; "nm -C --size $f | sort -r" "echo" "size -A $f | grep '^[.T]' | grep -v 'debug\\|command.line\\|stack' | sort -rnk2" @@ -455,7 +455,7 @@ (error "don't know how to show assembly for non c/c++ source file")) (let* ((default-directory root) (compile-command - (format "/usr/bin/make %s -j12 -O MODE=%s %s %s" + (format "make %s -j12 MODE=%s %s %s" (or extra-make-flags "") mode asm-gcc asm-clang))) (save-buffer) (set-visited-file-modtime (current-time))