Unicorn Engine: one C API over ten architectures, and where the packaging stops
Unicorn CPU emulator framework (ARM, AArch64, M68K, Mips, Sparc, PowerPC, RiscV, S390x, TriCore, X86)
At a glance
- What is it?
- Unicorn is a GPLv2 CPU emulator framework written in pure C on top of QEMU, with an architecture-neutral API and bindings for fifteen languages. Its front page carries no install command at all, hands the build to three files under docs, and its Rust manifest deliberately ships a fraction of the repository, so the interesting decisions live in the manifests rather than in the documentation.
- Who is it for?
- Unicorn fits when you need one C API across ARM, MIPS, RISC-V, x86 and the rest, and you are willing to read docs/COMPILE.md yourself before anything compiles.
- Can I use it commercially?
- Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 3 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 November 13, 2026, and from our analysis. They are not legal advice.
Star history
- unicorn-engine/unicorn9,572 ★ · +210
| Repository | 2026-09-22 | 2026-11-13 |
|---|---|---|
| unicorn-engine/unicorn | 9.4k | 9.6k |
210 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
One C core, ten architectures, and a QEMU tree carried beside it
Unicorn is written in pure C and sits on top of QEMU rather than next to it. The repository root shows how that is arranged: there is a qemu/ directory and a .gitmodules file at the same level as uc.c, list.c and include/, so the small architecture-neutral API you link against and the machine models underneath it are two separate pieces of source. What that arrangement gives a reader is one interface instead of ten, described as clean, simple, lightweight and intuitive. It also means the build carries the QEMU tree with it, and the root CMakeLists.txt, cmake/, msvc/, build.rs, build.zig and build.zig.zon all sit beside it to serve the different toolchains the bindings expect. AUTHORS.TXT and CREDITS.TXT record who wrote it, and format.sh and .clang-format sit alongside them for the formatting pass.
The samples directory doubles as an index of what the API reaches
samples/ is the closest thing to a manual inside the tree, and its file list is an inventory of the surface rather than a set of tutorials. There is one entry per architecture: sample_arm.c, sample_arm64.c, sample_m68k.c, sample_mips.c, sample_ppc.c, sample_riscv.c, sample_s390x.c, sample_sparc.c, sample_tricore.c and sample_x86.c. Four more files are named after a subsystem instead of a machine, namely mem_apis.c, sample_ctl.c, sample_batch_reg.c and sample_mmu.c. One filename carries the full detail, sample_x86_32_gdt_and_seg_regs.c, which points at 32-bit x86 descriptor table and segment register setup. samples/sample_all.sh and samples/Makefile exist to run the set. The catch for a package consumer is that none of these files travel with the published Rust crate.
Pull requests target dev, while master is the branch you clone
The default branch is master, and that is where the release history lives. Contributions go somewhere else: the project asks for pull requests against the dev branch and keeps the harder problems listed in milestones for the next regular release. The practical difference is that master and dev are not the same target. The most recent tag is 2.1.4, dated 2025-09-09, after 2.1.3 in March 2025 and 2.1.2 in February 2025, while the last push to the repository landed on 2026-10-18. Track the tag and you get 2.1.4; track dev and you get whatever has been merged since, with no version bump to tell you it changed. The front page also carries an open call for contributors pointing at issue 2237, so the project is telling you where its own gaps are.
The README hands you a documentation path instead of an install command
There is no install line anywhere in the README, and the project says plainly where the answer lives: docs/COMPILE.md for how to compile and install Unicorn, docs/README.md for more documentation, and docs/FAQ.md for common questions, which it asks you to read before raising an issue. None of those steps is reproduced on the front page, so there is no pinned dependency version, no package manager recipe, and no supported release matrix to copy. What is stated instead is the platform claim, native support for Windows and *nix with Mac OSX, Linux, Android, *BSD and Solaris confirmed, plus a pointer to http://www.unicorn-engine.org for everything else. The consequence is that your first ten minutes go into reading three files rather than pasting one line, and a reproducible setup has to be written by whoever installs it.
The Rust package ships the sys crate and drops most of the repository
Cargo.toml turns the repository into a workspace with a single member, bindings/rust/unicorn-engine, and a root package named unicorn-engine-sys whose library path is bindings/rust/sys/src/lib.rs. The version recorded there is 2.1.5, ahead of the 2.1.4 tag, the edition is 2024, and rust-version is pinned at 1.85.0. Two lines matter more than the version. The package sets links = "unicorn", which tells Cargo the native library belongs to the link step, and its exclude list drops /docs, /samples, /tests and nine binding directories: dotnet, go, haskell, java, pascal, python, ruby, vb6 and zig. So what a Rust user pulls through that manifest leaves out the sample files you would read to learn the API and the other language bindings.
go.mod declares 1.17 while the Rust workspace demands 1.85
go.mod is two meaningful lines long: module github.com/unicorn-engine/unicorn, go 1.17. The module path is the repository path itself rather than a versioned subpackage, and its Go language floor of 1.17 is unrelated to the Rust workspace's rust-version of 1.85.0. Cargo.toml records version 2.1.5, while go.mod's 1.17 is a Go language version, not a library version. The practical effect for a team choosing a binding is that the minimum toolchain is set per language rather than by the project. The categories in Cargo.toml also advertise no-std, a claim that covers the Rust side of the tree only.
JIT speed and instrumentation are listed as features, not as per-architecture guarantees
Two of the strongest claims sit in a bulleted list with no detail underneath. One is high performance via Just-In-Time compilation, the other is support for fine-grained instrumentation at various levels. The page names ten architectures across 16, 32 and 64 bit and calls the API architecture-neutral, but it does not say which of those ten carry instrumentation hooks, nor whether the Just-In-Time path exists on every confirmed platform including Solaris and Android. Thread-safety by design is listed the same way, with no statement about which objects are safe to share between threads. The consequence is that none of these claims can be checked from the project page: if your target is one of the less common machines, you have nothing to plan against and the front page will not tell you what to turn off.
Editorial conclusion
Unicorn fits when you need one C API across ARM, MIPS, RISC-V, x86 and the rest, and you are willing to read docs/COMPILE.md yourself before anything compiles. Before you build on it, check which of the ten architectures actually carry the instrumentation you need, confirm your Just-In-Time backend exists on your target platform, and read the exclude list in Cargo.toml if the Rust crate is your entry point, because the samples and the other language bindings are not in that package.
Frequently asked questions
how to install unicorn
The README carries no install command of its own. It points at docs/COMPILE.md for how to compile and install Unicorn, docs/README.md for more documentation, and docs/FAQ.md for common questions that it asks you to read before raising an issue.
Which architectures does Unicorn Engine emulate?
Ten of them in 16, 32 and 64 bit forms: ARM, ARM64 (ARMv8), M68K, MIPS, PowerPC, RISCV, SPARC, S390X, TriCore and X86. Each machine has its own file under samples/.
Which language bindings does Unicorn Engine ship?
Bindings exist for Crystal, Clojure, Visual Basic, Perl, Rust, Ruby, Python, Java, .NET, Go, Delphi/Free Pascal, Haskell, Pharo, Lua and Zig. The emulator core itself is pure C, and the API is described as architecture-neutral.
What license is Unicorn Engine released under?
GPLv2. The repository carries COPYING, COPYING.LGPL2 and COPYING_GLIB at the root, and both the README and the Rust workspace manifest in Cargo.toml state GPL-2.0.
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/unicorn-engine-unicorn)Star history chart for your README
[](https://hysenlabs.com/projects/unicorn-engine-unicorn)Was this useful?