fold/BUILD.md
trvon 2945e59bc2
docs: trim the README and move the performance roadmap to docs/
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.
2026-09-24 21:11:54 -06:00

8.6 KiB
Raw Permalink Blame History

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-dev system-wide. On macOS, Homebrew's z3 formula installs both the library and the z3.pc pkg-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 the r2 CLI 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

./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.