這一系列是我讀 The little book about OS development 的一些實作紀錄,此篇為介紹有關於 C 的使用,讓組合語言和 C 語言可以結合在一起。
組合語言雖然可以很直接地和電腦溝通,但對於人類來說難以理解。C 語言相對親民得多,所以在控制系統上,我們會傾向用 C 語言,除非有必要才會使用組語。接下來會介紹如何由組合語言呼叫 C 語言所寫的函式。以這本 little book about OS 而言,他們使用 cdecl 呼叫慣例,這也是 C 和 C++ 程式的預設呼叫慣例。
堆疊 Stack
C 語言有個很重要的資料結構概念叫做「Stack」。函數內的參數會從右到左依序堆疊到 stack 裡面,並將結果儲存在 eax 暫存器中。用下面簡單的 C 程式碼和組語說明:
int sum_of_three(int arg1, int arg2, int arg3){
return arg1 + arg2 + arg3;
}
組語:
external sum_of_three ; 在別的地方定義函式 sum_of_three push dword 3 ; push arg3 push dword 2 ; push arg2 push dword 1 ; push arg1 call sum_of_three ; 呼叫函式,並且把定義存在 eax 內
Struct 的大小
接下來要理解的是 struct 資料結構的大小計算,不過這裡我打算獨立一篇文章來討論,所以這邊不多加說明,屆時會再放連結在這邊。
在本次的目的中,主要是強調 packing 和 padding 的概念。
編譯 C 語言
先隨便寫個程式碼,儲存檔名為 kmain.c,裡面有個名字為 kmain 的函式:
void kmain(){}
當我們程式寫好,最後當然還是必須能成功編譯才有用。我們這個迷你的作業系統目前啥都沒有,不能使用任何的標準函式庫,所以會需要很多個 flags(旗標)。
接下來寫個 Makefile。對了,下面是為了閱讀美觀用了很多空白格對齊,但實際操作不要複製下方的檔按,主要是因為 Makefile 內的 tab 和空格有不同的用法,若直接複製下方會有錯誤。
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
我上傳一個正確版本,但很亂:Makefile ,也可以參考原著 Getting to C 的章節。
針對 flag 簡單抓幾個解釋一下:
-m32:編譯 32-bit 程式-nostdlib:不使用標準函式庫-nostdinc:不要把系統提供的標頭檔給包進來-fno-builtin:不使用 C 語言內建的函式-fno-stack-protector:關閉 stack protector-Wall -Wextra -Werror:把所有警告變為 error
並且把這兩個檔案 kmain.c 和 Makefile 放入和 loader.s 同一層的位置,並且在這個位置執行指令:
make run
正常來說可以看見 Bochs 啟動。記得和之前一樣,在終端機按 c 並 Enter 後,讓它繼續執行。







