You've basically described it. Basically all of the runtime is written in C#. The GC itself is written in C# using the "unsafe" extensions.
The GC gets all of its memory from a very simple page manager at the very bottom of the system.
The page manager and GC are written carefully so that they don't require any GC'd memory.
The MSIL to x86 compiler we use is Bartok, developed by Microsoft Research's Advanced Compiler Technology Group (https://research.microsoft.com/act/). David Tarditi and his team have created this fantastic
whole-program optimizing compiler that reads in a collection of MSIL Assemblies and outputs an x86 binary. At the end of the day, its just code.
Beer28, remember that libc is just x86 code. So, we replace whatever one might need from libc, with C# code. Instead of calling a C version of libc, Singularity uses safe code written in C# to directly access the screen hardware (for example).
This probably makes more sense when you realizes the most OSes don't use BIOS except during the very earliest stage of boot. Singularity does the same as well, it only use BIOS during the 16-bit real-mode boot strap. Once jump to 32-bit mode, we never use
BIOS again, but use device drivers written in C# instead. Yes, we had to replace a lot of CLR libraries with different code. However, unlike the CLR, the Singularity runtime is written in C#.
In Singularity, you can add new code to your application. However, instead of loading it into your own process, you load it into a child process. The OS facilitiates setting up channels between the child and its parent.
While this is still very much a work in progress, the results so far look promising. For example, we have a dynamic web server that uses child processes. Also all of our device drivers run in child processes.
This isn't the CLR. In our world, we compile entire MSIL for the kernel into x86 instructions at installation time. There is no libc at the bottom.
However, we do have around some assembly code. Like a kernel written in C, our C# kernel needs assembly code to handle the lowest part of the interrupt dispatch on the x86. But once the assembly code has finished, it dispatches directly into compiled C# (no
C). BTW, there is some C code in the system, primarily for the debugger stub.