selfie: a compiler, an emulator and a hypervisor that all contain themselves
An educational software system of a tiny self-compiling C compiler, a tiny self-executing RISC-V emulator, and a tiny self-hosting RISC-V hypervisor.
At a glance
- What is it?
- Twelve thousand lines of C in a single file implement a self-compiling compiler, a self-executing RISC-V emulator and a self-hosting hypervisor, plus the teaching material, an autograder and three bachelor theses that ship as release tags.
- Who is it for?
- Selfie is the rare teaching project where the constraint did the work. One file, 12KLOC, no build dependencies beyond a C compiler, and every one of the four subsystems able to contain the other three.
- Can I use it commercially?
- Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 9 days ago.
- What is it written in?
- Mainly Jupyter Notebook, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The name is a commitment, not a joke
Selfie is a project of the Computational Systems Group at the Department of Computer Sciences of the University of Salzburg in Austria, and the README explains the name directly. The common theme is to identify and resolve self-reference in systems code, which is seen as the key challenge when teaching systems engineering, hence the name.
That is a sharper claim than it first appears. Most teaching systems demonstrate a compiler, or demonstrate an emulator, or demonstrate a hypervisor, and each demonstration sits beside the others. Selfie instead makes self-reference the load-bearing property of every layer, so that a single idea can be taught once and then felt in four different places. A self-compiling compiler is self-reference in the compilation stage. A self-executing emulator is self-reference at the interpretation stage. A self-hosting hypervisor is self-reference at the virtualization stage. Once a student has followed one of those loops all the way round, the other three stop being separate techniques and start being the same technique at different altitudes.
The repository description says the same thing more compactly: an educational software system of a tiny self-compiling C compiler, a tiny self-executing RISC-V emulator, and a tiny self-hosting RISC-V hypervisor. Three subsystems, one adjective repeated three times, and the repetition is the design. BSD-2-Clause licensed, 2525 stars, 355 forks, and only 17 open issues, which is a small number for a repository of this size and says something about how settled the scope is. Not archived, on main, last pushed 2026-09-27.
One small oddity is worth noting because it misleads people browsing github. The repository language is detected as Jupyter Notebook, which is accurate for the docs directory and the examples but wildly misleading for the actual system, which is C concentrated in one file called selfie.c. If you are filtering by language to find systems code, this repository will not surface.
Four programs inside one twelve thousand line file
The README describes Selfie as a self-contained 64-bit, 12KLOC C implementation of four things. The first is starc, a self-compiling compiler that compiles a tiny but still fast subset of C called C Star, written C*, into a tiny and easy-to-teach subset of RISC-V called RISC-U. The second is mipster, a self-executing emulator that executes RISC-U code including itself when compiled with starc. The third is hypster, a self-hosting hypervisor that provides RISC-U virtual machines which can host all of selfie, meaning starc, mipster and hypster itself. The fourth is libcstar, a tiny C* library used by selfie.
The three language definitions live as plain markdown at the repository root, which is a good sign about the project's priorities. semantics.md gives the operational meaning of C*, grammar.md gives its syntax, and riscu.md covers the instruction subset. A student can therefore read the specification of the input language without installing anything, which matters because the whole point is that the artefact is small enough to hold in your head.
Alongside those four, the same file carries what the README calls the supporting machinery: a simple in-memory linker, a RISC-U disassembler, a garbage collector, L1 instruction and data caches, a profiler, and a debugger with replay, plus minimal operating system support in the form of RISC-V system calls built into both the emulator and the hypervisor. The caches and the system calls are what make the emulator a plausible host rather than a toy, and having them inside the same 12KLOC is the reason the system can host itself without becoming a distributed project.
The garbage collector deserves its own sentence because it is unusual. It is conservative, it is O(n^2), and the README says it is even self-collecting. It may operate as a library in the same address space as the mutator, or as part of the emulator in the address space of the kernel. A collector that can collect itself is another self-reference loop, and the same design decision would be absurd in production software and is exactly right here, where the pedagogy is the specification.
The output is real, which is the check that matters for a teaching system. Selfie generates ELF binaries that run on actual RISC-V hardware as well as on QEMU, and they are compatible with the official RISC-V toolchain, specifically the spike emulator and the pk kernel. A compiler that only compiles for itself teaches less than one that can also be checked against an independent reference.
The 32-bit mode is a correctness claim, not a convenience
Selfie is designed as a 64-bit system and requires a 64-bit host to run, using the LP64 data model. It also compiles on systems that support compiling and executing 32-bit binaries under the ILP32 data model, and in that case selfie becomes a 32-bit system that generates and executes 32-bit binaries out of the box. The reason given is specific: this is possible because the implementation of selfie carefully avoids 32-bit overflows throughout the system.
Read that last clause as the requirement it is. A 12KLOC compiler and emulator that runs its own hypervisor on top of its own emulator has to be correct about widths at every arithmetic site, and a hidden assumption that pointers and integers share a width will not show up on the 64-bit path that most developers test on. Running the entire self-hosting stack in 32-bit mode is therefore an unusually cheap and unusually thorough consistency check, and it is a good example of a teaching constraint that exists to catch a class of bug rather than to support a platform.
The Makefile shows the machinery, and it is short. The default flags are -Wall -Wextra -O3 with a define that maps uint64_t to unsigned long, and the bootstrap rule compiles selfie.c into an executable named selfie. A separate target, selfie-32, repeats the bootstrap with -m32 and adds -Wno-builtin-declaration-mismatch alongside the width define. That third flag is a small tell: the compiler complains about builtin declarations disagreeing with the hand-rolled integer model, and the project has decided the disagreement is expected rather than wrong.
The rest of the Makefile is pattern rules that show the system compiling itself. A rule of the form %.m: %.c selfie runs ./selfie -c to produce RISC-U executable output, and a parallel %.s rule adds -s to emit assembly. There is also a rule that generates selfie.h from selfie.c by rewriting the main function to selfie_main, which is the mechanism by which the system becomes linkable into itself. Another chain of sed commands produces selfie-gc.h, renaming gc_init, allocate_memory, mark_block and sweep to versions suffixed _deleted so an alternative collector can be substituted. The build is legible at a glance, which is the correct property for a repository whose purpose is to be read.
The teaching apparatus is larger than the code
For a system whose code is one file, the amount of scaffolding around it is striking. The support section lists a Slack channel at cksystemsteaching.slack.com, a home page at selfie.cs.uni-salzburg.at, a paper from Onward! 2017, and a book in the book directory called What is Intelligence? Discovering Unproven Truth, with a previous edition titled Elementary Computer Science: From Bits and Bytes to the Universality of Computing.
The teaching claim is stated with unusual directness. There are three classes built on selfie, an introduction to computer science, a compiler class and a systems class, each with fourteen HTML decks and PDFs, all telling one story along one axis from the small to the vast to the countable to the uncountable. The stated purpose is a deep understanding of basic computer science principles, deep enough to position generative AI, and whatever comes next, properly. That last clause is a deliberate positioning decision rather than an aside, and it explains why a systems project would ship a companion lecture on What is Intelligence? developing the same proof-versus-truth distinction through size, infinity, Gödel, Turing, universality and complexity for an audience in any field.
The autograder is the part that turns a research artefact into coursework. It lives in grader/README.md and carries compiler and operating systems assignments, including two that turn selfie's model checker on on the students own code. Running your own bounded model checker against student submissions is a genuinely good idea, because it means the grader does not merely compare answers but exercises the submitted code.
The releases make the research lineage unusually legible, because there are exactly three and each is a bachelor thesis pinned to a commit. The tag bachelor_thesis_wulz from 2021-10-28 references Linear-Time Static Analysis of RISC-V Binary Code by Thomas Wulz. bachelor_thesis_pape from 2022-01-03 is a code release for RISC-U Binary Optimization for Selfie by David Pape. bachelor_thesis_thiele from 2023-01-18 references Automated Testing of Atomic Instructions, LR/SC Implementations in Selfie, by Luis Thiele. Tagging a commit rather than cutting a version is the right convention here: these are pointers into history that preserve work worth citing, not releases anyone should install.
The examples directory is a syllabus in its own right, with hello-world.c next to hello-world-minified.c, count.c and countdown.c for iteration, pointer.c and local.c for memory, bitwise.c, bounds.c, overflows.c and division-by-zero.c for the cases where a compiler meets its limits, procedure.c and function.c for control flow, encoding.c and escape.c, negative.c and double.c, overhead.c, quine.c, and directories for assembly, cache, gc and the examples README.
Where the extensions live, and what they are built from
The tools directory holds the research extensions, and all four of them are written in the same style as the core, which is to say built out of selfie and therefore able to target selfie itself. buzzr is described as a simple but self-fuzzing fuzzer that fuzzes RISC-U code including all of selfie and itself, in tools/buzzr.c.
monster in tools/monster.c is a self-executing symbolic execution engine that translates RISC-U code, again including all of selfie and itself, into SMT-LIB formulae. The soundness property is stated precisely enough to be testable: those formulae are satisfiable if and only if there is input to the code such that the code exits with non-zero exit codes or performs division by zero within a given number of machine instructions. That is a real theorem about the translation rather than a heuristic, and the bounded instruction count is what makes the SMT problem decidable in practice.
beator in tools/beator.c is the bounded model checking counterpart, a self-translating modeling engine that emits BTOR2 formulae rather than SMT-LIB, targeting a different solver ecosystem. Between monster and beator you have two independent formal backends over the same instruction set, which is the kind of redundancy that makes a claim like the one in the autograder section defensible.
There is also a garbage collector story beyond the built-in one. Alongside the conservative O(n^2) collector in selfie, tools/boehm-gc.c is an O(n) Boehm implementation for small memory blocks with fall-back to the collector in selfie for large memory blocks. The hybrid boundary is a reasonable engineering decision and an instructive one for a systems course, since it makes the time-space trade-off of the simpler algorithm explicit in the code.
The repository root also contains a GEMINI.md and a PLAN.md, alongside a CITATION.cff, an AUTHORS file, a benchmark directory, an assignments directory, a docs directory, a machine directory and a theses directory. The GEMINI.md in particular is worth noticing, because it indicates the project now expects to be worked on with AI assistance while continuing to publish a lecture series whose stated goal is to let students position generative AI properly. The last push was 2026-09-27, so none of this is an archived curiosity.
Building it takes a C compiler, and testing it takes the RISC-V toolchain
The core build genuinely needs nothing more than a C compiler, which is the first thing worth knowing. The Dockerfile in the repository is not for building selfie itself. It exists to build the surrounding RISC-V world so that selfie's output can be run and checked against something independent, and it does that in stages.
The first stage is named riscvgnutoolchainbuilder and starts from ubuntu:latest. It sets RISCV to /opt/riscv and puts that on PATH, installs the usual autotools chain plus bison, flex, texinfo, gperf, libtool, ninja-build, cmake and libglib2.0-dev, then clones riscv-gnu-toolchain and configures it:
git clone https://github.com/riscv/riscv-gnu-toolchain
cd riscv-gnu-toolchain
./configure --prefix=$RISCV --enable-multilibThe second stage is pkbuilder, which is more economical. It installs make, git, gcc-riscv64-linux-gnu and libc-dev-riscv64-cross from apt rather than compiling a toolchain, then clones riscv-pk. It includes a comment that is worth repeating to anyone doing the same exercise by hand: the compiled binaries are moved from riscv64-linux-gnu to riscv64-unknown-elf because spike looks for riscv64-unknown-elf by default when running pk. That is a naming mismatch between two official components of the same ecosystem, and it will otherwise cost an evening.
Both stages set MAKEFLAGS=-j4, which is a small concession to container memory limits and is the same species of caution the Ceph README expresses about Ninja job counts. Two unrelated projects arriving at a parallel-build cap for containerised builds is a decent sign that this is the correct default rather than a specific workaround.
For running without any of that, the repository also carries .replit and replit.nix, and a Dockerfile with a .dockerignore alongside it, so there is a Repl.it badge at the top of the README and a hosted environment that works without a local toolchain. The site selfie.cs.uni-salzburg.at hosts the class decks and the short animated talk, which is also available as a narrated video, with the recipe that builds that video from the talk checked into the video directory. For a system whose argument is that these ideas can be taught in full rather than sketched, having the lectures, the slides, the paper, the thesis tags and a working online environment in one repository is the part that makes the project usable rather than merely interesting.
Editorial conclusion
Selfie is the rare teaching project where the constraint did the work. One file, 12KLOC, no build dependencies beyond a C compiler, and every one of the four subsystems able to contain the other three. If you want to understand why that combination is possible rather than merely admire it, the repository tells you where to start. semantics.md, grammar.md and riscu.md at the root define the C Star and RISC-U subsets that make the self-reference close, which is the actual subject of the project rather than emulation or compilation in isolation. The Makefile then shows the whole build in three rules: selfie bootstraps from selfie.c with -Wall -Wextra -O3, the pattern rules produce RISC-U .m and .s output through ./selfie -c, and selfie.h is generated by rewriting main to selfie_main so the system can be linked into itself. For students, the graded path runs through the autograder in grader/README.md, including two assignments that point selfie's own model checker at their code. For everyone else, note that the repository carries a GEMINI.md and a PLAN.md, which tells you the project is still being worked rather than archived, and its last push was 2026-09-27.
Frequently asked questions
What is selfie in this repository?
Not a photograph. Selfie is a 12KLOC single-file C system from the University of Salzburg holding a self-compiling compiler, a self-executing RISC-V emulator, a self-hosting hypervisor and a small library. The name refers to self-reference in systems code, which is the thing the project exists to teach.
What do starc, mipster and hypster actually do?
starc compiles a tiny subset of C called C Star down to RISC-U, a teachable subset of RISC-V. mipster emulates RISC-U code, including itself once compiled by starc. hypster provides RISC-U virtual machines that can host all of selfie, so the whole stack runs inside itself.
What do I need to build and run selfie?
Only a C compiler, since the Makefile bootstraps the single selfie.c file with -Wall -Wextra -O3 and nothing else. Comparing its output against the official RISC-V toolchain, spike and the pk kernel needs more: the Dockerfile in the repository builds riscv-gnu-toolchain and riscv-pk for that purpose.
Why is the GitHub language shown as Jupyter Notebook?
It reflects the docs and examples directories rather than the system itself, which is C. The whole implementation lives in one file, selfie.c, defined as the single target of the Makefile bootstrap rule.
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/cksystemsteaching-selfie)