rllvm
Extract whole-program LLVM bitcode from any build.
Point your build system at rllvm's compiler wrappers, build normally, then pull a
single .bc for the whole program back out of the finished binary.
Features
- Drop-in compiler wrappers.
export CC=rllvm-ccand build. No separator, no shim script, no build-system plugin. - Rust and cargo. Wrap
cargo buildand extract from the binary, dependency crates included. - WebAssembly. Whole-program bitcode from a linked
wasm32module, not just from individual objects. - Relocatable bitcode paths. Objects normally pin themselves to the directory that built them. Record paths relative to a root instead, and extraction keeps working after the tree moves, comes out of a container, or is replayed from a compiler cache.
- Merge strategies. Link everything into one module, stage the merge by directory for large projects, or produce a bitcode archive.
- Single static binary. No runtime to install;
cargo install rllvm. - Bitcode inspection.
rllvm-inforeports what a module contains.
Quick start
Install LLVM/Clang, then rllvm:
Build something and extract its bitcode:
Or point an existing project at it:
&& &&
On first run rllvm writes a config with tool paths discovered from llvm-config.
Usage
Extracting
Wrapper flags
Wrapper options are long-only and prefixed --rllvm-, so they cannot collide
with a compiler flag. Everything else — including -c, -v, --help and
--version — goes straight to the compiler, because build systems identify the
compiler by running $CC --version.
--rllvm-compiler <PATH> Override the wrapped compiler path
--rllvm-verbose[=LEVEL] Log verbosity; bare flag is level 1, max 4
--rllvm-help Print help for the wrapper
--rllvm-version Print the wrapper version
A -- separator is still accepted, so existing shim scripts keep working.
CMake toolchain file
See examples/cmake/.
Rust and cargo
RUSTC_WRAPPER=rllvm-rustc
Every crate in the graph contributes, so the extracted module covers dependencies as well as the binary's own code. A library crate works on its own:
WebAssembly
Linking needs wasm-ld, which ships with LLD rather than LLVM and must match
your LLVM version. See examples/wasm/.
Relocatable bitcode paths
By default an object records the absolute path of its bitcode, which pins it to the directory that built it. Set a root to record paths relative to it, then name the root again when extracting:
# later, after the tree has moved:
Objects built without a root keep absolute paths and are unaffected — the extractor tells the two apart by the leading separator, so both forms can appear in the same binary.
Configuration
A TOML file, created on first run with paths inferred from llvm-config. It
lives at $RLLVM_CONFIG if set, otherwise ~/.rllvm/config.toml.
| Key | Required | Description |
|---|---|---|
llvm_config_filepath |
Yes | Absolute path to llvm-config |
clang_filepath |
Yes | Absolute path to clang |
clangxx_filepath |
Yes | Absolute path to clang++ |
llvm_ar_filepath |
Yes | Absolute path to llvm-ar |
llvm_link_filepath |
Yes | Absolute path to llvm-link |
llvm_objcopy_filepath |
No | Absolute path to llvm-objcopy; preferred for embedding, with an internal fallback |
rustc_filepath |
No | Absolute path to rustc (default: rustc on PATH) |
bitcode_store_path |
No | Directory for intermediate bitcode files (must be absolute) |
bitcode_root |
No | Record embedded paths relative to this root (default: absolute) |
llvm_link_flags |
No | Extra flags for llvm-link |
lto_ldflags |
No | Extra flags for link-time optimization |
bitcode_generation_flags |
No | Extra flags for bitcode generation (e.g. -flto) |
is_configure_only |
No | Skip bitcode generation entirely (default: false) |
cache_enabled |
No | Reuse bitcode across rebuilds; also RLLVM_CACHE=1 (default: false) |
log_level |
No | 0=error (default), 1=warn, 2=info, 3=debug, 4+=trace |
= '/opt/homebrew/opt/llvm/bin/llvm-config'
= '/opt/homebrew/opt/llvm/bin/clang'
= '/opt/homebrew/opt/llvm/bin/clang++'
= '/opt/homebrew/opt/llvm/bin/llvm-ar'
= '/opt/homebrew/opt/llvm/bin/llvm-link'
= 3
How it works
The wrappers run clang normally and, for each source, also emit a .bc. The
absolute path of that .bc is written into a custom section of the object file,
newline-terminated. The linker concatenates those sections, so the finished
binary carries a list of every translation unit that went into it.
rllvm-get-bc reads that list and links the bitcode into one module.
source.c ──► rllvm-cc ──► object file (with embedded .bc path)
│
▼
executable ◄── linker ◄── object files
│
▼
rllvm-get-bc ──► whole-program.bc
rllvm-rustc does the same per crate. A crate that links carries the path in a
marker object added to the link; a crate that produces an .rlib carries it in
the archive's members, so a dependency brings its bitcode wherever it is used.
Relationship to gllvm and wllvm
rllvm started as a Rust port of gllvm (Go)
and wllvm (Python), and keeps
the same workflow: set CC/CXX, build, extract. It has since added
WebAssembly support, a Rust wrapper, relocatable bitcode paths, and merge
strategies.
If gllvm or wllvm already work for you, there is no urgency to switch.