2025-02-26
Tags: emacs
Disaster [1] is an Emacs package authored by the venerable Justine
Tunney that allows disassembling C/C++ and Fortran code from the
comfort of your source editing buffer.
I really like this idea, but most of the time I'm interested in
[40m`x86_64`[49m assembly instead of my [40m`aarch64`[49m host
platform, so I decided to hack on cross compilation support using
Zig as a C/C++ cross compiler [2]!
I'm using disaster because its simplicity fits my use case, but you
should be able to use the same idea with other packages like
(HTM) RMSbolt
Has support for tons of languages (and bytecode, not just
assembly), and you can track point across source and compilation
buffers
(HTM) Beardbolt
Rewrite of RMSbolt that is faster/simpler than RMSbolt, but only
supports C/C++/Rust
== [1m[4mPoC Or GTFO[22m[24m
[1m[37m([0m[37muse-package[0m[1m[37m [0m[1m[36mdisaster[0m[1m[37m[0m
[1m[37m [0m[37m:config[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37msetq[0m[1m[37m [0m[1m[36mdisaster-cflags[0m[1m[37m [0m[1m[37m([0m[37msetq[0m[1m[37m [0m[1m[36mdisaster-cxxflags[0m[1m[37m [0m[33m"-Wno-everything -g"[0m[1m[37m))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37msetq[0m[1m[37m [0m[1m[36mdisaster-assembly-mode[0m[1m[37m [0m[33m'nasm-mode[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[37m:init[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mdefine-advice[0m[1m[37m [0m[1m[36mdisaster[0m[1m[37m [0m[1m[37m([0m[37m:around[0m[1m[37m [0m[1m[37m([0m[1m[36mfn[0m[1m[37m [0m[33m&rest[0m[1m[37m [0m[1m[36margs[0m[1m[37m)[0m[1m[37m [0m[1m[36mcross-compile[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37minteractive[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[33m;; OPTIONAL: support for non-file buffers[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37msetq[0m[1m[37m [0m[1m[36marg[0m[1m[37m [0m[1m[37m([0m[37mor[0m[1m[37m [0m[1m[36margs[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mlist[0m[1m[37m [0m[1m[37m([0m[1m[36mif-let*[0m[1m[37m [0m[1m[37m(([0m[1m[36mbuf[0m[1m[37m [0m[1m[37m([0m[37mcurrent-buffer[0m[1m[37m))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mbuf-name[0m[1m[37m [0m[1m[37m([0m[37mbuffer-name[0m[1m[37m [0m[1m[36mbuf[0m[1m[37m))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mfile[0m[1m[37m [0m[1m[37m([0m[37mbuffer-file-name[0m[1m[37m [0m[1m[37m([0m[37mcurrent-buffer[0m[1m[37m))))[0m[1m[37m[0m
[1m[37m [0m[1m[36mfile[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mmake-temp-file[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mfile-name-base[0m[1m[37m [0m[1m[36mbuf-name[0m[1m[37m)[0m[1m[37m [0m[1m[37mnil[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mcond[0m[1m[37m [0m[1m[37m(([0m[37meq[0m[1m[37m [0m[1m[36mmajor-mode[0m[1m[37m [0m[33m'fundamental-mode[0m[1m[37m)[0m[1m[37m [0m[1m[37m([0m[1m[36mc-or-c++-ts-mode[0m[1m[37m))[0m[1m[37m[0m
[1m[37m [0m[1m[37m(([0m[37mmember[0m[1m[37m [0m[1m[36mmajor-mode[0m[1m[37m [0m[1m[36m'[0m[1m[37m([0m[1m[36mc-mode[0m[1m[37m [0m[1m[36mc-ts-mode[0m[1m[37m))[0m[1m[37m [0m[33m".c"[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m(([0m[37mmember[0m[1m[37m [0m[1m[36mmajor-mode[0m[1m[37m [0m[1m[36m'[0m[1m[37m([0m[1m[36mc++-mode[0m[1m[37m [0m[1m[36mc++-ts-mode[0m[1m[37m))[0m[1m[37m [0m[33m".cpp"[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m(([0m[37meq[0m[1m[37m [0m[1m[36mmajor-mode[0m[1m[37m [0m[33m'fortran-mode[0m[1m[37m)[0m[1m[37m [0m[33m".f"[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[37mt[0m[1m[37m [0m[1m[37m([0m[1m[36mfile-name-extension[0m[1m[37m [0m[1m[36mbuf-name[0m[1m[37m)))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mbuffer-string[0m[1m[37m)))))[0m[1m[37m[0m
[1m[37m [0m[33m;; replace `doit` with `apply fn args` if you get rid of this[0m[1m[37m[0m
[1m[37m [0m[1m[36mdoit[0m[1m[37m [0m[1m[37m([0m[37mlambda[0m[1m[37m [0m[1m[37m()[0m[1m[37m [0m[1m[37m([0m[37mif[0m[1m[37m [0m[1m[36margs[0m[1m[37m [0m[1m[37m([0m[37mapply[0m[1m[37m [0m[1m[36mfn[0m[1m[37m [0m[1m[36marg[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mwith-temp-buffer[0m[1m[37m [0m[1m[37m([0m[37mapply[0m[1m[37m [0m[1m[36mfn[0m[1m[37m [0m[1m[36marg[0m[1m[37m)))))[0m[1m[37m[0m
[1m[37m [0m[33m;; END-OPTIONAL[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mif[0m[1m[37m [0m[1m[37m([0m[37mand[0m[1m[37m [0m[1m[36mcurrent-prefix-arg[0m[1m[37m [0m[1m[37m([0m[37mmapc[0m[1m[37m [0m[1m[37m([0m[37mlambda[0m[1m[37m [0m[1m[37m([0m[1m[36mexe[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mor[0m[1m[37m [0m[1m[37m([0m[1m[36mexecutable-find[0m[1m[37m [0m[1m[36mexe[0m[1m[37m)[0m[1m[37m [0m[1m[37m([0m[1m[37muser-error[0m[1m[37m [0m[33m"disaster: %s not found"[0m[1m[37m [0m[1m[36mexe[0m[1m[37m)))[0m[1m[37m[0m
[1m[37m [0m[1m[36m'[0m[1m[37m([0m[33m"zig"[0m[1m[37m [0m[33m"jq"[0m[1m[37m)))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mlet*[0m[1m[37m [0m[1m[37m(([0m[1m[36mmonch[0m[1m[37m [0m[1m[37m([0m[37mlambda[0m[1m[37m [0m[1m[37m([0m[1m[36mprompt[0m[1m[37m [0m[1m[36mcollection[0m[1m[37m [0m[1m[36mdefault[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mcompleting-read[0m[1m[37m [0m[1m[36mprompt[0m[1m[37m [0m[1m[37m([0m[1m[36msplit-string[0m[1m[37m [0m[1m[36mcollection[0m[1m[37m [0m[33m" "[0m[1m[37m)[0m[1m[37m [0m[1m[37mnil[0m[1m[37m [0m[1m[37mnil[0m[1m[37m [0m[1m[37mnil[0m[1m[37m [0m[1m[37mnil[0m[1m[37m [0m[1m[36mdefault[0m[1m[37m)))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mfile-exists-wrapped[0m[1m[37m [0m[1m[37m([0m[37msymbol-function[0m[1m[37m [0m[37m#'file-exists-p[0m[1m[37m))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mtargets[0m[1m[37m [0m[1m[37m([0m[1m[36msplit-string[0m[1m[37m [0m[1m[37m([0m[1m[36mshell-command-to-string[0m[1m[37m [0m[33m"zig targets | jq -r '.arch,.os,.abi | join(\" \")'"[0m[1m[37m)[0m[1m[37m [0m[33m"\n"[0m[1m[37m [0m[1m[37mt[0m[1m[37m))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mhost-target[0m[1m[37m [0m[1m[37m([0m[37mmapcar[0m[1m[37m [0m[1m[37m([0m[37mlambda[0m[1m[37m [0m[1m[37m([0m[1m[36ms[0m[1m[37m)[0m[1m[37m [0m[1m[37m([0m[37mcar[0m[1m[37m [0m[1m[37m([0m[1m[36msplit-string[0m[1m[37m [0m[1m[36ms[0m[1m[37m [0m[33m"[ \.\t\n\r]+"[0m[1m[37m)))[0m[1m[37m [0m[1m[37m([0m[1m[36msplit-string[0m[1m[37m [0m[1m[37m([0m[1m[36mshell-command-to-string[0m[1m[37m [0m[33m"zig env | jq -r '.target'"[0m[1m[37m)[0m[1m[37m [0m[33m"-"[0m[1m[37m)))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mtarget-arg[0m[1m[37m [0m[1m[37m([0m[37mapply[0m[1m[37m [0m[37m#'format[0m[1m[37m [0m[33m" -target %s-%s-%s"[0m[1m[37m [0m[1m[37m([0m[1m[36mcl-mapcar[0m[1m[37m [0m[1m[36mmonch[0m[1m[37m [0m[1m[36m'[0m[1m[37m([0m[33m"Arch: "[0m[1m[37m [0m[33m"OS: "[0m[1m[37m [0m[33m"ABI: "[0m[1m[37m)[0m[1m[37m [0m[1m[36mtargets[0m[1m[37m [0m[1m[36mhost-target[0m[1m[37m)))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mdisaster-cc[0m[1m[37m [0m[33m"zig cc"[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mdisaster-cxx[0m[1m[37m [0m[33m"zig c++"[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mdisaster-cflags[0m[1m[37m [0m[1m[37m([0m[37mconcat[0m[1m[37m [0m[1m[36mdisaster-cflags[0m[1m[37m [0m[1m[36mtarget-arg[0m[1m[37m))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mdisaster-cxxflags[0m[1m[37m [0m[1m[37m([0m[37mconcat[0m[1m[37m [0m[1m[36mdisaster-cxxflags[0m[1m[37m [0m[1m[36mtarget-arg[0m[1m[37m)))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mwith-environment-variables[0m[1m[37m [0m[1m[37m(([0m[33m"CC"[0m[1m[37m [0m[1m[36mdisaster-cc[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[33m"CXX"[0m[1m[37m [0m[1m[36mdisaster-cxx[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[33m"CFLAGS"[0m[1m[37m [0m[1m[36mdisaster-cflags[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[33m"CXXFLAGS"[0m[1m[37m [0m[1m[36mdisaster-cxxflags[0m[1m[37m))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mcl-letf[0m[1m[37m [0m[1m[37m((([0m[37msymbol-function[0m[1m[37m [0m[37m#'file-exists-p[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mlambda[0m[1m[37m [0m[1m[37m([0m[1m[36mfile[0m[1m[37m)[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37munless[0m[1m[37m [0m[1m[37m([0m[1m[36mstring=[0m[1m[37m [0m[33m"compile_commands.json"[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mfile-name-nondirectory[0m[1m[37m [0m[1m[36mfile[0m[1m[37m))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mfuncall[0m[1m[37m [0m[1m[36mfile-exists-wrapped[0m[1m[37m [0m[1m[36mfile[0m[1m[37m)))))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mfuncall[0m[1m[37m [0m[1m[36mdoit[0m[1m[37m))))[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[37mfuncall[0m[1m[37m [0m[1m[36mdoit[0m[1m[37m))[0m[1m[37m[0m
[1m[37m [0m[33m;; OPTIONAL: Put point in assembly buffer[0m[1m[37m[0m
[1m[37m [0m[1m[37m([0m[1m[36mswitch-to-buffer-other-window[0m[1m[37m [0m[1m[36mdisaster-buffer-assembly[0m[1m[37m)))[0m[1m[37m[0m
This requires [40m[35m`zig`[39m[49m and
[40m[35m`jq`[39m[49m to be in Emac's [40m`exec-path`[49m,
although I'm sure you could use Elisp to do the JSON parsing
instead, especially now that Emacs 30.1 ships with its own JSON
implementation.
Most sane people would object to my usage of [40m`cl-letf`[49m
and advice instead of a separate wrapper function; this would
probably be more readable as a patch instead, but I like having a
snippet that you can quickly try out with [40m`eval-last-
sexp`[49m.
Caveat emptor: this falls apart for large projects, poorly behaved
Makefiles, and probably CMake (I tried ameliorating the latter
issue upstream, but I don't use CMake that often so YMMV).
Also, this definitely doesn't count as a "Compiler Explorer" in the
strict sense because you're using the same version of LLVM
regardless of target; you might be able to do something like
leverage nixpkgs' cross compilation support to build older cross-
compilers, but you're probably better off using Docker or Godbolt
[3] at that point.
References:
(HTM) [1] Disaster
(HTM) [2] Zig as a C/C++ cross compiler
(HTM) [3] Godbolt
>=================================================================<
(DIR) Blog
(DIR) Writeups
(DIR) jp
copyright 2026 George Huebner
(HTM) email