Hello everybody, it’s me, Lovelace. Welcome to the first part of a complete series of guides about Operating System development.
I have been studying operating systems and operating system development for quite a bit now (not for that long, yet I still have some experience with it). Operating systems is – probably – one of the most complicated projects a programmer can start working in, but it is one of most rewarding, as you literally get to manage the resources of a real device, which feels so incredible, trust me.
I am currently working in a kernel called Kinl, all these guides (I have planned to do more than one) will be based on the code and the things that I learn with Kinl, I have been working in it since June 2021 – or so – but I started rewriting it a few days ago because I have learned a lot of things that I wanted to implement in Kinl.
As mentioned before, this is one of the most complicated projects you will code, therefore if you are a beginner you shouldn’t try doing this (even though you could read the theory so you keep getting ready for the day you can actually start implementing an OS by yourself). The only requirements I’d ask you for is to be proficient with the Assembly and C programming language, I’ll be using the NASM assembler, there’s no need for you to already know about operating systems, all the concepts about them will be taught in these guides. In this first entry, I had planned to help you set up a development environment in your computer and tell you about the things that your operating system will have at the time of completing all these lessons, so let’s go ahead with them.
On completion, your operating system will have:
Your own bootloader: this is a little program that will load your operating system from disk to memory, this is the first program that gets executed when a computer boots.
- Enabled A20 Line: that allows you to access all memory.
- An operating system in protected mode: which is the main mode of a processor in which operating systems are run.
- A graphical operating system: so you can draw shapes, text and even images with your operating system.
- An implementation of the heap: so memory can be dynamically allocated and freed.
- Support for the FAT-16 filesystem.
- Tasks and processes.
- Implemented userland.
- Implemented the interrupt 0x80.
- Write a keyboard and mouse driver.
- A working elf loader.
- The basis for writing a C stdlib.
- A shell that will be able to run programs and pass arguments to it.
- Simple multitasking.
That should have motivated you enough to start reading all these guides, there are a lot of complicated topics that I would have loved to find when I was just starting to develop kernels.
Building a Cross-Compiler
One of the things that we will need to create our own kernel, is a cross-compiler, as it will let us compile our code without any headers or any os-dependant header, for example.
Note: all these instructions will be written for a Unix-like environment, if you are not using an environment based on Unix to develop, please get one before continuing with these guides. Before compiling our cross-compiler, we will need to download some dependencies:
# For Arch pacman -Syu base-devel gmp libmpc mpfr # For Debian-based distributions apt install build-essential bison flex libgmp3-dev libmpc-dev libmpfr-dev texinfo libcloog-isl-dev libisl-dev # For Gentoo emerge --ask sys-devel/gcc sys-devel/make sys-devel/flex dev-libs/gmp dev-libs/mpc dev-libs/mpfr sys-apps/texinfo dev-libs/cloog dev-libs/isl # For Fedora dnf install gcc gcc-c++ make bison flex gmp-devel libmpc-devel mpfr-devel texinfo cloog-devel isl-devel # For OpenBSD pkg_add gmp libmpc mpfr texinfo
Now, you’ll need to download the source code of both Binutils and GCC. You can do so by executing these commands:
mkdir ~/src cd ~/src wget https://ftp.gnu.org/gnu/binutils/binutils-2.37.tar.xz tar xvf binutils-2.37.tar.xz mv binutils-2.37 binutils wget https://ftp.gnu.org/gnu/gcc/gcc-11.2.0/gcc-11.2.0.tar.gz tar xvf gcc-11.2.0.tar.gz mv gcc-11.2.0.tar.gz gcc mkdir -p ~/opt/cross
Before we get to compiling Binutils and GCC, we first need to export some variables so we can use them later at the moment of compiling:
export PREFIX="$HOME/opt/cross" export TARGET=i686-elf export PATH="$PREFIX/bin:$PATH"
Now, we can compile Binutils:
cd $HOME/src mkdir build-binutils cd build-binutils ../binutils/configure --target=$TARGET --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror make make install
And now, we can build GCC:
cd $HOME/src which -- $TARGET-as || echo $TARGET-as is not in the PATH mkdir build-gcc cd build-gcc ../gcc/configure --target=$TARGET --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers make all-gcc make all-target-libgcc make install-gcc make install-target-libgcc
Once it is finished compiling, we can use both our “naked” Binutils, and GCC. Reference for building the Cross-Compiler: wiki.osdev.org.
That was it for this first guide, I just wanted to talk a little about what you will learn and setup the cross-compiler so when we start writing C code, we won’t have to worry about it anymore.