-
Notifications
You must be signed in to change notification settings - Fork 6
gef + rizin: the scriptable half of the reverse-engineering loadout #547
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bde0bdb
0e7bd14
2731817
0ba4b2a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| let { standaloneTest, Attrs, BuildSpec, Local, OutputBin, OutputData, Source, Test, .. } = import "minimal.ncl" in | ||
| let base = import "../base/build.ncl" in | ||
| let bash = import "../bash/build.ncl" in | ||
| let gdb = import "../gdb/build.ncl" in | ||
| let python = import "../python/build.ncl" in | ||
|
|
||
| let version = "2026.01" in | ||
| { | ||
| name = "gef", | ||
| build_deps = [ | ||
| { file = "build.sh" } | Local, | ||
| { | ||
| url = "https://github.com/hugsy/gef/archive/refs/tags/%{version}.tar.gz", | ||
| sha256 = "6e58afbda13ef976aa46a2f138b5422829a0d3c4b71246f4546cbd7383ce2e76", | ||
| } | Source, | ||
| base, | ||
| ], | ||
| runtime_deps = [ | ||
| bash, | ||
| # gef is a gdb EXTENSION — it is not a program, it is 433 KB of Python that | ||
| # gdb loads. Both of these are hard requirements, not conveniences: | ||
| # gef.py asserts GDB_MIN_VERSION (10,0) and PYTHON_MIN_VERSION (3,10) at | ||
| # import time and refuses to load otherwise. Ours are gdb 17.2 and Python | ||
| # 3.14, comfortably past both. | ||
| # | ||
| # The gdb package is built --with-python specifically so this works; a gdb | ||
| # without it reports "Python scripting is not supported in this copy of | ||
| # GDB" and gef cannot load at all. That flag is pinned by a test over | ||
| # there rather than trusted. | ||
| gdb, | ||
| python, | ||
| ], | ||
|
|
||
| cmd = "./build.sh", | ||
| build_args = { | ||
| include version, | ||
| }, | ||
|
|
||
| outputs = { | ||
| gef = { glob = "usr/bin/gef" } | OutputBin, | ||
| gef_py = { glob = "usr/share/gef/gef.py" } | OutputData, | ||
| }, | ||
|
|
||
| tests = { | ||
| smoketest = standaloneTest "/bin/gef --version", | ||
|
|
||
| # Loading is the whole package. gef is a single Python file, so there is no | ||
| # compile step that could fail — the only way this package breaks is that | ||
| # gdb cannot IMPORT it, and that failure is quiet: gdb prints the Python | ||
| # traceback to stderr and then carries on to a normal prompt, exiting 0. | ||
| # So assert on gef's own output, never on gdb's exit code. | ||
| loads_into_gdb = | ||
| { | ||
| class = 'Standalone, | ||
| test_deps = [base], | ||
| cmds = [ | ||
| # `gef` is registered as a gdb command only if gef.py imported | ||
| # cleanly all the way to the bottom of the file. | ||
| ["/bin/bash", "-c", "gef -batch -ex 'gef help' 2>&1 | grep -qi 'gef'"], | ||
| # A traceback on stderr means a partial import — gef would look | ||
| # present while most commands were missing. | ||
| ["/bin/bash", "-c", "! gef -batch -ex 'gef help' 2>&1 | grep -q 'Traceback'"], | ||
| ], | ||
| } | Test, | ||
| }, | ||
|
|
||
| attrs = | ||
| { | ||
| upstream_version = version, | ||
| license_spdx = "MIT", | ||
| source_provenance = { | ||
| category = 'GithubRepo, | ||
| owner = "hugsy", | ||
| repo = "gef", | ||
| }, | ||
| } | Attrs, | ||
| } | BuildSpec | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| #!/bin/sh | ||
| set -eux | ||
|
|
||
| # A GitHub tag archive arrives named after the URL's last component — for | ||
| # `.../archive/refs/tags/2026.01.tar.gz` that is `2026.01.tar.gz`, NOT | ||
| # `gef-2026.01.tar.gz`. Extract whatever tarball is here rather than encoding a | ||
| # guess: the failure mode for guessing wrong is tar's "Error is not | ||
| # recoverable", which says nothing useful. | ||
| for t in *.tar.gz; do | ||
| [ -f "$t" ] || continue | ||
| tar -xof "$t" | ||
| done | ||
|
|
||
| # The archive expands to `gef-<version>/`, but locate the file rather than | ||
| # assume — this is the second thing that would break silently on an upstream | ||
| # repackaging. | ||
| GEF_PY=$(find . -name gef.py -maxdepth 3 -type f | head -1) | ||
| if [ -z "$GEF_PY" ]; then | ||
| echo "cannot find gef.py after extraction; tree is:" >&2 | ||
| ls -la >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| mkdir -p "$OUTPUT_DIR/usr/share/gef" "$OUTPUT_DIR/usr/bin" | ||
| cp "$GEF_PY" "$OUTPUT_DIR/usr/share/gef/gef.py" | ||
|
|
||
| # A LAUNCHER, not a dotfile edit. Upstream's install instructions append a | ||
| # `source` line to ~/.gdbinit, which would make this package mutate the user's | ||
| # home directory and silently change the behaviour of every unrelated `gdb` | ||
| # invocation on the system. Ship a separate entry point instead: `gef` is gdb | ||
| # with gef loaded, and `gdb` stays exactly what it was. | ||
| # | ||
| # -q suppresses the banner so gef's own header is the first thing you see. | ||
| # -x sources gef BEFORE the target is loaded, which is what gef expects; any | ||
| # further args (a binary, --args, -p PID) are forwarded untouched. | ||
| cat > "$OUTPUT_DIR/usr/bin/gef" << 'EOF' | ||
| #!/bin/sh | ||
| exec gdb -q -x /usr/share/gef/gef.py "$@" | ||
| EOF | ||
| chmod 755 "$OUTPUT_DIR/usr/bin/gef" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| let { standaloneTest, Attrs, BuildSpec, Local, OutputBin, OutputData, OutputLib, Source, Test, .. } = import "minimal.ncl" in | ||
| let base = import "../base/build.ncl" in | ||
| let bash = import "../bash/build.ncl" in | ||
| let binutils = import "../binutils/build.ncl" in | ||
| let gcc = import "../gcc/build.ncl" in | ||
| let glibc = import "../glibc/build.ncl" in | ||
| let lz4 = import "../lz4/build.ncl" in | ||
| let meson = import "../meson/build.ncl" in | ||
| let ninja = import "../ninja/build.ncl" in | ||
| let openssl = import "../openssl/build.ncl" in | ||
| let pcre2 = import "../pcre2/build.ncl" in | ||
| let pkgconf = import "../pkgconf/build.ncl" in | ||
| let python = import "../python/build.ncl" in | ||
| let toolchain = import "../toolchain/build.ncl" in | ||
| let tree-sitter = import "../tree-sitter/build.ncl" in | ||
| let xz = import "../xz/build.ncl" in | ||
| let zlib = import "../zlib/build.ncl" in | ||
| let zstd = import "../zstd/build.ncl" in | ||
|
|
||
| let version = "0.9.1" in | ||
| { | ||
| name = "rizin", | ||
| build_deps = [ | ||
| { file = "build.sh" } | Local, | ||
| # The official `rizin-src` tarball, NOT a GitHub tag archive: it vendors | ||
| # the meson subprojects (capstone-next, tree-sitter, pcre2, lz4, nettle, | ||
| # blake2/3, libmspack, softfloat, libzip...), which is what lets the build | ||
| # run with --wrap-mode=nodownload and no network. | ||
| { | ||
| url = "https://github.com/rizinorg/rizin/releases/download/v%{version}/rizin-src-v%{version}.tar.xz", | ||
| sha256 = "7ac1cd7daca7afdda742e15478b1f747fc1f813e496fee71839d1e109e543dca", | ||
| } | Source, | ||
| base, | ||
| toolchain, | ||
| gcc, | ||
| meson, | ||
| ninja, | ||
| pkgconf, | ||
| python, # meson is Python | ||
| glibc, | ||
|
Comment on lines
+34
to
+40
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Inspect what toolchain/build.ncl already provides
fd -a build.ncl packages/toolchain
cat -n packages/toolchain/build.nclRepository: gominimal/pkgs Length of output: 719 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- minimal.ncl references ---'
rg -n -C 3 'build_deps|runtime_deps|BuildSpec|transitive|closure' packages/minimal.ncl packages -g '*.ncl' -g '*.md' | head -240
printf '%s\n' '--- relevant package declarations ---'
for f in packages/openssl/build.ncl packages/rizin/build.ncl packages/gcc/build.ncl packages/glibc/build.ncl; do
if [ -f "$f" ]; then
echo "--- $f ---"
cat -n "$f"
fi
doneRepository: gominimal/pkgs Length of output: 34578 🏁 Script executed: #!/bin/bash
set -eu
echo '--- minimal.ncl location ---'
git ls-files | rg '(^|/)minimal\.ncl$|README|docs' | head -100
echo '--- dependency semantics ---'
rg -n -C 4 'build_deps|runtime_deps|transitive|dependency closure|dependency graph|subsetOf' . -g '*.md' -g '*.ncl' -g '*.nickel' -g '*.toml' | head -300
echo '--- packages using toolchain with direct compiler dependencies ---'
rg -n -C 2 'toolchain|gcc|glibc' packages -g 'build.ncl' | rg -B2 -A2 'toolchain|gcc|glibc' | head -300Repository: gominimal/pkgs Length of output: 29875 Remove redundant
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| # The seven libraries we link from the SYSTEM rather than let rizin vendor, | ||
| # so a CVE in any of them is visible to pkgscan instead of buried in a | ||
| # static blob. See the use_sys_* rationale in build.sh. | ||
| zlib, | ||
| zstd, | ||
| xz, | ||
| lz4, | ||
| pcre2, | ||
| openssl, | ||
| tree-sitter, | ||
| ], | ||
| runtime_deps = [ | ||
| bash, | ||
| glibc, | ||
| zlib, | ||
| zstd, | ||
| xz, | ||
| lz4, | ||
| pcre2, | ||
| openssl, | ||
| tree-sitter, | ||
| ], | ||
|
|
||
| cmd = "./build.sh", | ||
| build_args = { | ||
| include version, | ||
| }, | ||
|
|
||
| outputs = { | ||
| # The headless/scriptable counterpart to Ghidra: Ghidra answers "what does | ||
| # this function do", rizin answers "run that over 400 binaries and diff the | ||
| # results". | ||
| rizin = { glob = "usr/bin/rizin" } | OutputBin, | ||
| rz_bins = { glob = "usr/bin/rz-*" } | OutputBin, | ||
| librz = { glob = "usr/lib/librz*.so*" } | OutputLib, | ||
| rz_data = { glob = "usr/share/rizin/**" } | OutputData, | ||
| }, | ||
|
|
||
| tests = { | ||
| smoketest = standaloneTest "/bin/rizin -v", | ||
|
|
||
| # Pin the two properties the use_sys_* flags exist for. Both regress | ||
| # SILENTLY: meson probes for each system library and falls back to the | ||
| # vendored subproject when one is not found, printing a note and carrying | ||
| # on to a successful build. You would get a rizin that works perfectly and | ||
| # has seven invisible bundled libraries — the exact outcome these flags | ||
| # exist to prevent, and one nothing else here would detect. | ||
| system_libs = | ||
| { | ||
| class = 'Standalone, | ||
| # binutils for readelf. The first version of this test used readelf | ||
| # with only `base` declared and failed with "readelf: command not | ||
| # found" — loudly, which is the correct behaviour and how this was | ||
| # caught, but it is a dependency and belongs here. | ||
| test_deps = [base, binutils], | ||
| cmds = [ | ||
| # Assert on the librz_* SHARED LIBRARIES, not on /usr/bin/rizin: | ||
| # the rizin binary is a thin frontend and carries none of these in | ||
| # its own DT_NEEDED — the linkage lives in the libraries. Checking | ||
| # the wrong file would have produced a test that always failed. | ||
| [ | ||
| "/bin/bash", | ||
| "-c", | ||
| m%" | ||
| set -eu | ||
| need="libz.so libzstd liblzma liblz4 libpcre2 libcrypto libtree-sitter" | ||
| all=$(readelf -d /usr/lib/librz*.so.* 2>/dev/null) | ||
| for lib in $need; do | ||
| case "$all" in | ||
| *"$lib"*) ;; | ||
| *) echo "$lib is NOT linked from the system — the use_sys_* flag" >&2 | ||
| echo "silently fell back to rizin's vendored copy, which pkgscan" >&2 | ||
| echo "cannot see." >&2 | ||
| exit 1 ;; | ||
| esac | ||
| done | ||
| "% | ||
| ], | ||
| # And that it actually analyses something. | ||
| ["/bin/bash", "-c", "rz-bin -I /bin/rizin | grep -qi 'bintype'"], | ||
| ], | ||
| } | Test, | ||
| }, | ||
|
|
||
| attrs = | ||
| { | ||
| upstream_version = version, | ||
| # Primary licence is LGPL-3.0-only (1813 SPDX headers), but the linked | ||
| # binary aggregates a 16-licence REUSE union that includes GPL-3.0 and | ||
| # GPL-2.0 files — so the shipped artifact is effectively GPL-3.0, and | ||
| # recording the permissive core alone would understate it. | ||
| license_spdx = "GPL-3.0-or-later", | ||
| source_provenance = { | ||
| category = 'GithubRepo, | ||
| owner = "rizinorg", | ||
| repo = "rizin", | ||
| }, | ||
| } | Attrs, | ||
| } | BuildSpec | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| #!/bin/sh | ||
| set -eux | ||
|
|
||
| tar -xof "rizin-src-v${MINIMAL_ARG_VERSION}.tar.xz" | ||
| cd "rizin-v${MINIMAL_ARG_VERSION}" | ||
|
|
||
| mkdir build | ||
| cd build | ||
|
|
||
| case $(uname -m) in | ||
| x86_64) MARCH="-march=x86-64-v3" ;; | ||
| aarch64) MARCH="-march=armv8-a" ;; | ||
| *) MARCH="" ;; | ||
| esac | ||
| export CFLAGS="$MARCH -O2 -pipe -gno-record-gcc-switches -ffile-prefix-map=$(pwd)=/builddir" | ||
| export CXXFLAGS="$CFLAGS" | ||
| export LDFLAGS="-Wl,--build-id=none" | ||
| export ARFLAGS=Drc | ||
|
|
||
| # THE use_sys_* DECISIONS ARE THE WHOLE JOB HERE, not the build. | ||
| # | ||
| # All seventeen `use_sys_*` options default to DISABLED, so out of the box | ||
| # rizin statically links its own copies of zlib, zstd, xz, lz4, pcre2, openssl | ||
| # and tree-sitter. Those copies are then INVISIBLE to pkgscan: a CVE in any of | ||
| # them would not appear against this package, because nothing in the tree | ||
| # declares them. For a distro whose entire premise is supply-chain vuln | ||
| # tracking, shipping seven silently-vendored libraries is the wrong default. | ||
| # | ||
| # So flip every one we actually package, and no more: | ||
| # zlib zstd lzma(xz) lz4 pcre2 openssl tree_sitter -> system | ||
| # | ||
| # Deliberately left VENDORED, with reasons: | ||
| # capstone rizin defaults to use_capstone_version=next, i.e. the | ||
| # unreleased capstone 6 — there is no released tarball that | ||
| # corresponds, so a "system" capstone would be a DIFFERENT | ||
| # disassembler than the one rizin was tested against. | ||
| # libzip, magic, zydis, xxhash, libmspack, softfloat, blake2, blake3 | ||
| # not packaged here; vendored is the only option today. Each is a | ||
| # future pkgscan blind spot, so they are named rather than left | ||
| # for someone to discover. | ||
| # | ||
| # --wrap-mode=nodownload is what makes the build hermetic: the official | ||
| # `rizin-src` tarball vendors its subprojects (capstone-next, tree-sitter, | ||
| # pcre2, lz4, nettle, blake2/3, libmspack, softfloat, ...), and this flag makes | ||
| # meson FAIL rather than reach for the network if one is ever missing. Note | ||
| # `liblzma` and `sigdb` ship as bare .wrap files, not bundled sources — the | ||
| # former is covered by use_sys_lzma below; the latter means no signature | ||
| # database, which is a real functional gap and not silently papered over. | ||
| meson setup \ | ||
| --prefix=/usr \ | ||
| --buildtype=release \ | ||
| --wrap-mode=nodownload \ | ||
| -Duse_sys_zlib=enabled \ | ||
| -Duse_sys_libzstd=enabled \ | ||
| -Duse_sys_lzma=enabled \ | ||
| -Duse_sys_lz4=enabled \ | ||
| -Duse_sys_pcre2=enabled \ | ||
| -Duse_sys_openssl=enabled \ | ||
| -Duse_sys_tree_sitter=enabled \ | ||
| .. | ||
|
|
||
| ninja | ||
|
|
||
| DESTDIR="$OUTPUT_DIR" ninja install |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Reject the GDB unknown-command result.
Line 59 passes when GDB prints
Undefined command: "gef"because that error containsgef. Line 62 does not reject this error. The test can pass whengef.pydid not register the command.Add an explicit assertion that rejects the unknown-command output.
Proposed fix
["/bin/bash", "-c", "gef -batch -ex 'gef help' 2>&1 | grep -qi 'gef'"], + ["/bin/bash", "-c", "! gef -batch -ex 'gef help' 2>&1 | grep -qi 'Undefined command'"], # A traceback on stderr means a partial import — gef would look # present while most commands were missing. ["/bin/bash", "-c", "! gef -batch -ex 'gef help' 2>&1 | grep -q 'Traceback'"],📝 Committable suggestion
🤖 Prompt for AI Agents