Last time I missed updating, I made a branch from an earlier git commit, and threw away a bit of work and messy code rather than trying to catch up.
This time, however, I'm actually quite happy that I didn't write too much, because I've recently changed a lot of what I would have written about.
So instead, I'll describe one of my most recent changes here - a new cross compiler and a newlib port - and then in subsequent posts go over chosen parts of the other things I've done.
The be-or-not-to-be of cross compilers has been discussed at length and it seems every time someone posts a description on how to build one they are told that you don't need one if you know how to use your compiler or whatever. For examples, read the osdev wiki - Why do I need a Cross Compiler? talk page (I don't recommend it if you're in a bad mood).
Well, I'm developing on OSX but have chosen to build an X86 kernel in elf format due to the availability of documentation, support and expertize. That's reason enough for a cross compiler build of binutils at least.
Clang offers excellent support for cross compilation out of the box, so I didn't use to have a need for a gcc cross compiler as illustrated in this post. As it turns out, however, compiling newlib with that setup is rather annoying.
That's the makefile for the entire /bin directory in my os.
And that prepared me for building newlib.
Those last two lines tells Homebrew that you want to use those specific versions for now.
This gives me two versions of the newlib c library. One with all syscalls defined and one without. The latter is suitable for linking into the kernel and allows me to use a standard C library with things like sprintf and even a ready made malloc.
"But wait," you say. "Doesn't malloc make an sbrk() syscall? You can't make a syscall from inside the kernel, can you?"
Well. Remember my last post where I said that keeping versions of every syscall function inside the kernel would turn out to have some really cool advantages? Here they are.
I need an sbrk anyway, so all I have to do differently is make it check whether the request came from a user process or from the kernel and handle them a slight bit differently (i.e. make sure the brk is above 0xC0000000 for kernel and below for user mode...).
Similar checks can be made for all syscalls and some doesn't actually need any changing at all.
Needless to say, this revelation changed my kernel structure quite a bit and I entirely threw away my own kernel heap manager.
Having the cross compiler toolchain and library setup allows me to compile the kernel using a very simple makefile.
and everything else is taken care of by the default rules of gnu make, including preprocessing and assembling .S files.
On a final note, the automatic rules for building object files out of .S files runs them through CC, which means they are assembled by i586-pc-myos-as which uses AT&T syntax rather than NASM which uses Intel syntax. I actually converted all my assembly code to AT&T syntax, but you might want to use the .intel_syntax directive instead.
The methods described in this post has been implemented in the directory toolchain of git commit f09bd57b8e.