How can I generate an executable of an application written in Rust that was compiled into LLVM-IR bitcode?
If I try to compile the .bc file with rustc it tells me stream did not contain valid UTF-8
and I can't seem to figure out if there is a specific option in rustc for this.
Basically I want to achieve this:
program.rs
-> program.bc
-> program
.
Where program
is the final executable. What steps should I make to achieve this?
What is commonly known as the LLVM bitcode file format (also, sometimes anachronistically known as bytecode) is actually two things: a bitstream container format and an encoding of LLVM IR into the container format. The bitstream format is an abstract encoding of structured data, very similar to XML in some ways.
Tradeoff #4: LLVM and poor LLVM IR generationrustc uses LLVM to generate code. LLVM can generate very fast code, but it comes at a cost. LLVM is a very big system. In fact, LLVM code makes up the majority of the Rust codebase.
Yes. cargo/rustc use LLVM as a codegen backend by default, and likely will for the foreseeable future. However, there's ongoing work to support using rustc with alternative backends.
LLVM is designed around a language-independent intermediate representation (IR) that serves as a portable, high-level assembly language that can be optimized with a variety of transformations over multiple passes.
Starting with this source code:
fn main() {
println!("Hello, world!");
}
You can create LLVM intermediate representation (IR) or bitcode (BC):
# IR in hello.ll
rustc hello.rs --emit=llvm-ir
# BC in hello.bc
rustc hello.rs --emit=llvm-bc
These files can then be further processed by LLVM to produce assembly or an object file:
# Assembly in hello.s
llc hello.bc
# Object in hello.o
llc hello.bc --filetype=obj
Then you need to link the files to produce an executable. This requires linking to the Rust standard libraries. The path is platform- and version-dependent:
cc -L/path/to/stage2/lib/rustlib/x86_64-apple-darwin/lib/ -lstd-f4a73f2c70e583e1 -o hello2 hello.o
You can then run the program:
DYLD_LIBRARY_PATH=/path/to/stage2/lib/rustlib/x86_64-apple-darwin/lib/ ./hello2
This answer has macOS specific solutions, but the general concepts should be extendable to Linux and Windows. The implementation will differ slightly for Linux and probably greatly for Windows. Notably, I'm using DYLD_LIBRARY_PATH
as I've dynamically linked to the Rust standard library which isn't in my usual library search path.
Note that LLVM IR and BC files don't have the strongest forward / backward compatibility guarantees. This means that you need to use a version of llc
that is compatible with the version of rustc
you are using. For this answer, I used an llc
that was produced by my local Rust development build:
% rustc --version --verbose
rustc 1.53.0 (53cb7b09b 2021-06-17)
binary: rustc
commit-hash: 53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b
commit-date: 2021-06-17
host: x86_64-apple-darwin
release: 1.53.0
LLVM version: 12.0.1
% llc --version
LLVM (http://llvm.org/):
LLVM version 12.0.1-rust-dev
Optimized build.
Default target: x86_64-apple-darwin20.5.0
Host CPU: skylake
See also:
It isn't obvious since the LLVM documentation is very obscure, but clang will compile both LLVM IR files (".ll") and bitcode files (".bc"), and link with your system libraries.
On Linux with Rust 1.9:
clang -dynamic-linker /usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-d16b8f0e.so hello.ll -o hello
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With