Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions packages/gef/build.ncl
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'"],
Comment on lines +59 to +62

Copy link
Copy Markdown
Contributor

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 contains gef. Line 62 does not reject this error. The test can pass when gef.py did 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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
["/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'"],
["/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'"],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/gef/build.ncl` around lines 59 - 62, Update the GDB validation
commands in the build test to explicitly reject output indicating `Undefined
command: "gef"` (or the equivalent unknown-command message). Ensure the check
around the existing `gef help` invocation fails when the command is unavailable,
while preserving the traceback rejection and successful registered-command
behavior.

],
} | Test,
},

attrs =
{
upstream_version = version,
license_spdx = "MIT",
source_provenance = {
category = 'GithubRepo,
owner = "hugsy",
repo = "gef",
},
} | Attrs,
} | BuildSpec
40 changes: 40 additions & 0 deletions packages/gef/build.sh
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"
139 changes: 139 additions & 0 deletions packages/rizin/build.ncl
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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.ncl

Repository: 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
done

Repository: 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 -300

Repository: gominimal/pkgs

Length of output: 29875


Remove redundant build_deps entries.

toolchain already exposes gcc and glibc through its transitive runtime dependencies. Remove direct gcc and glibc entries from build_deps, but keep glibc in runtime_deps.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/rizin/build.ncl` around lines 34 - 40, Remove the direct gcc and
glibc entries from the build_deps list in the package definition, since
toolchain supplies them transitively; retain glibc in runtime_deps and leave the
other build dependencies unchanged.

Source: 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
64 changes: 64 additions & 0 deletions packages/rizin/build.sh
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
Loading