[心得] 作業系統之前的程式

作者: descent (「雄辯是銀,沉默是金」)   2014-06-07 12:18:38
first edit: 20120506
這是在傳統的 BIOS 的環境, UEFI 就不是這樣了。
相關文章:
作業系統之前的程式 (1) - c++ 篇 ( http://goo.gl/HJy4jn )
作業系統之前的程式 (2) - c++ global object ctor/dtor can not be invoked (
http://goo.gl/S248QM )
這是我在 mosut ( http://goo.gl/YEfGEz ) 分享的主題。介紹一下在作業系統之前的程
式 (僅僅印出個字串)。寫一個作業系統之前的程式就和在作業系統下很類似, 不過有幾
點要注意。
bootloader hello world 被載入的位址是 0x7c00, 也就是從這裡開始執行。
510, 511 byte 必須是 0xaa, 0x55 結尾。
不能大於 512 byte, 其實扣掉 0x11, 0x55 2 byte, 只能有 510 byte。
沒有 printf 可以用, 得使用 bios call 或是寫入 video ram 來完成螢幕的輸出。
可以使用組合語言, 還有大家很熟悉的 c/c++ 語言來寫這樣的練習程式, 其他可用的語
言我就不知道了, 我只實作過這幾種語言。不過使用的 toolchain 和在作業系統下有點
不同。也就是說, 程式的寫法是一樣的, 但是使用 compiler 產生執行檔的方式是不一樣
的。而使用 C 語言只是展示用, 實際上一個 bootloader 要完成的事情可能無法使用C
語言來完成, 因為很容易就超過 512 byte。
bh.asm
1 ( http://goo.gl/l2TEyL ) org 07c00h ; 告訴編譯器程序加載到 7c00 處
2 ( http://goo.gl/l2TEyL ) mov ax, cs
3 ( http://goo.gl/l2TEyL ) mov ds, ax
4 ( http://goo.gl/l2TEyL ) mov es, ax
5 ( http://goo.gl/l2TEyL ) call DispStr ; 使用顯示字元串例程
6 ( http://goo.gl/l2TEyL ) jmp $ ; 無限循環
7 ( http://goo.gl/l2TEyL ) DispStr:
8 ( http://goo.gl/l2TEyL ) mov ax, BootMessage
9 ( http://goo.gl/l2TEyL ) mov bp, ax ; ES:BP = 串位址
10 ( http://goo.gl/l2TEyL ) mov cx, 16 ; CX = 串長度
11 ( http://goo.gl/l2TEyL ) mov ax, 01301h ; AH = 13, AL = 01h
12 ( http://goo.gl/l2TEyL ) mov bx, 000ch ; 頁號為 0(BH = 0) 黑底紅字 (BL =
0Ch, 高亮)
13 ( http://goo.gl/l2TEyL ) mov dl, 0
14 ( http://goo.gl/l2TEyL ) int 10h ; 10h 號中斷
15 ( http://goo.gl/l2TEyL ) ret
16 ( http://goo.gl/l2TEyL ) BootMessage: db "Hello, NASM!"
17 ( http://goo.gl/l2TEyL ) times 510-($-$$) db 0 ; 填充剩下的空間,使生成的
二進制程式碼恰好為 512 字節
18 ( http://goo.gl/l2TEyL ) dw 0xaa55 ; 結束標誌
bh.asm 是使用 nasm 來完成的 bootloader 程式 (from Orange's 一個作業系統的實現
( http://goo.gl/fFD7QT ) p 1-3), 範例的組合語言的是 intel 語法。使用 nasm 產生
bootloader 的可執行檔很簡單。
nasm bh.asm -o bh.asm.bin
bh.asm.bin 為 512 byte 的執行檔, 寫入軟碟第 1 個磁區即可。把軟碟放入開機後即可
看到 Hello, NASM!
( http://goo.gl/W1OxZx )
bh.s
1 ( http://goo.gl/l2TEyL ) .code16
2 ( http://goo.gl/l2TEyL ) .text
3 ( http://goo.gl/l2TEyL ) .global begin
4 ( http://goo.gl/l2TEyL ) begin:
5 ( http://goo.gl/l2TEyL ) mov %cs,%ax
6 ( http://goo.gl/l2TEyL ) mov %ax,%ds
7 ( http://goo.gl/l2TEyL ) mov %ax,%es
8 ( http://goo.gl/l2TEyL ) movw $0xb800, %ax
9 ( http://goo.gl/l2TEyL ) movw %ax, %gs
10 ( http://goo.gl/l2TEyL )
11 ( http://goo.gl/l2TEyL )
12 ( http://goo.gl/l2TEyL ) mov $0, %edi /* Destination */
13 ( http://goo.gl/l2TEyL ) mov $msg, %esi /* Source */
14 ( http://goo.gl/l2TEyL )
15 ( http://goo.gl/l2TEyL ) 1:
16 ( http://goo.gl/l2TEyL ) #cmp $0, %ecx
17 ( http://goo.gl/l2TEyL ) cmpb $0, (%esi)
18 ( http://goo.gl/l2TEyL ) jz 2f
19 ( http://goo.gl/l2TEyL ) movb %ds:(%esi), %al
20 ( http://goo.gl/l2TEyL ) inc %esi
21 ( http://goo.gl/l2TEyL ) movb %al, %gs:(%edi)
22 ( http://goo.gl/l2TEyL ) inc %edi
23 ( http://goo.gl/l2TEyL ) movb $0xc, %gs:(%edi)
24 ( http://goo.gl/l2TEyL ) inc %edi
25 ( http://goo.gl/l2TEyL ) dec %ecx
26 ( http://goo.gl/l2TEyL ) jmp 1b
27 ( http://goo.gl/l2TEyL ) 2:
28 ( http://goo.gl/l2TEyL ) movb $'E', %gs:(160)
29 ( http://goo.gl/l2TEyL ) jmp .
30 ( http://goo.gl/l2TEyL ) #msg:.ascii "Hello GAS"
31 ( http://goo.gl/l2TEyL ) msg:
32 ( http://goo.gl/l2TEyL ) .asciz "Hello GAS"
33 ( http://goo.gl/l2TEyL ) #.asciz "Hello World"
34 ( http://goo.gl/l2TEyL ) .org 510
35 ( http://goo.gl/l2TEyL ) .word 0xaa55
36 ( http://goo.gl/l2TEyL )
bh.s 是使用 gas 來完成的 bootloader 程式,範例的組合語言是at & t 語法, 使用
gas 產生 bootloader 的可執行檔有點複雜, 除了程式碼本身, 還要一個 linker
script, 在連結的時候使用。
as.ld
1 ( http://goo.gl/l2TEyL ) ENTRY(begin);
2 ( http://goo.gl/l2TEyL ) SECTIONS
3 ( http://goo.gl/l2TEyL ) {
4 ( http://goo.gl/l2TEyL ) . = 0x7C00;
5 ( http://goo.gl/l2TEyL ) .text : AT(0x7C00)
6 ( http://goo.gl/l2TEyL ) {
7 ( http://goo.gl/l2TEyL ) _text = .;
8 ( http://goo.gl/l2TEyL ) *(.text);
9 ( http://goo.gl/l2TEyL ) _text_end = .;
10 ( http://goo.gl/l2TEyL ) }
11 ( http://goo.gl/l2TEyL ) .data :
12 ( http://goo.gl/l2TEyL ) {
13 ( http://goo.gl/l2TEyL ) _data = .;
14 ( http://goo.gl/l2TEyL ) *(.bss);
15 ( http://goo.gl/l2TEyL ) *(.bss*);
16 ( http://goo.gl/l2TEyL ) *(.data);
17 ( http://goo.gl/l2TEyL ) *(.rodata*);
18 ( http://goo.gl/l2TEyL ) *(COMMON)
19 ( http://goo.gl/l2TEyL ) _data_end = .;
20 ( http://goo.gl/l2TEyL ) }
21 ( http://goo.gl/l2TEyL ) /*
22 ( http://goo.gl/l2TEyL ) .sig : AT(0x7DFE)
23 ( http://goo.gl/l2TEyL ) {
24 ( http://goo.gl/l2TEyL ) SHORT(0xaa55);
25 ( http://goo.gl/l2TEyL ) }
26 ( http://goo.gl/l2TEyL ) */
27 ( http://goo.gl/l2TEyL ) /DISCARD/ :
28 ( http://goo.gl/l2TEyL ) {
29 ( http://goo.gl/l2TEyL ) *(.note*);
30 ( http://goo.gl/l2TEyL ) *(.iplt*);
31 ( http://goo.gl/l2TEyL ) *(.igot*);
32 ( http://goo.gl/l2TEyL ) *(.rel*);
33 ( http://goo.gl/l2TEyL ) *(.comment);
34 ( http://goo.gl/l2TEyL ) /* add any unwanted sections spewed out by your
version of gcc and flags here */
35 ( http://goo.gl/l2TEyL ) }
36 ( http://goo.gl/l2TEyL ) }
產生 bootloader 執行檔的步驟為:
as -o bh.s.o bh.s
ld -Tas.ld -o bh.s.elf bh.s.o
objcopy -O binary bh.s.elf bh.s.bin
( http://goo.gl/T9QT13 )
bh.s.bin 就是 bootloader 執行檔, 不像 nasm 那麼乾脆是吧! 你一定不喜歡, 不過這
樣的學習負擔是有額外的收穫的。下面的 C 語言版本就需要這樣的作法。
cb.c
1 ( http://goo.gl/l2TEyL ) __asm__(".code16gcc\n");
2 ( http://goo.gl/l2TEyL ) /*
3 ( http://goo.gl/l2TEyL ) * c bootloader
4 ( http://goo.gl/l2TEyL ) */
5 ( http://goo.gl/l2TEyL )
6 ( http://goo.gl/l2TEyL )
7 ( http://goo.gl/l2TEyL )
8 ( http://goo.gl/l2TEyL ) void main(const char *s);
9 ( http://goo.gl/l2TEyL )
10 ( http://goo.gl/l2TEyL ) int bbb=0; // test bss section
11 ( http://goo.gl/l2TEyL )
12 ( http://goo.gl/l2TEyL ) void WinMain(void)
13 ( http://goo.gl/l2TEyL ) {
14 ( http://goo.gl/l2TEyL ) main("hello world");
15 ( http://goo.gl/l2TEyL ) while(1);
16 ( http://goo.gl/l2TEyL ) }
17 ( http://goo.gl/l2TEyL )
18 ( http://goo.gl/l2TEyL ) void main(const char *s)
19 ( http://goo.gl/l2TEyL ) {
20 ( http://goo.gl/l2TEyL ) while(*s)
21 ( http://goo.gl/l2TEyL ) {
22 ( http://goo.gl/l2TEyL ) __asm__ __volatile__ ("int $0x10" : :
"a"(0x0E00 | *s), "b"(7));
23 ( http://goo.gl/l2TEyL ) s++;
24 ( http://goo.gl/l2TEyL ) }
25 ( http://goo.gl/l2TEyL ) }
很像一般的 C 程式吧!不過我們有著 windows 才有的 WinMain 和一般 C 的 main, 哪
個才是程式開始的地方呢?仍然需要搭配的 linker script, 從以下的 linker script
可以看出, 我們的程式由 WinMain 開始。
l.ld
1 ( http://goo.gl/l2TEyL ) /* for cb.c */
2 ( http://goo.gl/l2TEyL ) ENTRY(WinMain);
3 ( http://goo.gl/l2TEyL ) SECTIONS
4 ( http://goo.gl/l2TEyL ) {
5 ( http://goo.gl/l2TEyL ) . = 0x7C00;
6 ( http://goo.gl/l2TEyL ) .text : AT(0x7C00)
7 ( http://goo.gl/l2TEyL ) {
8 ( http://goo.gl/l2TEyL ) _text = .;
9 ( http://goo.gl/l2TEyL ) *(.text);
10 ( http://goo.gl/l2TEyL ) _text_end = .;
11 ( http://goo.gl/l2TEyL ) }
12 ( http://goo.gl/l2TEyL ) .data :
13 ( http://goo.gl/l2TEyL ) {
14 ( http://goo.gl/l2TEyL ) _data = .;
15 ( http://goo.gl/l2TEyL ) *(.bss);
16 ( http://goo.gl/l2TEyL ) *(.bss*);
17 ( http://goo.gl/l2TEyL ) *(.data);
18 ( http://goo.gl/l2TEyL ) *(.rodata*);
19 ( http://goo.gl/l2TEyL ) *(COMMON)
20 ( http://goo.gl/l2TEyL ) _data_end = .;
21 ( http://goo.gl/l2TEyL ) }
22 ( http://goo.gl/l2TEyL ) .sig : AT(0x7DFE)
23 ( http://goo.gl/l2TEyL ) {
24 ( http://goo.gl/l2TEyL ) SHORT(0xaa55);
25 ( http://goo.gl/l2TEyL ) }
26 ( http://goo.gl/l2TEyL ) /DISCARD/ :
27 ( http://goo.gl/l2TEyL ) {
28 ( http://goo.gl/l2TEyL ) *(.note*);
29 ( http://goo.gl/l2TEyL ) *(.iplt*);
30 ( http://goo.gl/l2TEyL ) *(.igot*);
31 ( http://goo.gl/l2TEyL ) *(.rel*);
32 ( http://goo.gl/l2TEyL ) *(.comment);
33 ( http://goo.gl/l2TEyL ) /* add any unwanted sections spewed out by your
version of gcc and flags here */
34 ( http://goo.gl/l2TEyL ) }
35 ( http://goo.gl/l2TEyL ) }
使用以下的步驟來產生 bootloader 執行檔:
gcc -fno-stack-protector -std=c99 -march=i686 -ffreestanding -Wall -c cb.c
ld -Tl.ld -nostdlib -o cb.elf cb.o
objcopy -R .pdr -R .comment -R.note -S -O binary cb.elf cb.bin
最前面提到的第一點: 載入的位址是 0x7c00, 這表示, 在程式裡變數的位址要從
0x7c00 來當作計算標準。nasm 使用 org 07c00h 來達成目的。而 gas 使用 linker
script 來完成。C 版本也是一樣。
as.ld L4 . = 0x7C00; 就是告訴 ld 用 0x7c00 來計算位址, 而不是從 0 開始算起。
至於第二點: nasm 使用 bh.asm L17, 18 的方式來產生 0xaa55; gas 使用 bh.s L34,
35 的方法來產生 0xaa55; C 版本則使用 linker script 來產生 0xaa55, l.ld L21 ~
24
20120611 補充 C 語言篇:
模擬器畢竟是模擬器, 在真實的機器上測試果然就有問題了, 需要加入 L14 - L17, 將
%ds, %ss, %sp 設定好才行。
cb.c
1 ( http://goo.gl/3d1tcH ) __asm__(".code16gcc\n");
2 ( http://goo.gl/3d1tcH ) /*
3 ( http://goo.gl/3d1tcH ) * c bootloader
4 ( http://goo.gl/3d1tcH ) */
5 ( http://goo.gl/3d1tcH )
6 ( http://goo.gl/3d1tcH ) //#define POINTER_TEST
7 ( http://goo.gl/3d1tcH )
8 ( http://goo.gl/3d1tcH ) void main(const char *s);
9 ( http://goo.gl/3d1tcH )
10 ( http://goo.gl/3d1tcH ) int bbb=0; // test bss section
11 ( http://goo.gl/3d1tcH )
12 ( http://goo.gl/3d1tcH ) void WinMain(void)
13 ( http://goo.gl/3d1tcH ) {
14 ( http://goo.gl/3d1tcH ) __asm__ ("mov %cs, %ax\n");
15 ( http://goo.gl/3d1tcH ) __asm__ ("mov %ax, %ds\n");
16 ( http://goo.gl/3d1tcH ) __asm__ ("mov %ax, %ss\n");
17 ( http://goo.gl/3d1tcH ) __asm__ ("mov $0xff00, %sp\n");
...
35 ( http://goo.gl/3d1tcH ) }
36 ( http://goo.gl/3d1tcH )
37 ( http://goo.gl/3d1tcH ) #ifndef POINTER_TEST
38 ( http://goo.gl/3d1tcH ) void main(const char *s)
39 ( http://goo.gl/3d1tcH ) {
40 ( http://goo.gl/3d1tcH ) while(*s)
41 ( http://goo.gl/3d1tcH ) {
42 ( http://goo.gl/3d1tcH ) __asm__ __volatile__ ("int $0x10" : : "a"(0x0E00
| *s), "b"(7));
43 ( http://goo.gl/3d1tcH ) s++;
44 ( http://goo.gl/3d1tcH ) }
45 ( http://goo.gl/3d1tcH ) }
46 ( http://goo.gl/3d1tcH ) #endif
執行畫面像這樣:
( http://goo.gl/8BtA2Q )
20120726 補充:
身為 c++ 愛好者, 怎麼可以沒有 c++ 版本, 要不是因為 c++ runtime 實在太過複雜,
只好先屈就 c runtime, 我實際上很想用 c++ 來寫 os。
先來看看 c++ 版本, 不知道為什麼 g++ 一定會檢查 main() 的 prototype (加上
-ffreestanding 即可, 這樣 g++ 就不會檢查 main() prototype), 所以無法任意宣告 (
一定要 int main(int argc, char** argv)), 我只好將 main 改成 print, 我不知道有
無 option 可以關掉這種檢查。其他和 c 版本差不多。
12 ( http://goo.gl/3d1tcH ) extern "C" void WinMain(void)
c++ Mangling ( http://goo.gl/blDB4P ) 的特性會把 function 重新改名, 使用 L:12
來避免這樣的情形, 因為 linker script 指定 WinMain 當進入點, 要不然自己改
linker script。
用了一個 c++ 的特性, function 的參數可以有預設值, 要不然就和 c 版本沒什麼差別
了。
8 ( http://goo.gl/3d1tcH ) void print(const char *s, const char
*msg="\r\ng++ test");
cppb.cpp
1 ( http://goo.gl/3d1tcH ) __asm__(".code16gcc\n");
5 ( http://goo.gl/3d1tcH )
7 ( http://goo.gl/3d1tcH )
8 ( http://goo.gl/3d1tcH ) void print(const char *s, const char
*msg="\r\ng++ test");
9 ( http://goo.gl/3d1tcH )
11 ( http://goo.gl/3d1tcH )
12 ( http://goo.gl/3d1tcH ) extern "C" void WinMain(void)
13 ( http://goo.gl/3d1tcH ) {
14 ( http://goo.gl/3d1tcH ) __asm__ ("mov %cs, %ax\n");
15 ( http://goo.gl/3d1tcH ) __asm__ ("mov %ax, %ds\n");
16 ( http://goo.gl/3d1tcH ) __asm__ ("mov %ax, %ss\n");
17 ( http://goo.gl/3d1tcH ) __asm__ ("mov $0xfff0, %sp\n");
25 ( http://goo.gl/3d1tcH ) print("hello cpp");
36 ( http://goo.gl/3d1tcH ) while(1);
37 ( http://goo.gl/3d1tcH ) }
38 ( http://goo.gl/3d1tcH )
40 ( http://goo.gl/3d1tcH ) void print(const char *s, const char *msg)
41 ( http://goo.gl/3d1tcH ) {
42 ( http://goo.gl/3d1tcH ) #if 1
43 ( http://goo.gl/3d1tcH ) while(*s)
44 ( http://goo.gl/3d1tcH ) {
45 ( http://goo.gl/3d1tcH ) __asm__ __volatile__ ("int $0x10" : :
"a"(0x0E00 | *s), "b"(7));
46 ( http://goo.gl/3d1tcH ) s++;
47 ( http://goo.gl/3d1tcH ) }
48 ( http://goo.gl/3d1tcH ) #endif
49 ( http://goo.gl/3d1tcH ) #if 1
50 ( http://goo.gl/3d1tcH ) while(*msg)
51 ( http://goo.gl/3d1tcH ) {
52 ( http://goo.gl/3d1tcH ) __asm__ __volatile__ ("int $0x10" : :
"a"(0x0E00 | *msg), "b"(7));
53 ( http://goo.gl/3d1tcH ) msg++;
54 ( http://goo.gl/3d1tcH ) }
55 ( http://goo.gl/3d1tcH ) #endif
56 ( http://goo.gl/3d1tcH ) }
使用以下的步驟來產生 bootloader 執行檔 (linker script 同 c 版本那個):
g++ -m32 -g -Wall -Wextra -Werror -nostdlib -fno-builtin -nostartfiles
-nodefaultlibs -fno-exceptions -fno-rtti -fno-stack-protector -c cppb.cpp
ld -m elf_i386 -static -Tl.ld -nostdlib -M -o cppb.elf cppb.o > cb.elf.map
objcopy -R .pdr -R .comment -R.note -S -O binary cppb.elf cppb.bin
ref:
http://wiki.osdev.org/C%2b%2b_Bare_Bones ( http://goo.gl/EeqHT )
真實機器的執行畫面。
( http://goo.gl/DqyqjX )
以下方式可以取得本文的程式碼:
git clone git@github.com:descent/simple_os.git
cd simple_os
git checkout origin/cpp_bootloader -b cpp_bootloader
// 本文使用 Blog2BBS 自動將Blog文章轉成縮址的BBS純文字 http://goo.gl/TZ4E17 //
blog 原文:
http://descent-incoming.blogspot.tw/2012/05/0-hello-xyz.html
作者: aelin   2014-06-07 13:50:00
push
作者: alibuda174 (阿哩不達)   2014-06-08 08:41:00
推專業
作者: mikeqoo1 (海葵)   2014-06-29 22:49:00

Links booklink

Contact Us: admin [ a t ] ucptt.com