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

267 lines
8.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Building fold
## TL;DR
```sh
./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:
```sh
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
```sh
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
```sh
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)
```sh
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)
```sh
./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
```sh
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):
```sh
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
```sh
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:
```sh
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:
```sh
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:
```sh
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`).
```sh
meson setup build/release ... -Drust-frontend=true
```
Disable when you don't have Rust installed or want a C++/binary-only build:
```sh
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.
```sh
meson setup build/release ... -Dswift-frontend=true
```
Disable when Swift is not installed or you want a C++/binary-only build:
```sh
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
```sh
./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.