This series pertains to create a basic Operating System using Rust Programming Language. This series aims to learn and understand the basics of Operating System.
Through this series, you will get some ideas about the internal components of Operating System and how they interact with each other.
This article pertains to building a custom kernel for the custom target that we created in our previous post.

To building our kernel we need to follow a few steps:
- Recompiling
core
library - Enabling
compiler-builtins-mem
feature - Setting-up default target
Before proceeding further let’s brush-up our understanding of the custom target. So, here is the JSON file(named x86_64-os-in-rust.json
) that we created in our previous post:
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"os": "none",
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float"
}
A quick update on this, we’ve configured the target for x86_64-unknown-none
where the architecture is x86_64
and our OS(operating system) is none because we don’t need any underlying operating system for our kernel. To know more about this custom target file, please have a look at the previous blog of this series.
Alright!!! we brushed-up the details of our previous article, now let’s dive into the scope of this one.
Here we’re trying to build our kernel for the custom target. And for building any Rust code, cargo(build tool) is ready for us.
Now let’s fire the build command for the custom target:



Oops!!! we got an error. Okay, so let’s understand why we got this?
The error tells us that the Rust compiler can’t find the core
library. So the very first question that comes into our mind is Why we need this? Right?
So the reason for this library is it contains basic Rust types such as Result
, Option
, and iterators
, and one more thing it is implicitly linked to all no_std
crates as well.
But here is a problem, as we are building our kernel for our custom target, and the core library is distributed together with the Rust compiler as a precompiled library. So it is only valid for supported host triples (e.g., x86_64-unknown-linux-gnu
) but not for our custom target.
Now we only one option left, we need to recompile the core
library for our target.
Recompile core
library
To recompile core
, we have to use cargo’s feature built-std
. It allows us to recompile core
and other standard library crates. And as this feature is in its very early stages of development so this would available for us in nightly
version only.
In order to use this feature we need to create a cargo configuration file .cargo/config.toml and need to put this on that file:
[unstable]
build-std = ["core", "compiler_builtins"]
Through this the cargo will recompile the core
and compile_builtins
libraries.
Another thing comes here is if we want to use this feature, then we need to have the source code for the standard library available, and at this time the only supported method of doing so is to add the rust-src
rust’s rustup component:
rustup component add rust-src
Now let’s try to build our kernel again:



In the output, we can see that cargo recompiles core
, rustc-std-workspace-core
, and compiler_builtins
libraries for our custom target. And we build our kernel successfully now the next stage is to provide some kind of memory-related built-ins.
Enable compiler-builtins-mem
feature
Built-in functions are available for all systems and most of these functions are provided by the compiler_builtins
crate that we just recompiled.
The memory-related functions in the compiler_builtins are:
memset
: sets all bytes in a memory block to a given value,memcpy
: copies one memory block to another,memcmp
: compares two memory blocks
These functions will be needed when we add some more code to it, but as of now, they don’t require us to compile our kernel.
So as these functions are available for all systems and since we can’t link to the C library of the operating system, we have to figure out another way to provide these functions to the compiler.
There are two approaches to provide these functions to our compiler:
- Either we have to write our implementation for these functions,
- Or we can enable the disabled feature provided by the compiler_builtins crate because it already contains implementations for all the needed functions. They are just disabled by default to not collide with the implementations from the C library.
So we’ll go with the second approach because this would be the best-suited approach for this time.
Okay, to enable these functions by setting cargo’s build-std-features
flag to ["compiler-builtins-mem"]
. So to do this, we have to configure this in the unstable table inside the .cargo/config.toml
file. Like this:
[unstable]
build-std-features = ["compiler-builtins-mem"]
Our config.toml
file will look like this:



Now our last step for this article is to setting-up the target as default for our kernel. As we are explicitly providing --target
parameter with cargo build
command. So to avoid this we can configure it as default by providing instructions in our config.toml
file like this:
[build]
target = "x86_64-os-in-rust.json"
From now onwards, whenever we will trigger the cargo build command, it will automatically build a kernel for our defined target.
That’s all for this article, thanks for reading.
In the next part, we will try to run our kernel and print something on the screen.
Stay tuned!!!
References:
Blogs of this series:
- An executable that runs on bare metal: Part-1
- An executable that runs on bare metal: Part-2
- Custom target to build kernel for a bare metal: Part-3
If you want to read more content like this? Subscribe Rust Times Newsletter and receive insights and latest updates, bi-weekly, straight into your inbox. Subscribe Rust Times Newsletter: https://bit.ly/2Vdlld7 .





