The README listed 11 of fold's 17 detectors, never explained the confidence tiers every finding carries, did not mention the new exit code 2 / coverage reporting, and spent half its length on a performance roadmap. It now says what fold is, defines the five tiers (from include/fold/report/finding.hpp, including concrete-witness and latent), what "bounded" means after the loop-exit fix, a quick start, output formats and exit codes (0 none, 1 any unsuppressed finding, 2 unwritable report or any unanalysed translation unit), and links. The detector list defers to the complete table in docs/USAGE.md. The performance section moves to docs/architecture/performance.md, with one correction: the gold set was derived from fold's own reviewed output and is matched by detector kind and file, so it catches drift rather than measuring precision/recall independently. The lint pre-commit hook note moves to BUILD.md.
8.6 KiB
Building fold
TL;DR
./setup.sh release # autodetects LLVM on macOS or Linux
# binary at: build/release/src/cli/fold
# tests: meson test -C build/release
setup.sh also enables the tracked lint pre-commit hook
(.githooks/pre-commit).
Linux users without root can validate against a clean container:
docker build -t fold-linux-validate -f docker/Dockerfile.linux-validate .
docker run --rm -v "$PWD":/workspace:ro fold-linux-validate
The Dockerfile is the executable spec of the Linux dependency set.
If you change a system dependency, change it there too — the GitHub
Actions Linux job (.github/workflows/build.yml) installs the same
packages.
Required system dependencies
These must be installed by the user before ./setup.sh runs. Conan
handles the rest (spdlog, CLI11, nlohmann_json, sqlite3, Catch2).
LLVM / Clang
- Version range: 18–22 (19 in CI, 22 on the macOS dev machine).
- Provides
clang,clang++,llvm-config, and the libclang development headers fold's frontend builds against.
Z3
- Why: the SMT solver fold queries for every symbolic predicate.
- Linux users must install
libz3-devsystem-wide. On macOS, Homebrew'sz3formula installs both the library and thez3.pcpkg-config file that meson consumes.
Build tooling
- Meson ≥ 1.2, Ninja, pkg-config, Python 3, Conan 2.x.
Optional
- radare2 dev headers (
r_core.pc): linked binary backend. Without it fold falls back to ther2CLI at runtime. - sqlite3 dev headers: needed for the incremental-scan store.
Per-platform install commands
Ubuntu / Debian
sudo apt-get install \
clang-19 llvm-19-dev libclang-19-dev libclang-cpp19-dev \
libz3-dev libsqlite3-dev \
meson ninja-build pkg-config \
nlohmann-json3-dev libcli11-dev libspdlog-dev libfmt-dev \
python3-pip
pip3 install --user 'conan>=2,<3'
conan profile detect --force
Replace 19 with another version (18–22) if you prefer; setup.sh
probes /usr/lib/llvm-{22..18}/bin/llvm-config newest-first.
Fedora / RHEL
sudo dnf install \
clang clang-devel llvm-devel \
z3-devel sqlite-devel \
meson ninja-build pkgconf-pkg-config \
python3-pip
pip3 install --user 'conan>=2,<3'
conan profile detect --force
macOS (Homebrew)
brew install llvm@22 z3 meson ninja pkg-config conan
conan profile detect --force
setup.sh finds Homebrew LLVM automatically (/opt/homebrew/Cellar/llvm/*
on Apple Silicon, falls back to brew --prefix llvm@N).
Build steps
Quick build (recommended)
./setup.sh release
setup.sh autodetects LLVM, runs conan install, runs meson setup,
and invokes ninja. Pass debug instead of release for a debug
build.
It builds the vendored Ghidra P-Code binary backend by default
(-Dghidra=auto — the decompiler is vendored under third_party/, so auto
always builds it; this powers the SSA P-Code pipeline and the ROP exploit
primitives). It adds noticeable build time (compiling the decompiler and
generating the per-arch .sla specs); pass -Dghidra=disabled for a lighter
baseline that uses the r2-disasm fallback.
It also enables Capstone-backed ROP gadget discovery (-Dcapstone=auto
plus --wrap-mode=default). Capstone is built from the vendored wrap
(subprojects/capstone.wrap) as a CMake subproject, so this needs cmake and
network access on first configure; the --wrap-mode=default flag is required to
override the wrap_mode=nofallback that Conan's native file sets. If Capstone
can't be built (offline, or no cmake), the build still succeeds and gadget
discovery falls back to the radare2 /R search — setup.sh prints the resolved
status (Capstone ROP gadget discovery: ENABLED|disabled). A system Capstone
(brew install capstone) is used instead of the wrap when present.
For the manual build below, add -Dghidra=auto --wrap-mode=default -Dcapstone=auto to the meson setup line to get the same behavior.
Manual build
conan install . --output-folder build/release --build=missing \
-s build_type=Release -o build_tests=True
meson setup build/release \
--native-file build/release/conan_meson_native.ini \
-Dcpp_std=c++23 -Dbuild-tests=true \
-Dllvm-config-path=/path/to/llvm-config
meson compile -C build/release
On Linux, also symlink z3.pc into the build dir if pkg-config can't
find it automatically (setup.sh does this for you):
ln -sf /usr/lib/$(uname -m)-linux-gnu/pkgconfig/z3.pc build/release/z3.pc
Environment overrides
| Variable | Effect |
|---|---|
LLVM_BIN |
Override the autodetected LLVM bin directory (e.g. /opt/clang-20/bin). |
LLVM_CONFIG |
Full path to llvm-config. Threaded into -Dllvm-config-path. |
CC, CXX |
Set the C / C++ compilers used by Conan + meson. Defaults to the discovered clang. |
Tests
meson test -C build/release
When -Dbuild-tests=true is set, Fold also defines FOLD_ENABLE_DCHECK=1 so
unit/integration tests exercise debug-only invariant checks. For an independent
non-test canary or benchmark build with debug checks enabled, configure it with
its own Conan/Meson files:
conan install . --output-folder build/dcheck --build=missing \
-s build_type=Release -o build_tests=False
meson setup build/dcheck \
--native-file build/dcheck/conan_meson_native.ini \
-Dcpp_std=c++23 -Denable-dcheck=true
ninja -C build/dcheck
To toggle debug checks in an existing build dir:
meson configure build/release -Denable-dcheck=true
meson compile -C build/release
The optional binary_backend integration test uses radare2 and a real
binary. Defaults to /usr/bin/curl when available:
FOLD_TEST_BINARY=/path/to/binary meson test -C build/release binary_backend
The test skips itself when no suitable binary or r2 command is
present.
The binary P-Code / ROP pipeline is covered by test_binary_rop (gadget
discovery over the committed ELF fixtures in tests/fixtures/binary/, built only
when Capstone is enabled) and the sast_security_e2e test (security detectors
over tests/fixtures/security/). The full whole-binary BOF → ROP-chain scan is a
manual step (build/release/pcode_scan <binary>) — it takes minutes, so it is
not part of the automated suite.
Optional backends
Rust frontend (-Drust-frontend=true)
Builds fold-rust (a Cargo binary under tools/fold-rust/) and links
RustBackend into fold. Enables fold scan --mode rust.
Requires: nightly Rust toolchain (hardcoded to nightly-2026-05-28).
meson setup build/release ... -Drust-frontend=true
Disable when you don't have Rust installed or want a C++/binary-only build:
meson setup build/release ... -Drust-frontend=false
When disabled, --mode rust scans fail with a build-time-disabled frontend error; Rust detector names are still accepted by --detectors but produce no findings in non-Rust modes.
Swift frontend (-Dswift-frontend=true)
Builds fold-swift (a C++ binary under tools/fold-swift/) and links
SwiftBackend into fold. Enables fold scan --mode swift.
Runtime requirement: swiftc on PATH when running Swift scans.
meson setup build/release ... -Dswift-frontend=true
Disable when Swift is not installed or you want a C++/binary-only build:
meson setup build/release ... -Dswift-frontend=false
Compatibility matrix
| Option | Default | Requires | Enables |
|---|---|---|---|
rust-frontend |
true |
nightly Rust | --mode rust scans, Rust IR symbolic encoding |
swift-frontend |
true |
swiftc at scan runtime |
--mode swift scans, Swift SIL symbolic encoding |
ghidra |
auto |
vendored (always available) | P-Code binary pipeline, ROP primitives |
capstone |
auto |
cmake + network | Exhaustive ROP gadget discovery |
radare2 |
auto |
r_core.pc |
Linked binary backend (falls back to r2 CLI) |
Coverage
./scripts/coverage.sh
Builds an instrumented build/coverage (debug, ghidra + capstone), runs the
suite, and writes a line/function coverage report to
build/coverage/coverage.{txt,html} (terminal summary printed too). Vendored
code under third_party/ and subprojects/ is excluded. Uses gcovr via uvx
(no system install) and clang's llvm-cov gcov; set LLVM_BIN if LLVM isn't on
the Homebrew/PATH default.
Validation
The Linux container at docker/Dockerfile.linux-validate runs the
same pipeline against a clean Ubuntu 24.04 + clang-19 environment.
Use it locally before pushing if you don't have a Linux host. See
docker/run-validate.sh for the exact steps.
More
docs/USAGE.md— runtime invocation, output formats, MCP/LSP.docs/BUILDING.md— radare2 integration, vendoring notes.