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
5 changes: 5 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
### v1.2-dev
- `--capture rllvm` (or `REACHABILITY_CAPTURE=rllvm`) records bitcode with
rllvm 0.7.1 or newer instead of gllvm. Rust and mixed targets then take
their bitcode from one build and one extraction from the linked artifact,
so crates that only run at build time (`syn`, `quote`, `cc`, ...) are no
longer analyzed as part of the target.
- Support cargo 1.100's new build layout
- C/C++ analysis bitcode is now compiled at `-O0` in addition to
`-fno-inline -fno-inline-functions` (bitcode compile only, via
Expand Down
23 changes: 17 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,22 @@ flowchart TD
driver (Python) analyzer (C++ / LLVM)
─────────────── ─────────────────────
acquire bitcode ─┐
C/C++ : gllvm │ llvm-link load .bc → build call graph →
Rust : rustc ├─► merge .bc ─► resolve indirect calls → BFS from
--emit=llvm-bc ─┘ entry → JSON report + coverage lists
C/C++ : gllvm │
Rust : rustc │ llvm-link load .bc → build call graph →
--emit=llvm-bc ├─► merge .bc ─► resolve indirect calls → BFS from
│ entry → JSON report + coverage lists
or, with │
--capture rllvm: │
C/C++/Rust from ─┘
one build
```

The driver builds the project so that it emits bitcode, and merges it with
`llvm-link`. The analyzer builds the call graph, resolves indirect calls by
type and by tracking where function addresses flow, and walks the graph from
the entries; see [docs/analysis.md](docs/analysis.md).
`llvm-link`. With `--capture rllvm`, one build records the C, C++ and Rust
bitcode together, and `rllvm-get-bc` extracts it from the linked artifact. The
analyzer builds the call graph, resolves indirect calls by type and by
tracking where function addresses flow, and walks the graph from the entries;
see [docs/analysis.md](docs/analysis.md).

## Prerequisites

Expand All @@ -83,6 +90,9 @@ the entries; see [docs/analysis.md](docs/analysis.md).
especially for Rust.
- **Go** (to install gllvm), **Python 3.10 or newer**, and a **C++17**
compiler.
- Instead of gllvm: [rllvm](https://github.com/h1994st/rllvm) 0.7.1 or
newer, with `--capture rllvm`; see
[Capturing with rllvm](docs/usage.md#capturing-with-rllvm).
- For Rust targets: a nightly **rustc** and **cargo** whose LLVM is not newer
than yours.
- [AFL++](https://github.com/AFLplusplus/AFLplusplus) from commit
Expand All @@ -94,6 +104,7 @@ the entries; see [docs/analysis.md](docs/analysis.md).

```bash
bash scripts/setup.sh # gllvm, rust-src, .venv, analyzer build
REACHABILITY_CAPTURE=rllvm bash scripts/setup.sh # rllvm instead of gllvm
```

Or step by step: `make venv`, then `make build` (or `make build LLVM_MAJOR=23`).
Expand Down
11 changes: 8 additions & 3 deletions docs/analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ metric fields mean, and where the analysis is known to fall short.
driver (Python) analyzer (C++ / LLVM)
─────────────── ─────────────────────
acquire bitcode ─┐
C/C++ : gllvm │ llvm-link load .bc → build call graph →
Rust : rustc ├─► merge .bc ─► resolve indirect calls → BFS from
--emit=llvm-bc ─┘ entry → JSON report + coverage lists
C/C++ : gllvm │
Rust : rustc │ llvm-link load .bc → build call graph →
--emit=llvm-bc ├─► merge .bc ─► resolve indirect calls → BFS from
│ entry → JSON report + coverage lists
or, with │
--capture rllvm: │
C/C++/Rust from ─┘
one build
```

The **driver** (`driver/reachability/`) checks that the LLVM tools agree on one
Expand Down
4 changes: 3 additions & 1 deletion docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
bash scripts/setup.sh # gllvm 1.3.1, rust-src, .venv, analyzer build
```

`scripts/setup.sh` installs gllvm with `go install`, adds the `rust-src`
`scripts/setup.sh` installs gllvm with `go install` (with
`REACHABILITY_CAPTURE=rllvm`, rllvm 0.7.1 or newer with `brew install`, or
`cargo install` without Homebrew, unless a new enough one is on `PATH`), adds the `rust-src`
component, creates `.venv` with the driver (editable) and the pinned test
dependencies from `requirements-dev.txt`, and builds the analyzer. It does not
upgrade pip. `make venv` only creates the virtual environment.
Expand Down
4 changes: 3 additions & 1 deletion docs/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ Rust projects are built in one of two ways:

Both add `--emit=llvm-bc` to every rustc call and collect one bitcode file per
crate and codegen unit. rustc's LLVM must not be newer than the analyzer's;
see [llvm-support.md](llvm-support.md).
see [llvm-support.md](llvm-support.md). With `--capture rllvm`, both instead
take the bitcode from the linked artifact; see
[Capturing with rllvm](usage.md#capturing-with-rllvm).

The outputs on this page come from reachability 1.2-dev (commit `f5adeab`)
with LLVM 23 and rustc 1.99.0-nightly (2026-07-12, LLVM 22.1.8).
Expand Down
5 changes: 5 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ The run checks for `gclang`, `gclang++` and `get-bc` before it builds and lists
the missing ones. `scripts/setup.sh` installs gllvm; then put it on `PATH`:
`export PATH="$(go env GOPATH)/bin:$PATH"`.

With `--capture rllvm` it checks for `rllvm-cc`, `rllvm-cxx`, `rllvm-get-bc`
and `rllvm-info` instead, plus `rllvm-rustc` for Rust targets.
`REACHABILITY_CAPTURE=rllvm bash scripts/setup.sh` installs rllvm 0.7.1 or
newer with `brew install`, or with `cargo install` without Homebrew.

Test: `test_acquire_c.py::test_acquire_checks_gllvm_tools_before_building`.

### The build failed
Expand Down
67 changes: 63 additions & 4 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ reachability run --lang <target> --project <dir> [options]
| `--dot FILE` | none | Also write the reachable call graph as Graphviz DOT |
| `--no-edges` | off | Leave the `edges` array out of the report |
| `--build-cmd CMD` | detected | Shell command that builds the project, see [C and C++ builds](#c-and-c-builds) and [rust.md](rust.md#native-harness-builds) |
| `--artifact PATH` | detected | C/C++: the built file to take bitcode from, see [Artifact selection](#artifact-selection) |
| `--static-libs {auto,none,all}` | `auto` | C/C++: which static archives to analyze in full, see [Static libraries](#static-libraries) |
| `--capture {gllvm,rllvm}` | `gllvm` | Wrappers that record the bitcode, see [Capturing with rllvm](#capturing-with-rllvm) |
| `--artifact PATH` | detected | C/C++, or any target with `--capture rllvm`: the built file to take bitcode from, see [Artifact selection](#artifact-selection) |
| `--static-libs {auto,none,all}` | `auto` | C/C++, or any target with `--capture rllvm`: which static archives to analyze in full, see [Static libraries](#static-libraries) |
| `--optimize` | off | Analyze at the build's own optimization level, see [Source-faithful build](#source-faithful-build) |
| `--profile {debug,release}` | tool default | Rust build profile, see [rust.md](rust.md#profile-and-codegen-units) |
| `--codegen-units N` | detected | `rust` and `mixed`: rustc `-Ccodegen-units`, see [rust.md](rust.md#profile-and-codegen-units) |
Expand Down Expand Up @@ -85,7 +86,8 @@ only starts from `main`; a `staticlib` harness needs

`libfuzzer`, `ziggy` and `afl` build through the fuzzer's own command. `rust`
and `mixed` run a plain `cargo build`. Both are described in
[rust.md](rust.md).
[rust.md](rust.md). With `--capture rllvm`, every target takes its bitcode
from one build instead; see [Capturing with rllvm](#capturing-with-rllvm).

Test: `test_cli.py::test_target_entry_defaults`.

Expand Down Expand Up @@ -303,6 +305,62 @@ Tests: `test_acquire_c.py::test_build_env_injects_opt0_by_default`,
`::test_acquire_forwards_optimize_to_build_env`, and the
`test_report.py::test_optimization_advisory_*` tests.

## Capturing with rllvm

`--capture rllvm`, or `REACHABILITY_CAPTURE=rllvm`, records the bitcode with
[rllvm](https://github.com/h1994st/rllvm) 0.7.1 or newer instead of gllvm
(`brew install h1994st/tap/rllvm` or `cargo install rllvm`). `rllvm-cc`,
`rllvm-cxx`, `rllvm-rustc`, `rllvm-get-bc` and `rllvm-info` must be on `PATH`.
gllvm stays the default.

**C and C++.** The build runs with `CC=rllvm-cc` and `CXX=rllvm-cxx`. The
driver writes a private `RLLVM_CONFIG` that names the same `clang`, `clang++`
and `llvm-link` as for gllvm, and passes the
[source-faithful flags](#source-faithful-build), together with any
`LLVM_BITCODE_GENERATION_FLAGS`, as rllvm's `bitcode_generation_flags`, which
also apply to the bitcode compile only. Build command detection, artifact
selection and static libraries work as with gllvm. Archive members are matched
by the exact bitcode paths rllvm records rather than by object name: rllvm
names a bitcode file after its source and compilation
(`.parser_<hash>.o.bc`), not after the member object (`parser.o`).

**Rust and mixed.** rllvm also wraps rustc, so every target takes its bitcode
from one build and one extraction from the linked artifact, with no separate
Rust collection and no merge of the two:

| `--lang` | Build command | Wrapped |
|---|---|---|
| `rust` | `cargo build` | rustc |
| `libfuzzer`, `ziggy`, `afl` | the fuzzer's own command | rustc |
| `mixed` | the C/C++ build command | rustc, `CC`, `CXX` |

`--build-cmd` replaces the command in each case. A `mixed` build must link
the C/C++ code and the Rust crates into the artifact itself, for example
`--build-cmd 'cargo build && make && $CXX glue.o target/debug/libfoo.a -o fuzzer' --artifact fuzzer`.
Harness builds leave `CC` and `CXX` alone, so libFuzzer's C++ runtime stays
out of the report, and still run the clean command afterwards.

The rustc flags are the ones the per-crate collection adds: `-Copt-level=0`
unless `--optimize` (for `rust` and `mixed` with the profile's debug
assertions and overflow checks pinned), the `--mangling` flags, and
`--codegen-units` when given. Cargo applies the profile itself.

The bitcode is what the linker put into the artifact. Crates that only run at
build time, the dependencies of build scripts and proc macros such as `syn`,
`quote` or `cc`, are therefore not analyzed, whereas the per-crate collection
merges them in. A library-only crate yields only its own bitcode; point
`--artifact` at a binary that links it, such as the fuzz harness. The artifact
search also covers a cargo workspace's target directory and skips cargo's own
`deps/`, `build/` and `incremental/` copies in favor of the uplifted output.

Tests: the `test_acquire_c.py::test_rllvm_*` tests,
`::test_extract_bc_rllvm_writes_manifest_from_catalog`,
`::test_include_static_libs_rllvm_matches_members_by_bitcode_path`,
`::test_find_artifacts_prefers_cargo_uplifted_output`,
`::test_find_artifacts_searches_a_workspace_target_outside_the_project`,
`test_cli.py::test_run_static_archives_end_to_end` and
`test_reachable_sets.py::test_mixed_c_rust_reachable_from_one_rllvm_extraction`.

## Output files

The file formats are described in [output.md](output.md).
Expand Down Expand Up @@ -364,7 +422,8 @@ Tests: `test_cli.py::test_clean_c_removes_build_artifacts_and_outputs`,
|---|---|
| `REACHABILITY_ANALYZER` | Analyzer binary; default `analyzer/build/reachability-analyzer` in the repository |
| `CLANG`, `CLANGXX`, `LLVM_LINK` | Override one tool; otherwise the version matching the analyzer's LLVM is used (`clang-23`, …), then the plain name |
| `PATH` | Must contain gllvm's `gclang`, `gclang++` and `get-bc` for `c`, `cpp` and `mixed` |
| `PATH` | Must contain gllvm's `gclang`, `gclang++` and `get-bc` for `c`, `cpp` and `mixed`, or rllvm's tools with `--capture rllvm` |
| `REACHABILITY_CAPTURE` | Default for `--capture`, see [Capturing with rllvm](#capturing-with-rllvm) |
| `LLVM_BITCODE_GENERATION_FLAGS` | Extra flags for gllvm's bitcode compile, see [Source-faithful build](#source-faithful-build) |
| `RUSTFLAGS`, `CARGO_ENCODED_RUSTFLAGS` | Kept and merged with the driver's flags, see [rust.md](rust.md#plain-cargo-builds) |
| `CARGO_TARGET_DIR` | Honoured when collecting Rust bitcode |
Expand Down
Loading
Loading