Skip to content

Repository files navigation

rllvm

CI codecov crates.io

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-cc and build. No separator, no shim script, no build-system plugin.
  • Rust and cargo. Wrap cargo build and extract from the binary, dependency crates included.
  • WebAssembly. Whole-program bitcode from a linked wasm32 module, 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-info reports what a module contains.

Quick start

Install LLVM/Clang, then rllvm:

brew install llvm                                    # macOS
sudo apt install llvm llvm-dev clang libclang-dev    # Ubuntu / Debian

cargo install rllvm

Build something and extract its bitcode:

rllvm-cc -o hello hello.c
rllvm-get-bc hello              # produces hello.bc

Or point an existing project at it:

export CC=rllvm-cc CXX=rllvm-cxx

./configure && make             # autotools
cmake -B build && cmake --build build

rllvm-get-bc build/my_program

On first run rllvm writes a config with tool paths discovered from llvm-config.

Usage

Extracting

rllvm-get-bc hello                          # executable  -> hello.bc
rllvm-get-bc libfoo.a                       # archive     -> libfoo.a.bc
rllvm-get-bc -b libfoo.a                    # bitcode archive -> libfoo.bca
rllvm-get-bc --merge-strategy partial prog  # stage the merge by directory
rllvm-get-bc -m hello                       # also write hello.bc.manifest
rllvm-get-bc -o out.bc hello

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

cmake -B build -DCMAKE_TOOLCHAIN_FILE=path/to/rllvm/cmake/rllvm-toolchain.cmake
cmake --build build
rllvm-get-bc build/my_program

See examples/cmake/.

Rust and cargo

RUSTC_WRAPPER=rllvm-rustc cargo build
rllvm-get-bc target/debug/my_program

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:

rllvm-get-bc target/debug/deps/libmylib-<hash>.rlib

WebAssembly

rllvm-cc --target=wasm32-unknown-unknown -c -o lib.o lib.c
rllvm-cc --target=wasm32-unknown-unknown -nostdlib -Wl,--no-entry \
    -o app.wasm lib.o main.o
rllvm-get-bc app.wasm -o app.bc

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:

export RLLVM_BITCODE_ROOT=/path/to/build
make

# later, after the tree has moved:
rllvm-get-bc --bitcode-root /new/path/to/build prog -o prog.bc

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
llvm_config_filepath = '/opt/homebrew/opt/llvm/bin/llvm-config'
clang_filepath = '/opt/homebrew/opt/llvm/bin/clang'
clangxx_filepath = '/opt/homebrew/opt/llvm/bin/clang++'
llvm_ar_filepath = '/opt/homebrew/opt/llvm/bin/llvm-ar'
llvm_link_filepath = '/opt/homebrew/opt/llvm/bin/llvm-link'
log_level = 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.

License

Apache-2.0

About

Compiler wrappers for building whole-program LLVM bitcode files

Topics

Resources

Contributing

Stars

5 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages