Build your own operating System-part02
Implement_with_c
Last week ,we guided you to setup the booting part of our operating system. In this article, we are going to implement C language to our project instead of Assembly language.
So, let’s get started!
Setting Up a Stack
Since all non-trivial(not lightweight) C programs use a stack, and setting up a stack is not harder than to make the esp
register point to the end of an area of free memory. So far, in this development process, the only things in memory are GRUB, BIOS, the OS kernel, and some memory mapped I/Os. This is not a good thing to do; because, we don’t know how much memory is available or if the esp
pointed memory area is used by something else.
Reserving a piece of uninitialized memory in the bss
section in the ELF file of the kernel will be a solution. And also, this will reduce the OS executable size.Add this section to loader.s
file.
KERNEL_STACK_SIZE equ 4096 ; size of stack in bytes
section .bss
align 4 ; align at 4 bytes
kernel_stack: ; label points to beginning of memory
resb KERNEL_STACK_SIZE ; reserve stack for the kernel
And then, we need to setup the stack pointer by pointing esp
to the end of the kernel_stack
memory. In order to do that, you need to add the following statement inside the loader:
block you your loader.s
file
mov esp, kernel_stack + KERNEL_STACK_SIZE ; point esp to the start of the
; stack (end of memory area)
If you want to know how loader.s file look like ,you can follow this link:
skyOS/loader.s at c8fc368b90a4cd83c30705ded52645836effb9bf · pasanlk/skyOS (github.com)
Calling C Code From Assembly
The next step is to call a C function from assembly code. There are many different conventions for how to call C code from assembly code . This book uses the cdecl calling convention, since that is the one used by GCC. The cdecl calling convention states that arguments to a function should be passed via the stack (on x86). The arguments of the function should be pushed on the stack in a right-to-left order, that is, you push the rightmost argument first. The return value of the function is placed in the eax
register. The following code shows an example:
/* The C function */
int sum_of_three(int arg1, int arg2, int arg3)
{
return arg1 + arg2 + arg3;
}
We need to call this c function using assembly.So place this following code after the esp instruction in the loader label of the loader.s file.
; The assembly code
external sum_of_three ; the function sum_of_three is defined elsewhere
push dword 3 ; arg3
push dword 2 ; arg2
push dword 1 ; arg1
call sum_of_three ; call the function, the result will be in eax
Compiling C Code
The next step will be this. For normal compilations, we can use gcc fileName.c -o objectName
. But, in this case, we are compiling them for an operating system. So, we have to use a lot of flags as below.
-m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs
And, we recommend you to turn on all warnings and treat warnings as errors by adding these flaags:
-Wall -Wextra -Werror
Build Tools
Now we’ll set up some build tools to make compiling and testing our operating system easier. We will use make
as our build system.
Here’s a simple Makefile
for our OS:
OBJECTS = loader.o kmain.o
CC = gcc
CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \
-nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c
LDFLAGS = -T link.ld -melf_i386
AS = nasm
ASFLAGS = -f elf
all: kernel.elf
kernel.elf: $(OBJECTS)
ld $(LDFLAGS) $(OBJECTS) -o kernel.elf
os.iso: kernel.elf
cp kernel.elf iso/boot/kernel.elf
genisoimage -R \
-b boot/grub/stage2_eltorito \
-no-emul-boot \
-boot-load-size 4 \
-A os \
-input-charset utf8 \
-quiet \
-boot-info-table \
-o os.iso \
iso
run: os.iso
bochs -f bochsrc.txt -q
%.o: %.c
$(CC) $(CFLAGS) $< -o $@
%.o: %.s
$(AS) $(ASFLAGS) $< -o $@
clean:
rm -rf *.o kernel.elf os.iso
The contents of our working directory should now look like the following figure:
.
|-- bochsrc.txt
|-- iso
| |-- boot
| |-- grub
| |-- menu.lst
| |-- stage2_eltorito
|-- kmain.c
|-- loader.s
|-- Makefile
You should now be able to start the OS with the simple command make run
, which will compile the kernel and boot it up in Bochs.
Reference: https://littleosbook.github.io/
Following is the Github repository for this: from:https://github.com/pasanlk/skyOS.git
THANK YOU!