This post is dedicated to osdev.org community and #osdev IRC channel at freenode servers.
If you have followed my tutorial so far, you might have noticed that our kernel is almost 128 MB in size.
We have multiple issues with our compilation process which we must fix before we go further. Let’s list down the issues and why do we need to fix it and what benefits we are going to get?
We are right now using GCC 4.9, which is very old and also we have custom built GCC 4.9 to support i386-elf format. Also GCC 4.9 does not have certain attributes which we will require to use in later part. We also dont want to custom build GCC anymore. We should be able to use GCC out of the box. So First we will uninstall GCC 4.9 and Binutils, and install GCC straight out of packages.
sudo rm -rf /usr/local/i386elfgcc/
sudo apt remove gcc-4.9
sudo apt install gcc
Above commands will uninstall gcc 4.9 and install latest version of gcc.. For me gcc 9.3.0 is installed.
If we try to compile our code, it will not compile it straight away, we need to make adjustment in our compiler switches to make it work, but before that we also need to write a linker script. The main purpose of the linker script is to describe how the sections in the input files should be mapped into the output file, and to control the memory layout of the output file. Meaning we will be telling compiler how we want our code to be compiled, so that it executes the way we want.. not just from code / logic perspective but also in terms of where and how it will load variables, data, etc… in a memory. You can read more about Linker Scripts here – http://www.scoberlin.de/content/media/http/informatik/gcc_docs/ld_3.html
Below is the objdump of our kernel.elf file. Our objdump shows that our kernel is loaded at correct address but it is spreading through 0x8000000, also it is linked with libc.so library, which is Linux library. We do not want our OS to be dependent on any other OS library.. because it will be disaster later on.
You can produce objdump of kernel by executing following command:
objdump -phxd kernel.elf > kernel.objdump
LOAD off 0x00000000 vaddr 0x08048000 paddr 0x08048000 align 2**12
filesz 0x000002e8 memsz 0x000002e8 flags r--
LOAD off 0x00001000 vaddr 0x08049000 paddr 0x08049000 align 2**12
filesz 0x00000494 memsz 0x00000494 flags r-x
LOAD off 0x00002000 vaddr 0x0804a000 paddr 0x0804a000 align 2**12
filesz 0x00000244 memsz 0x00000244 flags r--
LOAD off 0x00002f08 vaddr 0x0804bf08 paddr 0x0804bf08 align 2**12
filesz 0x00000110 memsz 0x00000118 flags rw-
We will begin by writing a very simple linker script, create a new file named linker.ld
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
{
. = 0x01000; /*0x001000*/
.text BLOCK(2K) : ALIGN(2K)
{
*(.text)
}
/*Read-only data*/
.rodata BLOCK(2K) : ALIGN(2K)
{
*(.rodata)
}
/*Read-write data (initialized)*/
.data BLOCK(2K) : ALIGN(2K)
{
*(.data)
}
/*Read-write data (uninitialized) and stack*/
.bss BLOCK(2K) : ALIGN(2K)
{
*(.bss)
}
}
We also need to make corrections in our compile.bat file:
echo off
echo "clean all binaries"
del *.bin
del *.o
del *.elf
del *.objdump
del *.log
echo "compile boot.asm"
fasm boot.asm
echo "compile kernel.c"
wsl gcc -g -m32 -ffreestanding -c *.c drivers/*.c -nostartfiles -nostdlib
echo "link all c object files"
wsl ld -m elf_i386 -nostdlib -T linker.ld *.o -o kernel.elf
echo "compile loader.asm"
fasm loader.asm
echo "Link loader and kernel"
wsl ld -m elf_i386 -nostdlib -T linker.ld loader.o kernel.elf -o kernel_full.elf
echo "objdump loader file"
objdump -phxd loader.o > loader.objdump
echo "objdump kernel file"
objdump -phxd kernel_full.elf > kernel.objdump
echo "Producing elf file"
wsl objcopy kernel_full.elf -O binary kernel.bin
echo "Creating image...."
type boot.bin kernel.bin > os_image.bin
echo "Launching QEMU"
qemu-system-x86_64 os_image.bin
rem bochs -f bochsconfig.txt
rem bochsdbg -f bochsconfig.conf
Make changes in boot.asm file to read more sectors from the disk.
load_kernel:
mov bx, MSG_LOAD_KERNEL
call print
call print_nl
mov bx, KERNEL_OFFSET ;read from disk and store in 0x1000
mov dh, 7 ;read 7 sectors from HDD or bootable disk
mov dl, [BOOT_DRIVE]
call disk_load
ret
Change our kernel.c’s main method name to kmain method, and make some more changes in structure of a code (Not necessary).
#include "drivers/screen.h"
void kmain(void) {
char str[] = "Welcome to Learn OS. ";
char str1[] = "This message has been printed using printf.";
clear();
printf(str);
printf(str1);
}
And finally update loader.asm
org 0x1000
format ELF ;instruct assembler to produce ELF (Executable and Linkable Format) file.
extrn kmain ;tell assembler that main is the external function so ignore the assembler / compiler if main is not found in code.
start:
section '.text'
call kmain ;call external main function.
jmp $
Once we have made above changes, our code is now ready to get compiled. Also our kernel size has reduced drastically to be just under 12 KB.
Code is available at: https://github.com/dhavalhirdhav/LearnOS
In the next blog, we will see how we can further go ahead and load GDT table from kernel as well as IDT table, we will also learn how to write drivers and update our basic VGA driver. Also we are going to look at issues our boot loader and kernel has and how we plan to resolve them.
Recent Comments