Rellume: lifting x86-64, AArch64 and RISC-V machine code back into LLVM IR
Lift machine code to performant LLVM IR
At a glance
- What is it?
- Rellume is a lifter that takes compiled machine code and reconstructs an LLVM IR function with the same semantics, so that the LLVM optimiser can work on code that was never written in a high level language. Its focus is the performance of the lifted output, and its audience is people doing binary rewriting, instrumentation and binary analysis.
- Who is it for?
- Rellume suits a compiler or security engineer who needs to analyse or rewrite an existing binary using the LLVM toolchain, and it does not suit a general decompiler, because its output is LLVM IR for a specific instruction set rather than readable source.
- Can I use it commercially?
- Yes, with conditions. LGPL-2.1 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 43 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A lifter, not a decompiler
The repository description is one line: lift machine code to performant LLVM IR. The word lift is the key, and it distinguishes the project from what people usually mean by a decompiler.
A decompiler takes a binary and tries to produce source code a human can read. Rellume takes a binary and produces LLVM IR, which is not readable but is optimisable. The readme states the goal plainly: the generated LLVM IR can be compiled and executed again, for example using LLVM's JIT compiler, ideally having the same or even better performance as the original code.
That framing explains the whole design. If the output is going to be handed to an optimiser and a JIT, then the semantics have to be right in detail rather than approximately right in a human readable way, and the interesting problems become the ones a compiler person would recognise: how do you model a pointer so that alias analysis works, how do you model SIMD registers and memory so that the vectoriser fires, how do you keep the register state explicit enough that the optimiser can see through it.
The readme says special care is taken to model the SIMD instructions and pointers in a way that the optimizer can generate efficient code. That single sentence is the project's differentiator, because a lifter that produces semantically correct IR but models memory as an opaque byte array will get you correct answers and terrible performance.
The scope is three architectures: x86-64, AArch64 and RISC-V64, and the topics confirm the emphasis with llvm, llvm-ir and x86-86 tags.
How the lifted function is shaped
The mechanism has two halves, and the readme describes both.
The first half is the instruction selection. The lifter operates on a set of specified instructions, or decodes the control flow automatically, and creates an LLVM-IR function with the same semantics. So there are two modes: lift a list of instructions you name, or let it find the basic blocks itself.
The second half is the calling convention, and it is the part that determines how the lifted code is used. These functions operate on a generic structure containing the virtual CPU state, but can be wrapped for an arbitrary calling convention.
That is the key architectural idea. The lifted function does not take the original function's arguments. It takes a structure holding the CPU state, registers and flags, and the caller is responsible for marshalling real arguments into that state and reading results back out. A wrapper adapts a particular ABI to that generic interface.
This is why the project is useful for the applications listed. A binary rewriter does not need to call the original function with its original signature; it needs to intercept it, run the lifted body with modified state, and produce a result. Making the state explicit is what allows that. The same reasoning explains the instrumentation use case, where tracing and interception code is inserted into hot paths, and where high quality machine code is required because the hot path stays hot.
The repository layout matches a compiler component rather than a tool. There is a data/ directory that presumably holds the instruction definitions, src/ for the implementation, include/ for the public headers, tests/, subprojects/ for meson dependencies, a meson.build with a meson_options.txt, and a .gitmodules file for the submodules. There is also a Rellume.sublime-project, which is a small sign of a project developed in a text editor rather than an IDE.
The use cases are the argument for the design
The readme lists the applications under use cases, and they are worth reading as a set because each one is a case where having the original code in IR form is worth the lifting cost.
Binary rewriting is the first category, and it has three sub cases. Binary translation translates machine code to a different architecture while making use of compiler optimisations, which is implemented in Instrew. Performance improvement covers specialisation for runtime data, for example known parameters or memory locations, implemented in the LLVM back end of BinOpt. And instrumentation inserts tracing and interception code in hot code paths where high quality machine code is required.
The second category is binary analysis, and the argument here is short: existing tooling for analysis of LLVM IR code can be re-used for binary code. That is a large claim and it is the reason LLVM IR is the target rather than some other intermediate representation. Every LLVM pass, every analysis, every tooling chain that already understands LLVM IR now works on the binary.
The design follows from these uses. Binary translation wants the optimiser, because the whole point of routing through LLVM is that you get better code than a naive translator. Specialisation for runtime data wants the optimiser even more, since knowing a memory location or a parameter is exactly the kind of fact an optimiser can exploit. Instrumentation wants the optimiser not to slow the hot path down. And binary analysis wants a representation that existing tools already speak.
A lifter that emitted something other than LLVM IR would break all four of these at once, and the readme's mention of modelling SIMD and pointers for the optimiser's benefit is really a statement about which of the four the author had in mind.
The two sibling projects, Instrew and BinOpt, are by the same author, and both are described as being implemented on top of Rellume, which makes the framework relationship explicit rather than aspirational.
Building it, and what the examples show
The readme is thin on build instructions, and that is a real limitation for anyone starting here.
There is no install section and no command list. What the repository does tell you is that the build system is meson, from the meson.build file at the root and the meson_options.txt beside it, and the subprojects/ directory holds the meson subprojects the build depends on, with a .gitmodules file for the submodules themselves. The tests/ directory exists, and examples/ has a meson.build of its own.
The examples directory is the most useful thing in the tree for a newcomer, and the readme points at it directly: see examples/ for usage examples. There are three example files, and their names tell you the shape of the API. simple-x86-64.c is the minimal case. pic-x86-64.c is the position independent code case, which is the interesting one, because PIC is what a shared library uses and it is exactly the case where pointer modelling matters. And examples/meson.build shows how a client program is wired into the build.
There is a data/ directory at the root, and in a lifter like this it is where the per instruction definitions would live, since there are three architectures to support and each instruction needs its own semantics, its own register mapping and its own model of the flags. The .gitmodules entry and the presence of a subprojects/ directory both point at dependencies managed outside the main tree.
The publication record is the other way to understand the design, and it is substantial. There is a 2021 dissertation from the Technical University of Munich by Alexis Engelke titled Optimizing Performance Using Dynamic Code Generation, a VEE'20 paper with Martin Schulz on Instrew which the readme asks you to cite when referring to Rellume, and a HIPS 2017 workshop paper with Josef Weidendorfer on using LLVM for optimised lightweight binary re-writing at runtime.
Three papers spanning four years, all on the same theme of using LLVM to make binary manipulation fast, is a better guide to the design than the readme's single paragraph. If you are evaluating this, the dissertation is the one to read, and the readme is telling you so by listing it first.
Where Rellume is the wrong tool
There are four cases where this is the wrong choice, and the common thread is that lifting is the wrong strategy for the job.
The first is recovering readable source. Rellume produces LLVM IR, which is a compiler's intermediate form, not something a person reads. If the goal is to understand what a binary does, whether for a malware analysis report or for a licence audit, you want a decompiler that produces C-like output, and this project is not it. The readme's framing of binary analysis is specifically that existing LLVM IR tooling can be re-used, which is a different and narrower claim than understanding the binary.
The second is a whole program rather than a function. The lifter operates on a set of specified instructions, or decodes the control flow automatically, and it creates a function. Lifting a full executable means reconstructing the entry point, the runtime startup, the dynamic linker interactions, the syscall interface and the data section, and each of those is a problem the IR model does not solve. Rellume is built for lifting a body of code, not for lifting a program.
The third is a target it does not cover. Three architectures are named, x86-64, AArch64 and RISC-V64, and the readme says nothing about 32-bit x86, Windows kernel mode, or obfuscated binaries. It also says nothing about how a lifted function behaves when it calls out to code that was not lifted, and that boundary is where a lot of the real difficulty in binary rewriting lives.
The fourth is a licensing constraint. The project is LGPLv2.1+, which is a weak copyleft licence, and for a project that is linked into another tool that distinction matters. If you are building a commercial product that embeds Rellume, the LGPL obligations around relinking and source availability for modifications are the thing to check with a lawyer rather than with a summary in a review.
On the alternative, the difference in approach against a general purpose decompiler is the output. A decompiler makes the human the consumer of its output, and a lifter makes the compiler the consumer. That choice buys you the optimiser and every pass in the LLVM toolchain, and it costs you readability and generality, because you are now constrained to what the LLVM IR model can express and to the instruction semantics the author implemented.
The other alternative is not lifting at all. If you have the source, compile it to LLVM IR and skip the whole exercise. Lifting only makes sense where the source is unavailable, which is a security research or legacy binary scenario, and in that scenario the question of whether you need readable output or optimisable output is the one that decides the tool.
Editorial conclusion
Rellume suits a compiler or security engineer who needs to analyse or rewrite an existing binary using the LLVM toolchain, and it does not suit a general decompiler, because its output is LLVM IR for a specific instruction set rather than readable source. Before you adopt it, check that the instruction set and the operating system of your target binary are among those it handles, and read the examples/ directory and the papers before assuming it will recover a whole program, since the readme describes it as working on a specified instruction set with the control flow optionally decoded automatically.
Frequently asked questions
What is Rellume?
Rellume is a lifter that converts x86-64, AArch64 and RISC-V64 machine code into LLVM IR, so the original binary code can be recompiled or JIT compiled and analysed with existing LLVM IR tooling. The focus is on the performance of the lifted code.
What architectures does Rellume support?
The readme names x86-64, AArch64 and RISC-V64. The examples directory contains x86-64 examples, including a position independent code example, and and the data directory presumably holds the per instruction definitions.
How does Rellume handle calling conventions?
The generated functions operate on a generic structure containing the virtual CPU state, and can be wrapped for an arbitrary calling convention. The caller marshals real arguments into that state and reads the results back out.
What projects are built on Rellume?
Instrew, for binary translation and instrumentation, and the LLVM back end of BinOpt, for specialising binaries for runtime data such as known parameters or memory locations. The readme also lists a TUM dissertation and VEE'20 and HIPS 2017 papers.
What licence is Rellume released under?
Rellume is licensed under LGPLv2.1+, as stated in the readme, with the LICENSE file in the repository root.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/aengelke-rellume)Was this useful?