C Sharp Bare Bones
Difficulty level |
---|
![]() Medium |
This tutorial is intended to help you write a simple 'Hello World' OS in C# which you can then compile to machine code for the IA32 architecture and boot via GRUB. There are a number of tools for compiling C# to CIL, including Microsoft's csc (distributed with Microsoft.NET) and mcs/gmcs/dmcs (distributed with Mono). In addition there are a number of tools for compiling CIL to native machine code in an ahead-of-time manner, including Microsoft's ngen, mono (with the --aot option), Cosmos' IL2CPU, mosacl from the MOSA project and tysila from the tysos project. Given the author's familiarity with tysos, that is what we will focus on here.
Prerequisites[edit | edit source]
You will need a binutils which can target the elf_i386 emulation, mono (for the gmcs compiler) or csc from .net, grub and its xorriso dependency (for generating iso images), NASM/YASM/something similar for the assembly stub and of course tysila. For debian-based systems try sudo apt-get install nasm xorriso qemu mono-devel.
For tysila, you can download pre-compiled binaries from http://www.tysos.org/files/tysila/tysila-latest.tar.bz2 (http://web.archive.org/web/20170317154703/http://www.tysos.org/files/tysila/tysila-latest.tar.bz2) and extract them to somewhere in your path. Please note these binaries will not work on 64-bit Windows due to a bug in the current Microsoft CLR (see here) so instead you must build it from source.
Building tysila[edit | edit source]
This is only required if you have not downloaded the pre-compiled binaries above. Use subversion to get the latest sources 'svn co http://www.tysos.org/svn/trunk tysos' (not in Wayback Machine), or download the latest tar ball from http://www.tysos.org/files/src/tysos-latest.tar.bz2 (http://web.archive.org/web/*/http://www.tysos.org/files/src/tysos-latest.tar.bz2). Tysos is a project developing a full OS kernel and drivers in C#, however we only want the compiler from it therefore we only want to compile part of the build tree. Enter the tysos directory and run
cd tybuild && make && cd ..
cd mono/corlib && make mscorlib.dll && cd ../..
cd tysila2 && make && cd ..
You will need to put the mono/corlib/mscorlib.dll, tysila2/bin/Release/tysila2.exe, tysila2/bin/Release/libsupcs.dll, tysila2/bin/Release/libtysila.dll, tysila2/bin/Release/tydbfile.dll, tysila2/bin/Release/tydisasm.dll and tybuild/bin/Release/tybuild.exe files somewhere in your path.
You may need to enable binfmt_misc support for mono (if trying this on linux). How to do this is outside the scope of this document but the answer is easily obtained via Google.
Directory layout[edit | edit source]
We will create a directory to build our OS and ISO file in. Something like mkdir -p barebones/iso/boot/grub should suffice. Enter the barebones directory and start creating some files.
loader.asm[edit | edit source]
This is the assembly stub which will contain a Multiboot header.
global sthrow
extern kmain
MODULEALIGN equ 1<<0
MEMINFO equ 1<<1
FLAGS equ MODULEALIGN | MEMINFO
MAGIC equ 0x1BADB002
CHECKSUM equ -(MAGIC + FLAGS)
section .text
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
sthrow:
hlt
jmp sthrow
kernel.cs[edit | edit source]
This is the actual simple kernel - it just prints a message to the screen.
namespace BareBones
{
class Program
{
static int pos = 0;
unsafe static void Main()
{
// Clear the screen
for(int i = 0; i < 80 * 25 * 2; i++)
*(byte *)(0xb8000 + i) = 0;
// Say hi
Print("Hello World!");
}
static void Print(string s)
{
foreach(char c in s)
Print(c);
}
unsafe static void Print(char c)
{
*(byte *)(0xb8000 + pos) = (byte)c;
*(byte *)(0xb8000 + pos + 1) = 0x0f;
pos += 2;
}
}
}
linker.ld[edit | edit source]
The linker script
ENTRY (_start)
SECTIONS
{
. = 0x00100000;
.text ALIGN(0x1000) :
{
*(.text)
}
.rodata ALIGN(0x1000) :
{
*(.rodata*)
}
.data ALIGN(0x1000) :
{
*(.data)
}
.bss ALIGN(0x1000) :
{
*(COMMON)
*(.bss)
}
}
iso/boot/grub/grub.cfg[edit | edit source]
This is a short file to tell grub where to find our kernel
multiboot /kernel.bin
boot
Building it all[edit | edit source]
The following commands should build your new C# kernel. First, assemble the multiboot stub:
nasm -felf -o loader.o loader.asm
To compile the .cs file to a .exe you have a choice of three options (depending on your architecture):
gmcs /target:exe /out:kernel.exe /unsafe kernel.cs
csc /target:exe /out:kernel.exe /unsafe kernel.cs
tybuild.exe /unsafe kernel.cs
To compile kernel.exe to machine code we use tysila:
tysila2.exe --arch i586-elf-tysos -fno-rtti -o kernel.o kernel.exe
Here, the -fno-rtti switch disables run-time type information, support for which would greatly enlarge the size of your kernel and require you to provide a great number of run time functions to support this.
To link:
ld -m elf_i386 -T linker.ld -o iso/kernel.bin loader.o kernel.o
Then we make a bootable iso image with:
grub-mkrescue -o barebones.iso iso
And run it on qemu with:
qemu-system-i386 -cdrom barebones.iso