I achieved a poorly documented thing where showing the commit to people actually helps them. To be more helpful, I now write a blog about it. May it serve as some documentation.

Quick overview

Our work will be in the clang and llvm project only, though you might want to add support to libc++.

LLVM only needs minor changes, 3 additional lines for LF OS. You have to add the OS to the Triple enum (llvm/include/llvm/ADT/Triple.h) and detection + toString code for it in llvm/lib/Support/Triple.cpp.

For clang we write a new TargetInfo (this one sets the default preprocessor definitions) and a ToolChain (this sets default flags, which linker to use, …). We also add some Code in the Driver to use our new ToolChain.

Be aware of the long compile time of clang! You want to use ccache. Also: a clang debug build is quite large. 50GB kind of large with a 2GB clang binary (dynamically linked! not sure, maybe it was actually statically linked). You probably want to use the release config.

Diving in

The changes in llvm are pretty boring and described in the overview already. If the instructions are not enough, the rest of the process might be over your current abilities. Learn some more stuff an come back :)

Creating our new Target means creating a new template in clang/lib/Basic/Targets/OSTargets.h. I suggest to copy an existing one and modify it for your needs. The most important stuff are the preprocessor definitions you definitely need for building real stuff for your OS.

When your new Target is added, you have to tell clang to use it in clang/lib/Basic/Targets.cpp. Look for the correct place in the switch-case-cascade to add your OS Triple and return your target (templated with the CPU arch).

You added your target? Great! Now to the bigger chunk of code, the ToolChain. A ToolChain in clang is a combination of compiler, assembler, linker and stuff… the toolchain ¯\_(ツ)_/¯

To create your own ToolChain, create a new class in clang/lib/Driver/ToolChains (I called mine LFOS). You need a .h and a .cpp file. You can copy an existing one that matches your OS a bit (I used Fuchsia a lot) or use the LF OS commit linked below as a reference.

The basic idea is to set the correct flags to find headers and libraries, add some static libraries if required, select a runtime (libgcc or compiler-rt), select a C++ stdlib implementation (libstdc++ or libc++) and so on.

After you have created your ToolChain, you have to tell clang when to use it. Go into clang/lib/Driver/Driver.cpp and add an include for your toolchain header. Also add a new case in getToolChain for your OS.

For LF OS I had to add some more cases in clang/lib/Frontend/InitHeaderSearch.cpp:AddDefaultCIncludePaths to tell clang not to use random additional default directories. I just added LF OS whereever I found Fuchsia ¯\_(ツ)_/¯

Side note: if you have problems with a dash in your OS name which you have replaced by an underscore in the triple and clang not finding the compiler-rt or something: add a case to clang/lib/Driver/ToolChain.cpp:getOSLibName. See the LF OS commit for reference.


Define your OS triple. I use x86_64-pc-lf_os. Make LLVM know about the OS component. Add a target in OSTarget.h and a Toolchain in clang/lib/Driver/ToolChains. Make the Driver know about your toolchain. Maybe tweak some more random things.

I was surprised how fast I was able to find the things I have to change. The codebase is very clean, way cleaner than I expected for a compiler. The community is friendly, a fix in libc++ was appreciated and merged, the discussion helped me learn things (having my code reviewed by a member of the C++ standards committee was a bit intimidating ^^’).

A great new feature for clang would be to load a new operating system from a config file, maybe from sysroot. You’d never have to change clang for a hobby OS then as you could just write that config (clang is always able to cross-compiler to whatever target you may find).


Feel free to copy, remix, share. Attribution would be nice but is not required.