顯示具有 bare-metal x86_32 標籤的文章。 顯示所有文章
顯示具有 bare-metal x86_32 標籤的文章。 顯示所有文章

2012年9月12日 星期三

作業系統之前的程式 (0) - hello XYZ

the 1st edition: 20120506
the 2nd edition: 20150918
the 3rd edition: 20221207
environment: x86 32bit pc

本篇文章介紹在 pc 上寫所謂的「開機程式」, 這是在 legacy BIOS 的環境, UEFI 就不是這樣了。可能有點過時, 不過有些觀念還是可以在應用在其他平台, ex: arm。

uefi 的「開機程式」寫法請參考:
http://descent-incoming.blogspot.tw/search/label/uefi
https://descent-incoming.blogspot.com/search/label/uefi%20os%20loader

作業系統之前的程式有個很威的英文名詞 bare metal program, 什麼意思? 就是字面上的意思, 不另外解釋了。但它不見得是 pc 開機的第一隻程式, BIOS/UEFI 才是 pc 上開機時跑的的一支程式, 你可能覺得有些失落, 但沒辦法, 除非你打算寫 BIOS/UEFI。和你想像中的「開機程式」可能有些不同, 不過就算是 arm 平台的機器, 也有類似的東西, 通常會有一個 rom code 才是所謂的一開機執行的程式, 一般開發者是碰不到這塊的。

相關文章:
  1. 作業系統之前的程式 (1) - c++ 篇
  2. 作業系統之前的程式 (2) - c++ global object ctor/dtor can not be invoked
這是我在 mosut 分享的主題。介紹一下在作業系統之前的程式 (僅僅印出個字串)。寫一個作業系統之前的程式就和在作業系統下很類似, 不過有幾點要注意。
  1. bootloader hello world 被載入的位址是 0x7c00, 也就是從這裡開始執行。
  2. 510, 511 byte 必須是 0xaa, 0x55 結尾。
  3. 不能大於 512 byte, 其實扣掉 0x11, 0x55 2 byte, 只能有 510 byte。
  4. 沒有 printf 可以用, 得使用 bios call 或是寫入 video ram 來完成螢幕的輸出, 或是使用 com1 來輸出訊息。
可以使用組合語言, 還有大家很熟悉的 c/c++ 語言來寫這樣的練習程式, 其他可用的語言我就不知道了, 我只實作過這幾種語言。不過使用 toolchain 的方式和在作業系統下有點不同。也就是說, 程式的寫法是一樣的, 但是使用 compiler 產生執行檔的方式是不一樣的。而使用 C 語言只是展示用, 實際上一個 bootloader 要完成的事情可能無法使用 C 語言來完成, 因為很容易就超過 512 byte。

bh.asm
 1  org 07c00h   ; 告訴編譯器程序加載到 7c00 處
 2  mov ax, cs
 3  mov ds, ax
 4  mov es, ax
 5  call DispStr   ; 使用顯示字元串例程
 6  jmp $   ; 無限循環
 7 DispStr:
 8  mov ax, BootMessage
 9  mov bp, ax   ; ES:BP = 串位址
10  mov cx, 16   ; CX = 串長度
11  mov ax, 01301h  ; AH = 13,  AL = 01h
12  mov bx, 000ch  ; 頁號為 0(BH = 0) 黑底紅字 (BL = 0Ch, 高亮)
13  mov dl, 0
14  int 10h   ; 10h 號中斷
15  ret
16 BootMessage:  db "Hello, NASM!"
17 times  510-($-$$) db 0 ; 填充剩下的空間,使生成的二進制程式碼恰好為 512 字節
18 dw  0xaa55    ; 結束標誌


bh.asm 是用 nasm 的 bootloader 程式 (from Orange's 一個作業系統的實現 p 1-3), bh.asm 的組合語言是 intel 語法, 使用 nasm 產生 bootloader 的可執行檔很簡單。

nasm bh.asm -o bh.asm.bin

bh.asm L1 的註解不算精確, 應該是告訴 linker 這個程式需要計算的位址以 0x07c00 為基準去計算。把它載入 0x07c00 可以正常執行, 載入到其他位址可能會有問題。

bh.asm.bin 為 512 byte 的執行檔, 寫入軟碟第 1 個磁區即可。把軟碟放入開機後即可看到 Hello, NASM!



hb.s
 1 .code16
 2 .text
 3 .global begin
 4 begin:
 5   mov     %cs,%ax
 6   mov     %ax,%ds
 7   mov     %ax,%es
 8   movw    $0xb800, %ax
 9   movw    %ax, %gs
10 
11 
12   mov    $0, %edi    /* Destination */
13   mov    $msg, %esi   /* Source */
14 
15 1:
16   #cmp $0, %ecx
17   cmpb $0, (%esi)
18   jz 2f
19   movb %ds:(%esi), %al
20   inc %esi
21   movb %al, %gs:(%edi)
22   inc %edi
23   movb $0xc, %gs:(%edi)
24   inc %edi
25   dec %ecx
26   jmp 1b
27 2:
28   movb $'E', %gs:(160)
29   jmp .
30 #msg:.ascii "Hello GAS"
31 msg:
32   .asciz "Hello GAS"
33   #.asciz "Hello World"
34 .org 510
35 .word 0xaa55


bh.s 是使用 gas 來完成的 bootloader 程式, 範例的組合語言是 at & t 語法, 使用 gas 產生 bootloader 的可執行檔有點複雜, 除了程式碼本身, 還要一個 linker script, 在連結的時候使用。

hb.s L1 .code16 是告知 gcc 輸出 x86 16 bit 的組合語言, 應該很少人看過這個用法。

as.ld
 1 ENTRY(begin);
 2 SECTIONS
 3 {
 4     . = 0x7C00;
 5     .text : AT(0x7C00)
 6     {
 7         _text = .;
 8         *(.text);
 9         _text_end = .;
10     }
11     .data :
12     {
13         _data = .;
14         *(.bss);
15         *(.bss*);
16         *(.data);
17         *(.rodata*);
18         *(COMMON)
19         _data_end = .;
20     }
21 /*
22     .sig : AT(0x7DFE)
23     {
24         SHORT(0xaa55);
25     }
26 */
27     /DISCARD/ :
28     {
29         *(.note*);
30         *(.iplt*);
31         *(.igot*);
32         *(.rel*);
33         *(.comment);
34 /* add any unwanted sections spewed out by your version of gcc and flags here */
35     }
36 }


產生 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




bh.s.bin 就是 bootloader 執行檔, 不像 nasm 那麼乾脆是吧! 你一定不喜歡, 不過這樣的學習負擔是有額外的收穫的。下面的 C 語言版本就需要這樣的作法。

cb.c
 1 __asm__(".code16gcc\n");
 2 /*
 3  * c bootloader
 4  */
 5 
 6 void main(const char   *s);
 7 
 8 int bbb=0; // test bss section
 9 
10 void WinMain(void)
11 {
12   main("hello world");
13   while(1);
14 }
15 
16 void main(const char   *s)
17 {
18   while(*s)
19   {
20     __asm__ __volatile__ ("int  $0x10" : : "a"(0x0E00 | *s), "b"(7));
21     s++;
22   }
23 }


很像一般的 C 程式吧! 不過我們有著 windows 才有的 WinMain 和一般 C 的 main, 哪個才是程式開始的地方呢?仍然需要搭配的 linker script, 從以下的 linker script 可以看出, 我們的程式由 WinMain 開始。

l.ld
 1 /* for cb.c */
 2 ENTRY(WinMain);
 3 SECTIONS
 4 {
 5     . = 0x7C00;
 6     .text : AT(0x7C00)
 7     {
 8         _text = .;
 9         *(.text);
10         _text_end = .;
11     }
12     .data :
13     {
14         _data = .;
15         *(.bss);
16         *(.bss*);
17         *(.data);
18         *(.rodata*);
19         *(COMMON)
20         _data_end = .;
21     }
22     .sig : AT(0x7DFE)
23     {
24         SHORT(0xaa55);
25     }
26     /DISCARD/ :
27     {
28         *(.note*);
29         *(.iplt*);
30         *(.igot*);
31         *(.rel*);
32         *(.comment);
33 /* add any unwanted sections spewed out by your version of gcc and flags here */
34     }
35 }


使用以下的步驟來產生 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

0x7DFE 是 0x7C00 + 510 得到, 落在 512 最後的 2 byte, 從執行位址 0x7C00 開始算。

20120611 補充 C 語言篇:
模擬器畢竟是模擬器, 在真實的機器上測試果然就有問題了, 需要加入 L14 - L17, 將 %ds, %ss, %sp 設定好才行。

cb.c
 1 __asm__(".code16gcc\n");
 2 /*
 3  * c bootloader
 4  */
 5 
 6 //#define POINTER_TEST
 7 
 8 void main(const char   *s);
 9 
10 int bbb=0; // test bss section
11 
12 void WinMain(void)
13 {
14   __asm__ ("mov  %cs, %ax\n");
15   __asm__ ("mov  %ax, %ds\n");
16   __asm__ ("mov  %ax, %ss\n");
17   __asm__ ("mov  $0xff00, %sp\n");
     ...
35 }
36 
37 #ifndef POINTER_TEST
38 void main(const char   *s)
39 {
40   while(*s)
41   {
42     __asm__ __volatile__ ("int  $0x10" : : "a"(0x0E00 | *s), "b"(7));
43     s++;
44   }
45 }
46 #endif


執行畫面像這樣:



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 extern "C" void WinMain(void)

c++ Mangling 的特性會把 function 重新改名, 使用 L:12 來避免這樣的情形, 因為 linker script 指定 WinMain 當進入點, 要不然自己改 linker script。

用了一個 c++ 的特性, function 的參數可以有預設值, 要不然就和 c 版本沒什麼差別了。

 8 void print(const char   *s, const char *msg="\r\ng++ test");

cppb.cpp
 1 __asm__(".code16gcc\n");
 5 
 7 
 8 void print(const char   *s, const char *msg="\r\ng++ test");
 9 
11 
12 extern "C" void WinMain(void)
13 {
14   __asm__ ("mov  %cs, %ax\n");
15   __asm__ ("mov  %ax, %ds\n");
16   __asm__ ("mov  %ax, %ss\n");
17   __asm__ ("mov  $0xfff0, %sp\n");
25   print("hello cpp");
36   while(1);
37 }
38 
40 void print(const char   *s, const char *msg)
41 {
42 #if 1
43   while(*s)
44   {
45     __asm__ __volatile__ ("int  $0x10" : : "a"(0x0E00 | *s), "b"(7));
46     s++;
47   }
48 #endif
49 #if 1
50   while(*msg)
51   {
52     __asm__ __volatile__ ("int  $0x10" : : "a"(0x0E00 | *msg), "b"(7));
53     msg++;
54   }
55 #endif
56 }


使用以下的步驟來產生 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

真實機器的執行畫面。



以下方式可以取得本文的程式碼:

git clone git@github.com:descent/simple_os.git
cd simple_os
git checkout origin/cpp_bootloader -b cpp_bootloader

以下的簡報是之前在 mosut 的分享, 也就是這篇文章的主題之一:


ref:
使用 GCC 和 GNU Binutils 编写能在 x86 实模式运行的 16 位代码
X86 Booting Sequence - X86 開機流程小

2012年8月10日 星期五

作業系統之前的程式 (1) - c++ 篇

這是作業系統之前的程式 (0) - hello XYZ的 c++ 版本。 作業系統之前的程式 (0) - hello XYZ這是在 mosut 分享的一個主題。後來補充了 c++ 版本, 示範了預設參數的 function c++ 語言特性。

不過 c++ 主要的 class 特性若不能使用, 會使得 c++ 的能力大大失色 (和 c 相差不遠), 這次的分享就是來使用 c++ 的 class。

fig 1 在真實機器上測試

一直以來都很喜歡 c++ 這個語言, simple os 很想用 c++ 來完成, 無奈實在不知道如何實作 c++ runtime, 目前透過 ref 1 可以完成到這樣的階段 (我找到這資料時, 你可以想像到我有多興奮)。c++ 有很多豐富的特性, 有些需要 c++ runtime 來實作, 目前這版本只能做到這樣, 千萬別帥氣的把 rtti, exception 這樣的語法帶入, 肯定會失敗的, 也無法使用 global/static object。global object 還好, 很多書籍都告知要少用 global object, static object 嘛 ... 那就真的沒辦法了。

c++ 似乎帶給某些程式員效率低下的錯覺, 沒錯, 要使用某些語言特性, 像是 rtti, exception 是會有額外負擔的, 「要馬兒好又要馬兒到對方陣營偷糧草」這樣的事情是不存在的。

不過以這個例子來說, 是和 c 一樣快的。ctor/dtor 並沒有帶來效率上的額外負擔, 不過由於在 ctor 需要使用 exception handle 來處理錯誤, 所以正常的 ctor, 應該會有額外的負擔。這負擔有多大呢?我不清楚, 我還不會處理 exception handle runtime, 距離這篇文章四年之後 (真的是好久), 我寫了以下幾篇關於 exception handle 的文章:
但程式是自己寫的, 可以用某些方法迴避使用 exception handle。

-fno-exceptions 一定需要, 要不然會需要這個 symbol __gxx_personality_v0 需要 link -lstdc++(你應該不會預期可以 link stdc++ 吧?), 在 os 之前的 c++ 要使用 exception, 得自己提供這段程式碼。

喜歡語言評論的朋友可以參考:
我喜歡其中的一句話: 「由於 c++ 和 c 真的很像, 有人把 c 的坑也算到 c++頭上了」XD

和之前一樣, 列出所有檔案以其 linker script, 從程式來看就和一般 os 下的 c++ 程式一樣, 有變化的一樣是編譯流程。

io.h
 1 __asm__(".code16gcc\n");
 2 #ifndef IO_H
 3 #define IO_H
 4 
 5 class Io
 6 {
 7   public:
 8     Io();
 9     ~Io();
10     void print(const char   *s);
11   private:
12 };
13 
14 
15 #endif

io.cpp
 1 #include "io.h"
 2 Io::Io()
 3 {
 4   print("ctor\r\n");
 5 }
 6 Io::~Io()
 7 {
 8   print("dtor\r\n");
 9 }
10 
11 void Io::print(const char   *s)
12 {
13   while(*s)
14   {
15     __asm__ __volatile__ ("int  $0x10" : : "a"(0x0E00 | *s), "b"(7));
16     s++;
17   }
18 }

cppb.cpp
 1 __asm__(".code16gcc\n");
 2 #include "io.h"
 3 
 4 /*
 5  * c bootloader
 6  */
 7 
 8 //#define POINTER_TEST
 9 
10 
11 int bbb=0; // test bss section
12 
13 extern "C" void WinMain(void)
14 {
15   __asm__ ("mov  %cs, %ax\n");
16   __asm__ ("mov  %ax, %ds\n");
17   __asm__ ("mov  %ax, %ss\n");
18   __asm__ ("mov  $0xfff0, %sp\n");
19   {
20   Io io;
21   io.print("hello cpp class\r\n");
22   }
23   #if 0
24   unsigned char *vb = (unsigned char *)0xb8000;
25   *vb = 'A';
26   *(unsigned char *)0xb8001 = 0xc;
27   *(unsigned char *)0xb8002 = 'B';
28   *(unsigned char *)0xb8003 = 0x9;
29   *(unsigned char *)0xb8004 = '@';
30   *(unsigned char *)0xb8005 = 0xc;
31   #endif
32   while(1);
33 }

cpp.ld
 1 /* for cb.c */
 2 ENTRY(WinMain);
 3 SECTIONS
 4 {
 5 
 6     . = 0x7C00;
 7     .text : AT(0x7C00)
 8     {
 9         *(.text)
10         *(.gnu.linkonce.t*)
11     }
12 
13     .rodata :
14     {
15         start_ctors = .;
16         *(SORT(.ctors.*))  /* Note the "SORT" */
17         end_ctors = .;
18 
19         start_dtors = .;
20         *(SORT(.dtors.*))
21         end_dtors = .;
22 
23         *(.rodata*)
24         *(.gnu.linkonce.r*)
25     }
26 
27     .data :
28     {
29         *(.data)
30         *(.gnu.linkonce.d*)
31     }
32 
33     .bss :
34     {
35         sbss = .;
36         *(COMMON)
37         *(.bss)
38         *(.gnu.linkonce.b*)
39         ebss = .;
40     }
41 
42     .sig : AT(0x7DFE)
43     {
44         SHORT(0xaa55);
45     }
46     /DISCARD/ :
47     {
48         *(.comment)
49         *(.eh_frame) /* discard this, unless you are implementing time support for C++ exceptions. */
52     }
53 }

g++  -m32  -g -Wall -Wextra -Werror -nostdlib -fno-builtin -nostartfiles -nodefaultlibs -fno-exceptions -fno-rtti -fno-stack-protector  -c -o cppb.o cppb.cpp
g++  -m32  -g -Wall -Wextra -Werror -nostdlib -fno-builtin -nostartfiles -nodefaultlibs -fno-exceptions -fno-rtti -fno-stack-protector  -c -o io.o io.cpp
ld -m elf_i386 -static -Tcpp.ld -nostdlib -M -o cppb.elf cppb.o io.o > cb.elf.map
objcopy -R .pdr -R .comment -R.note -S -O binary cppb.elf cppb.bin

dd if=/dev/zero of=floppy.img skip=1 seek=1 bs=512 count=2879
dd if=cppb.bin of=floppy.img bs=1 count=512 conv=notrunc
qemu -fda floppy.img

除了使用 qemu 來測試, 大多數也會用實機來測試, 之前實在吃過太多苦頭, 沒有實機測試過, 真的不放心。

取得本文的程式碼:

git clone git@github.com:descent/simple_os.git
cd simple_os
git checkout origin/cpp_bootloader -b cpp_bootloader


以下是把 io object 宣告為 global 和 static 的錯誤訊息 (早就說過不行了, 還要試, A ... 理論是理論, 總是要驗證一下才是)。

global object error message
ld -m elf_i386 -static -Tcpp.ld -nostdlib -M -o cppb.elf cppb.o io.o > cb.elf.map
ld: section .sig loaded at [0000000000007dfe,0000000000007dff] overlaps section .rodata loaded at [0000000000007de4,0000000000007e11]
cppb.o: In function `__static_initialization_and_destruction_0':
/home/descent/test/simple_os.test/c-bootloader/cpp/cppb.cpp:12: undefined reference to `__dso_handle'
/home/descent/test/simple_os.test/c-bootloader/cpp/cppb.cpp:12: undefined reference to `__cxa_atexit'

static object error message
g++ -m32 -g -Wall -Wextra -Werror -nostdlib -fno-builtin -nostartfiles -nodefaultlibs -fno-exceptions -fno-rtti -fno-stack-protector -c -o cppb.o cppb.cpp
ld -m elf_i386 -static -Tcpp.ld -nostdlib -M -o cppb.elf cppb.o io.o > cb.elf.map
ld: section .sig loaded at [0000000000007dfe,0000000000007dff] overlaps section .rodata loaded at [0000000000007dd4,0000000000007e01]
cppb.o: In function `WinMain':
/home/descent/test/simple_os.test/c-bootloader/cpp/cppb.cpp:20: undefined reference to `__cxa_guard_acquire'
/home/descent/test/simple_os.test/c-bootloader/cpp/cppb.cpp:20: undefined reference to `__cxa_guard_release'
/home/descent/test/simple_os.test/c-bootloader/cpp/cppb.cpp:20: undefined reference to `__dso_handle'
/home/descent/test/simple_os.test/c-bootloader/cpp/cppb.cpp:20: undefined reference to `__cxa_atexit'
make: *** [cppb.elf] Error 1

不好搞, 沒錯吧?
ref: https://www.google.com/search?q=__cxa_atexit+osdev+c%2B%2B&ie=utf-8&oe=utf-8&client=ubuntu&channel=fs


23: 00007ca4    83 FUNC    GLOBAL DEFAULT    1 _ZN2IoC1Ev
    15: 00007c50    83 FUNC    GLOBAL DEFAULT    1 _ZN2IoC2Ev
    27: 00007d20    39 FUNC    GLOBAL DEFAULT    1 _ZN2IoD1Ev
    17: 00007cf8    39 FUNC    GLOBAL DEFAULT    1 _ZN2IoD2Ev
    26: 00007d48    68 FUNC    GLOBAL DEFAULT    1 _ZN2Io5printEPKc

紅色的 c++ mangling 意思請參考 (這也是 c++ ABI 不相容的原因之一): http://mentorembedded.github.com/cxx-abi/abi-mangling.html

對一下應該勉強看得懂。
C1, C2 是 constructor
D1, D2 是 destructor

雖然執行速度和 c 一樣, 不過大小好像比 c 大了點。ctor/dtor 竟然要兩份。不懂阿 ...

ref:
  1. http://wiki.osdev.org/C%2B%2B_Bare_Bones
  2. http://wiki.osdev.org/C%2B%2B
  3. C++ Bare Bones
  4. C++ 写内核需要注意的一些事情 (原创)

2012年8月9日 星期四

作業系統之前的程式 (2) - c++ global object ctor/dtor can not be invoked

作業系統之前的程式 (1) - c++ 篇的差別是只把 Io io 搬到 WinMain 的外面, 但結果可是大大不同。

cppb.cpp
 1 __asm__(".code16gcc\n");
 2 #include "io.h"
 3 
 4 /*
 5  * c bootloader
 6  */
 7 
 8 //#define POINTER_TEST
 9 
10 
11 int bbb=0; // test bss section
12 void *__dso_handle;
13 int __cxa_atexit(void (*destructor) (void *), void *arg, void *__dso_handle)
14 {
15   return 0;
16 }
17 
18   Io io;
19 
20 extern "C" void WinMain(void)
21 {
22   __asm__ ("mov  %cs, %ax\n");
23   __asm__ ("mov  %ax, %ds\n");
24   __asm__ ("mov  %ax, %ss\n");
25   __asm__ ("mov  $0xfff0, %sp\n");
26   {
27   io.print("hello cpp class\r\n");
28   }
29   #if 0
30   unsigned char *vb = (unsigned char *)0xb8000;
31   *vb = 'A';
32   *(unsigned char *)0xb8001 = 0xc;
33   *(unsigned char *)0xb8002 = 'B';
34   *(unsigned char *)0xb8003 = 0x9;
35   *(unsigned char *)0xb8004 = '@';
36   *(unsigned char *)0xb8005 = 0xc;
37   #endif
38   while(1);
39 }


ld -m elf_i386 -static -Tcpp.ld -nostdlib -M -o cppb.elf cppb.o io.o > cb.elf.map
ld: section .sig loaded at [0000000000007dfe,0000000000007dff] overlaps section .rodata loaded at [0000000000007df4,0000000000007e21]
cppb.o: In function `__static_initialization_and_destruction_0':
/home/descent/test/simple_os.test/c-bootloader/cpp/cppb.cpp:18: undefined reference to `__cxa_atexit'
make: *** [cppb.elf] Error 1

12 void *__dso_handle;
13 int __cxa_atexit(void (*destructor) (void *), void *arg, void *__dso_handle)
14 {
15   return 0;
16 }
18   Io io;

為了使用 global object 要補上 __cxa_atexit (註冊 dtor 用), __dso_handle (dynamic share object 相關), 這樣 link, compile 才會過。而令人害怕的是:

__static_initialization_and_destruction_0

我的 c++ 程式碼根本沒有這行 (要不是忘記 extern "C", 我可能沒發現, 不過用 objdump 還是可以看到), 所以肯定是 g++ 幹的好事。只不過把 io 宣告成 global object, 就發生了這麼大的差異。c++ 編譯器果然幹了不少事情, 也讓這支程式大於 512 byte, 看吧! c++ 的程式絕對不是大假的。

還有這個 _GLOBAL__I_io 也是 g++ 產生的。

_GLOBAL__I_io, __static_initialization_and_destruction_0 是幹嘛用的? 請參考: c++ runtime - global object ctor (0)

objdump -d cppb.elf
  1 
  2 cppb.elf:     file format elf32-i386
  3 
  4 
  5 Disassembly of section .text:
  6 
  7 00000100 <WinMain>:
  8  100: 66 55                 push   %bp
  9  102: 66 89 e5              mov    %sp,%bp
 10  105: 66 83 ec 18           sub    $0x18,%sp
 11  109: 87 db                 xchg   %ebx,%ebx
 12  10b: 8c c8                 mov    %cs,%eax
 13  10d: 8e d8                 mov    %eax,%ds
 14  10f: 8e d0                 mov    %eax,%ss
 15  111: 67 66 c7 44 24 04 f8  addr16 movw $0xf804,0x24(%si)
 16  118: 02 00                 add    (%eax),%al
 17  11a: 00 67 66              add    %ah,0x66(%edi)
 18  11d: c7 04 24 2c 03 00 00  movl   $0x32c,(%esp)
 19  124: 66 e8 8a 01           callw  2b2 <_ZN2IoD1Ev+0x26>
 20  128: 00 00                 add    %al,(%eax)
 21  12a: b8 00 4c cd 21        mov    $0x21cd4c00,%eax
 22  12f: 66 c9                 leavew 
 23  131: 66 c3                 retw   
 24 
 25 00000133 <__cxa_atexit>:
 26  133: 66 55                 push   %bp
 27  135: 66 89 e5              mov    %sp,%bp
 28  138: 66 b8 00 00           mov    $0x0,%ax
 29  13c: 00 00                 add    %al,(%eax)
 30  13e: 66 5d                 pop    %bp
 31  140: 66 c3                 retw   
 32 
 33 00000142 <_Z41__static_initialization_and_destruction_0ii>:
 34  142: 66 55                 push   %bp
 35  144: 66 89 e5              mov    %sp,%bp
 36  147: 66 83 ec 18           sub    $0x18,%sp
 37  14b: 67 66 83 7d 08 01     addr16 cmpw $0x1,0x8(%di)
 38  151: 75 3f                 jne    192 <_Z41__static_initialization_and_destruction_0ii+0x50>
 39  153: 67 66 81 7d 0c ff ff  addr16 cmpw $0xffff,0xc(%di)
 40  15a: 00 00                 add    %al,(%eax)
 41  15c: 75 34                 jne    192 <_Z41__static_initialization_and_destruction_0ii+0x50>
 42  15e: 67 66 c7 04 24 2c     addr16 movw $0x2c24,(%si)
 43  164: 03 00                 add    (%eax),%eax
 44  166: 00 66 e8              add    %ah,-0x18(%esi)
 45  169: a3 00 00 00 66        mov    %eax,0x66000000
 46  16e: b8 8c 02 00 00        mov    $0x28c,%eax
 47  173: 67 66 c7 44 24 08 30  addr16 movw $0x3008,0x24(%si)
 48  17a: 03 00                 add    (%eax),%eax
 49  17c: 00 67 66              add    %ah,0x66(%edi)
 50  17f: c7 44 24 04 2c 03 00  movl   $0x32c,0x4(%esp)
 51  186: 00 
 52  187: 67 66 89 04           addr16 mov %ax,(%si)
 53  18b: 24 66                 and    $0x66,%al
 54  18d: e8 a1 ff ff ff        call   133 <__cxa_atexit>
 55  192: 66 c9                 leavew 
 56  194: 66 c3                 retw   
 57 
 58 00000196 <_GLOBAL__I_io>:
 59  196: 66 55                 push   %bp
 60  198: 66 89 e5              mov    %sp,%bp
 61  19b: 66 83 ec 18           sub    $0x18,%sp
 62  19f: 67 66 c7 44 24 04 ff  addr16 movw $0xff04,0x24(%si)
 63  1a6: ff 00                 incl   (%eax)
 64  1a8: 00 67 66              add    %ah,0x66(%edi)
 65  1ab: c7 04 24 01 00 00 00  movl   $0x1,(%esp)
 66  1b2: 66 e8 8a ff           callw  140 <__cxa_atexit+0xd>
 67  1b6: ff                    (bad)  
 68  1b7: ff 66 c9              jmp    *-0x37(%esi)
 69  1ba: 66 c3                 retw   
 70 
 71 000001bc <_ZN2IoC2Ev>:
 72  1bc: 66 55                 push   %bp
 73  1be: 66 89 e5              mov    %sp,%bp
 74  1c1: 66 83 ec 18           sub    $0x18,%sp
 75  1c5: 67 66 8b 45 08        addr16 mov 0x8(%di),%ax
 76  1ca: 67 66 c7 00 0a 03     addr16 movw $0x30a,(%bx,%si)
 77  1d0: 00 00                 add    %al,(%eax)
 78  1d2: 67 66 c7 44 24 04 18  addr16 movw $0x1804,0x24(%si)
 79  1d9: 03 00                 add    (%eax),%eax
 80  1db: 00 67 66              add    %ah,0x66(%edi)
 81  1de: 8b 45 08              mov    0x8(%ebp),%eax
 82  1e1: 67 66 89 04           addr16 mov %ax,(%si)
 83  1e5: 24 66                 and    $0x66,%al
 84  1e7: e8 c8 00 00 00        call   2b4 <_ZN2Io5printEPKc>
 85  1ec: 67 66 8b 45 08        addr16 mov 0x8(%di),%ax
 86  1f1: 67 66 8b 00           addr16 mov (%bx,%si),%ax
 87  1f5: 67 66 89 44 24        addr16 mov %ax,0x24(%si)
 88  1fa: 04 67                 add    $0x67,%al
 89  1fc: 66 8b 45 08           mov    0x8(%ebp),%ax
 90  200: 67 66 89 04           addr16 mov %ax,(%si)
 91  204: 24 66                 and    $0x66,%al
 92  206: e8 a9 00 00 00        call   2b4 <_ZN2Io5printEPKc>
 93  20b: 66 c9                 leavew 
 94  20d: 66 c3                 retw   
 95  20f: 90                    nop
 96 
 97 00000210 <_ZN2IoC1Ev>:
 98  210: 66 55                 push   %bp
 99  212: 66 89 e5              mov    %sp,%bp
100  215: 66 83 ec 18           sub    $0x18,%sp
101  219: 67 66 8b 45 08        addr16 mov 0x8(%di),%ax
102  21e: 67 66 c7 00 0a 03     addr16 movw $0x30a,(%bx,%si)
103  224: 00 00                 add    %al,(%eax)
104  226: 67 66 c7 44 24 04 18  addr16 movw $0x1804,0x24(%si)
105  22d: 03 00                 add    (%eax),%eax
106  22f: 00 67 66              add    %ah,0x66(%edi)
107  232: 8b 45 08              mov    0x8(%ebp),%eax
108  235: 67 66 89 04           addr16 mov %ax,(%si)
109  239: 24 66                 and    $0x66,%al
110  23b: e8 74 00 00 00        call   2b4 <_ZN2Io5printEPKc>
111  240: 67 66 8b 45 08        addr16 mov 0x8(%di),%ax
112  245: 67 66 8b 00           addr16 mov (%bx,%si),%ax
113  249: 67 66 89 44 24        addr16 mov %ax,0x24(%si)
114  24e: 04 67                 add    $0x67,%al
115  250: 66 8b 45 08           mov    0x8(%ebp),%ax
116  254: 67 66 89 04           addr16 mov %ax,(%si)
117  258: 24 66                 and    $0x66,%al
118  25a: e8 55 00 00 00        call   2b4 <_ZN2Io5printEPKc>
119  25f: 66 c9                 leavew 
120  261: 66 c3                 retw   
121  263: 90                    nop
122 
123 00000264 <_ZN2IoD2Ev>:
124  264: 66 55                 push   %bp
125  266: 66 89 e5              mov    %sp,%bp
126  269: 66 83 ec 18           sub    $0x18,%sp
127  26d: 67 66 c7 44 24 04 1f  addr16 movw $0x1f04,0x24(%si)
128  274: 03 00                 add    (%eax),%eax
129  276: 00 67 66              add    %ah,0x66(%edi)
130  279: 8b 45 08              mov    0x8(%ebp),%eax
131  27c: 67 66 89 04           addr16 mov %ax,(%si)
132  280: 24 66                 and    $0x66,%al
133  282: e8 2d 00 00 00        call   2b4 <_ZN2Io5printEPKc>
134  287: 66 c9                 leavew 
135  289: 66 c3                 retw   
136  28b: 90                    nop
137 
138 0000028c <_ZN2IoD1Ev>:
139  28c: 66 55                 push   %bp
140  28e: 66 89 e5              mov    %sp,%bp
141  291: 66 83 ec 18           sub    $0x18,%sp
142  295: 67 66 c7 44 24 04 1f  addr16 movw $0x1f04,0x24(%si)
143  29c: 03 00                 add    (%eax),%eax
144  29e: 00 67 66              add    %ah,0x66(%edi)
145  2a1: 8b 45 08              mov    0x8(%ebp),%eax
146  2a4: 67 66 89 04           addr16 mov %ax,(%si)
147  2a8: 24 66                 and    $0x66,%al
148  2aa: e8 05 00 00 00        call   2b4 <_ZN2Io5printEPKc>
149  2af: 66 c9                 leavew 
150  2b1: 66 c3                 retw   
151  2b3: 90                    nop
152 
153 000002b4 <_ZN2Io5printEPKc>:
154  2b4: 66 55                 push   %bp
155  2b6: 66 89 e5              mov    %sp,%bp
156  2b9: 66 53                 push   %bx
157  2bb: eb 22                 jmp    2df <_ZN2Io5printEPKc+0x2b>
158  2bd: 67 66 8b 45 0c        addr16 mov 0xc(%di),%ax
159  2c2: 67 66 0f b6 00        addr16 movzbw (%bx,%si),%ax
160  2c7: 66 0f be c0           movsbw %al,%ax
161  2cb: 80 cc 0e              or     $0xe,%ah
162  2ce: 66 ba 07 00           mov    $0x7,%dx
163  2d2: 00 00                 add    %al,(%eax)
164  2d4: 66 89 d3              mov    %dx,%bx
165  2d7: cd 10                 int    $0x10
166  2d9: 67 66 83 45 0c 01     addr16 addw $0x1,0xc(%di)
167  2df: 67 66 8b 45 0c        addr16 mov 0xc(%di),%ax
168  2e4: 67 66 0f b6 00        addr16 movzbw (%bx,%si),%ax
169  2e9: 84 c0                 test   %al,%al
170  2eb: 0f 95 c0              setne  %al
171  2ee: 84 c0                 test   %al,%al
172  2f0: 75 cb                 jne    2bd <_ZN2Io5printEPKc+0x9>
173  2f2: 66 5b                 pop    %bx
174  2f4: 66 5d                 pop    %bp
175  2f6: 66 c3                 retw   

objdump -m i8086 -d cppb.elf
  1 
  2 cppb.elf:     file format elf32-i386
  3 
  4 
  5 Disassembly of section .text:
  6 
  7 00000100 <WinMain>:
  8  100: 66 55                 push   %ebp
  9  102: 66 89 e5              mov    %esp,%ebp
 10  105: 66 83 ec 18           sub    $0x18,%esp
 11  109: 87 db                 xchg   %bx,%bx
 12  10b: 8c c8                 mov    %cs,%ax
 13  10d: 8e d8                 mov    %ax,%ds
 14  10f: 8e d0                 mov    %ax,%ss
 15  111: 67 66 c7 44 24 04 f8  addr32 movl $0x2f8,0x4(%esp)
 16  118: 02 00 00 
 17  11b: 67 66 c7 04 24 2c 03  addr32 movl $0x32c,(%esp)
 18  122: 00 00 
 19  124: 66 e8 8a 01 00 00     calll  2b4 <_ZN2Io5printEPKc>
 20  12a: b8 00 4c              mov    $0x4c00,%ax
 21  12d: cd 21                 int    $0x21
 22  12f: 66 c9                 leavel 
 23  131: 66 c3                 retl   
 24 
 25 00000133 <__cxa_atexit>:
 26  133: 66 55                 push   %ebp
 27  135: 66 89 e5              mov    %esp,%ebp
 28  138: 66 b8 00 00 00 00     mov    $0x0,%eax
 29  13e: 66 5d                 pop    %ebp
 30  140: 66 c3                 retl   
 31 
 32 00000142 <_Z41__static_initialization_and_destruction_0ii>:
 33  142: 66 55                 push   %ebp
 34  144: 66 89 e5              mov    %esp,%ebp
 35  147: 66 83 ec 18           sub    $0x18,%esp
 36  14b: 67 66 83 7d 08 01     addr32 cmpl $0x1,0x8(%ebp)
 37  151: 75 3f                 jne    192 <_Z41__static_initialization_and_destruction_0ii+0x50>
 38  153: 67 66 81 7d 0c ff ff  addr32 cmpl $0xffff,0xc(%ebp)
 39  15a: 00 00 
 40  15c: 75 34                 jne    192 <_Z41__static_initialization_and_destruction_0ii+0x50>
 41  15e: 67 66 c7 04 24 2c 03  addr32 movl $0x32c,(%esp)
 42  165: 00 00 
 43  167: 66 e8 a3 00 00 00     calll  210 <_ZN2IoC1Ev>
 44  16d: 66 b8 8c 02 00 00     mov    $0x28c,%eax
 45  173: 67 66 c7 44 24 08 30  addr32 movl $0x330,0x8(%esp)
 46  17a: 03 00 00 
 47  17d: 67 66 c7 44 24 04 2c  addr32 movl $0x32c,0x4(%esp)
 48  184: 03 00 00 
 49  187: 67 66 89 04 24        addr32 mov %eax,(%esp)
 50  18c: 66 e8 a1 ff ff ff     calll  133 <__cxa_atexit>
 51  192: 66 c9                 leavel 
 52  194: 66 c3                 retl   
 53 
 54 00000196 <_GLOBAL__I_io>:
 55  196: 66 55                 push   %ebp
 56  198: 66 89 e5              mov    %esp,%ebp
 57  19b: 66 83 ec 18           sub    $0x18,%esp
 58  19f: 67 66 c7 44 24 04 ff  addr32 movl $0xffff,0x4(%esp)
 59  1a6: ff 00 00 
 60  1a9: 67 66 c7 04 24 01 00  addr32 movl $0x1,(%esp)
 61  1b0: 00 00 
 62  1b2: 66 e8 8a ff ff ff     calll  142 <_Z41__static_initialization_and_destruction_0ii>
 63  1b8: 66 c9                 leavel 
 64  1ba: 66 c3                 retl   
 65 
 66 000001bc <_ZN2IoC2Ev>:
 67  1bc: 66 55                 push   %ebp
 68  1be: 66 89 e5              mov    %esp,%ebp
 69  1c1: 66 83 ec 18           sub    $0x18,%esp
 70  1c5: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
 71  1ca: 67 66 c7 00 0a 03 00  addr32 movl $0x30a,(%eax)
 72  1d1: 00 
 73  1d2: 67 66 c7 44 24 04 18  addr32 movl $0x318,0x4(%esp)
 74  1d9: 03 00 00 
 75  1dc: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
 76  1e1: 67 66 89 04 24        addr32 mov %eax,(%esp)
 77  1e6: 66 e8 c8 00 00 00     calll  2b4 <_ZN2Io5printEPKc>
 78  1ec: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
 79  1f1: 67 66 8b 00           addr32 mov (%eax),%eax
 80  1f5: 67 66 89 44 24 04     addr32 mov %eax,0x4(%esp)
 81  1fb: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
 82  200: 67 66 89 04 24        addr32 mov %eax,(%esp)
 83  205: 66 e8 a9 00 00 00     calll  2b4 <_ZN2Io5printEPKc>
 84  20b: 66 c9                 leavel 
 85  20d: 66 c3                 retl   
 86  20f: 90                    nop
 87 
 88 00000210 <_ZN2IoC1Ev>:
 89  210: 66 55                 push   %ebp
 90  212: 66 89 e5              mov    %esp,%ebp
 91  215: 66 83 ec 18           sub    $0x18,%esp
 92  219: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
 93  21e: 67 66 c7 00 0a 03 00  addr32 movl $0x30a,(%eax)
 94  225: 00 
 95  226: 67 66 c7 44 24 04 18  addr32 movl $0x318,0x4(%esp)
 96  22d: 03 00 00 
 97  230: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
 98  235: 67 66 89 04 24        addr32 mov %eax,(%esp)
 99  23a: 66 e8 74 00 00 00     calll  2b4 <_ZN2Io5printEPKc>
100  240: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
101  245: 67 66 8b 00           addr32 mov (%eax),%eax
102  249: 67 66 89 44 24 04     addr32 mov %eax,0x4(%esp)
103  24f: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
104  254: 67 66 89 04 24        addr32 mov %eax,(%esp)
105  259: 66 e8 55 00 00 00     calll  2b4 <_ZN2Io5printEPKc>
106  25f: 66 c9                 leavel 
107  261: 66 c3                 retl   
108  263: 90                    nop
109 
110 00000264 <_ZN2IoD2Ev>:
111  264: 66 55                 push   %ebp
112  266: 66 89 e5              mov    %esp,%ebp
113  269: 66 83 ec 18           sub    $0x18,%esp
114  26d: 67 66 c7 44 24 04 1f  addr32 movl $0x31f,0x4(%esp)
115  274: 03 00 00 
116  277: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
117  27c: 67 66 89 04 24        addr32 mov %eax,(%esp)
118  281: 66 e8 2d 00 00 00     calll  2b4 <_ZN2Io5printEPKc>
119  287: 66 c9                 leavel 
120  289: 66 c3                 retl   
121  28b: 90                    nop
122 
123 0000028c <_ZN2IoD1Ev>:
124  28c: 66 55                 push   %ebp
125  28e: 66 89 e5              mov    %esp,%ebp
126  291: 66 83 ec 18           sub    $0x18,%esp
127  295: 67 66 c7 44 24 04 1f  addr32 movl $0x31f,0x4(%esp)
128  29c: 03 00 00 
129  29f: 67 66 8b 45 08        addr32 mov 0x8(%ebp),%eax
130  2a4: 67 66 89 04 24        addr32 mov %eax,(%esp)
131  2a9: 66 e8 05 00 00 00     calll  2b4 <_ZN2Io5printEPKc>
132  2af: 66 c9                 leavel 
133  2b1: 66 c3                 retl   
134  2b3: 90                    nop
135 
136 000002b4 <_ZN2Io5printEPKc>:
137  2b4: 66 55                 push   %ebp
138  2b6: 66 89 e5              mov    %esp,%ebp
139  2b9: 66 53                 push   %ebx
140  2bb: eb 22                 jmp    2df <_ZN2Io5printEPKc+0x2b>
141  2bd: 67 66 8b 45 0c        addr32 mov 0xc(%ebp),%eax
142  2c2: 67 66 0f b6 00        addr32 movzbl (%eax),%eax
143  2c7: 66 0f be c0           movsbl %al,%eax
144  2cb: 80 cc 0e              or     $0xe,%ah
145  2ce: 66 ba 07 00 00 00     mov    $0x7,%edx
146  2d4: 66 89 d3              mov    %edx,%ebx
147  2d7: cd 10                 int    $0x10
148  2d9: 67 66 83 45 0c 01     addr32 addl $0x1,0xc(%ebp)
149  2df: 67 66 8b 45 0c        addr32 mov 0xc(%ebp),%eax
150  2e4: 67 66 0f b6 00        addr32 movzbl (%eax),%eax
151  2e9: 84 c0                 test   %al,%al
152  2eb: 0f 95 c0              setne  %al
153  2ee: 84 c0                 test   %al,%al
154  2f0: 75 cb                 jne    2bd <_ZN2Io5printEPKc+0x9>
155  2f2: 66 5b                 pop    %ebx
156  2f4: 66 5d                 pop    %ebp
157  2f6: 66 c3                 retl   

objdump 為我們帶來一些資訊:

 33 00000142 <_Z41__static_initialization_and_destruction_0ii>:
 58 00000196 <_GLOBAL__I_io>:

原來是讓 _GLOBAL__I_io> call 的 (這是 16bit code, 所以使用 -m i8086 會得到比較正確的反組譯結果)。

這是額外產生的程式碼, 當然還有不知道為什麼有兩份的 ctor/dtor。其他的不用看, 那是我拿來佔版面用的。

global_object$ c++filt _Z41__static_initialization_and_destruction_0ii
__static_initialization_and_destruction_0(int, int)

c++filt 可以 demangle 還原本來的 symbol。

_GLOBAL__I_io 用猜的都能感覺出來是 global io 的 ctor, 不過要怎麼證明呢?等我證明之後會有下一篇 (沒有下一篇的話就是我錯了或是還沒證明出來 XD)。

還真的搞錯了, 和我想的不太一樣。ref: c++ runtime - global object ctor (0)

cppb.cpp
 1 __asm__(".code16gcc\n");
 2 #include "io.h"
 3 
 4 /*
 5  * c bootloader
 6  */
 7 
 8 //#define POINTER_TEST
 9 
10 
11 #define BOCHS_MB __asm__ __volatile__("xchg %bx, %bx");
12 
13 
14   Io io;
15 
16 extern "C" void WinMain(void)
17 {
18   BOCHS_MB  
19   #if 1
20   __asm__ ("mov  %cs, %ax\n");
21   __asm__ ("mov  %ax, %ds\n");
22   __asm__ ("mov  %ax, %ss\n");
23   //__asm__ ("mov  $0xfff0, %sp\n");
24   {
25   io.print("hello cpp class\r\n");
26   }
27   #if 0
28   unsigned char *vb = (unsigned char *)0xb8000;
29   *vb = 'A';
30   *(unsigned char *)0xb8001 = 0xc;
31   *(unsigned char *)0xb8002 = 'B';
32   *(unsigned char *)0xb8003 = 0x9;
33   *(unsigned char *)0xb8004 = '@';
34   *(unsigned char *)0xb8005 = 0xc;
35   #endif
36   //while(1);
37   __asm__ ("mov     $0x4c00, %ax\n");
38   __asm__ ("int     $0x21\n"); //   回到 DOS
39 
40   #endif
41 }
42 
43 
44 
45 void *__dso_handle;
46 extern "C"
47 {
48 int __cxa_atexit(void (*destructor) (void *), void *arg, void *__dso_handle)
49 {
50   return 0;
51 }
52 }

-rwxr-xr-x 1 descent descent     556 2012-08-08 13:31 cppb.bin
-rwxr-xr-x 1 descent descent    4979 2012-08-08 13:31 cppb.elf

由於超過 512, 所以我改到 msdos 下執行。

From write_os
顛覆 c++ 程式員的想像, ctor/dtor 完全沒執行 (請對照作業系統之前的程式 (1) - c++ 篇執行畫面)。

從這篇開始, 標題要改成 c++ runtime, 畢竟這是 os 下的程式, 無法再稱為作業系統之前的程式。

這是 g++ 4.4.3 的結果, g++ (Debian 4.7.0-9) 4.7.0 又不一樣了。不要整我啦!
自己對, 我不解說了。

g++ 4.7objdump -m i8086 -d cppb.elf
  1 
  2 cppb.elf:     file format elf32-i386
  3 
  4 
  5 Disassembly of section .text:
  6 
  7 00000100 <WinMain>:
  8  100: 66 55                 push   %ebp
  9  102: 66 89 e5              mov    %esp,%ebp
 10  105: 66 83 ec 28           sub    $0x28,%esp
 11  109: 87 db                 xchg   %bx,%bx
 12  10b: 8c c8                 mov    %cs,%ax
 13  10d: 8e d8                 mov    %ax,%ds
 14  10f: 8e d0                 mov    %ax,%ss
 15  111: 66 a1 e4 02           mov    0x2e4,%eax
 16  115: 67 66 89 45 f4        mov    %eax,-0xc(%ebp)
 17  11a: 66 a1 e4 02           mov    0x2e4,%eax
 18  11e: 67 66 89 45 f0        mov    %eax,-0x10(%ebp)
 19  123: 67 66 8b 45 f4        mov    -0xc(%ebp),%eax
 20  128: 67 66 89 45 ec        mov    %eax,-0x14(%ebp)
 21  12d: 67 66 8b 45 ec        mov    -0x14(%ebp),%eax
 22  132: 66 ff d0              calll  *%eax
 23  135: 67 66 c7 44 24 04 00  movl   $0x1000,0x4(%eax,%eax,1)
 24  13c: 10 00 00 
 25  13f: 67 66 c7 04 24 4c 10  movl   $0x104c,(%eax,%eax,1)
 26  146: 00 00 
 27  148: 66 e8 52 01 00 00     calll  2a0 <_ZN2Io5printEPKc>
 28  14e: b8 00 4c              mov    $0x4c00,%ax
 29  151: cd 21                 int    $0x21
 30  153: 66 c9                 leavel 
 31  155: 66 c3                 retl   
 32 
 33 00000157 <__cxa_atexit>:
 34  157: 66 55                 push   %ebp
 35  159: 66 89 e5              mov    %esp,%ebp
 36  15c: 66 83 ec 18           sub    $0x18,%esp
 37  160: 67 66 c7 44 24 04 12  movl   $0x1012,0x4(%eax,%eax,1)
 38  167: 10 00 00 
 39  16a: 67 66 c7 04 24 4c 10  movl   $0x104c,(%eax,%eax,1)
 40  171: 00 00 
 41  173: 66 e8 27 01 00 00     calll  2a0 <_ZN2Io5printEPKc>
 42  179: 66 b8 00 00 00 00     mov    $0x0,%eax
 43  17f: 66 c9                 leavel 
 44  181: 66 c3                 retl   
 45 
 46 00000183 <__cxa_finalize>:
 47  183: 66 55                 push   %ebp
 48  185: 66 89 e5              mov    %esp,%ebp
 49  188: 66 83 ec 18           sub    $0x18,%esp
 50  18c: 67 66 c7 44 24 04 1d  movl   $0x101d,0x4(%eax,%eax,1)
 51  193: 10 00 00 
 52  196: 67 66 c7 04 24 4c 10  movl   $0x104c,(%eax,%eax,1)
 53  19d: 00 00 
 54  19f: 66 e8 fb 00 00 00     calll  2a0 <_ZN2Io5printEPKc>
 55  1a5: 66 c9                 leavel 
 56  1a7: 66 c3                 retl   
 57 
 58 000001a9 <_Z41__static_initialization_and_destruction_0ii>:
 59  1a9: 66 55                 push   %ebp
 60  1ab: 66 89 e5              mov    %esp,%ebp
 61  1ae: 66 83 ec 18           sub    $0x18,%esp
 62  1b2: 67 66 83 7d 08 01     cmpl   $0x1,0x8(%ebp)
 63  1b8: 75 3d                 jne    1f7 <_Z41__static_initialization_and_destruction_0ii+0x4e>
 64  1ba: 67 66 81 7d 0c ff ff  cmpl   $0xffff,0xc(%ebp)
 65  1c1: 00 00 
 66  1c3: 75 32                 jne    1f7 <_Z41__static_initialization_and_destruction_0ii+0x4e>
 67  1c5: 67 66 c7 04 24 4c 10  movl   $0x104c,(%eax,%eax,1)
 68  1cc: 00 00 
 69  1ce: 66 e8 50 00 00 00     calll  224 <_ZN2IoC1Ev>
 70  1d4: 67 66 c7 44 24 08 50  movl   $0x1050,0x8(%eax,%eax,1)
 71  1db: 10 00 00 
 72  1de: 67 66 c7 44 24 04 4c  movl   $0x104c,0x4(%eax,%eax,1)
 73  1e5: 10 00 00 
 74  1e8: 67 66 c7 04 24 78 02  movl   $0x278,(%eax,%eax,1)
 75  1ef: 00 00 
 76  1f1: 66 e8 60 ff ff ff     calll  157 <__cxa_atexit>
 77  1f7: 66 c9                 leavel 
 78  1f9: 66 c3                 retl   
 79 
 80 000001fb <_GLOBAL__sub_I_io>:
 81  1fb: 66 55                 push   %ebp
 82  1fd: 66 89 e5              mov    %esp,%ebp
 83  200: 66 83 ec 18           sub    $0x18,%esp
 84  204: 67 66 c7 44 24 04 ff  movl   $0xffff,0x4(%eax,%eax,1)
 85  20b: ff 00 00 
 86  20e: 67 66 c7 04 24 01 00  movl   $0x1,(%eax,%eax,1)
 87  215: 00 00 
 88  217: 66 e8 8c ff ff ff     calll  1a9 <_Z41__static_initialization_and_destruction_0ii>
 89  21d: 66 c9                 leavel 
 90  21f: 66 c3                 retl   
 91  221: 00 00                 add    %al,(%bx,%si)
 92  ...
 93 
 94 00000224 <_ZN2IoC1Ev>:
 95  224: 66 55                 push   %ebp
 96  226: 66 89 e5              mov    %esp,%ebp
 97  229: 66 83 ec 18           sub    $0x18,%esp
 98  22d: 67 66 8b 45 08        mov    0x8(%ebp),%eax
 99  232: 67 66 c7 00 2a 10 00  movl   $0x102a,(%eax)
100  239: 00 
101  23a: 67 66 c7 44 24 04 38  movl   $0x1038,0x4(%eax,%eax,1)
102  241: 10 00 00 
103  244: 67 66 8b 45 08        mov    0x8(%ebp),%eax
104  249: 67 66 89 04 24        mov    %eax,(%eax,%eax,1)
105  24e: 66 e8 4c 00 00 00     calll  2a0 <_ZN2Io5printEPKc>
106  254: 67 66 8b 45 08        mov    0x8(%ebp),%eax
107  259: 67 66 8b 00           mov    (%eax),%eax
108  25d: 67 66 89 44 24 04     mov    %eax,0x4(%eax,%eax,1)
109  263: 67 66 8b 45 08        mov    0x8(%ebp),%eax
110  268: 67 66 89 04 24        mov    %eax,(%eax,%eax,1)
111  26d: 66 e8 2d 00 00 00     calll  2a0 <_ZN2Io5printEPKc>
112  273: 66 c9                 leavel 
113  275: 66 c3                 retl   
114  277: 90                    nop
115 
116 00000278 <_ZN2IoD1Ev>:
117  278: 66 55                 push   %ebp
118  27a: 66 89 e5              mov    %esp,%ebp
119  27d: 66 83 ec 18           sub    $0x18,%esp
120  281: 67 66 c7 44 24 04 3f  movl   $0x103f,0x4(%eax,%eax,1)
121  288: 10 00 00 
122  28b: 67 66 8b 45 08        mov    0x8(%ebp),%eax
123  290: 67 66 89 04 24        mov    %eax,(%eax,%eax,1)
124  295: 66 e8 05 00 00 00     calll  2a0 <_ZN2Io5printEPKc>
125  29b: 66 c9                 leavel 
126  29d: 66 c3                 retl   
127  29f: 90                    nop
128 
129 000002a0 <_ZN2Io5printEPKc>:
130  2a0: 66 55                 push   %ebp
131  2a2: 66 89 e5              mov    %esp,%ebp
132  2a5: 66 53                 push   %ebx
133  2a7: eb 22                 jmp    2cb <_ZN2Io5printEPKc+0x2b>
134  2a9: 67 66 8b 45 0c        mov    0xc(%ebp),%eax
135  2ae: 67 66 0f b6 00        movzbl (%eax),%eax
136  2b3: 66 0f be c0           movsbl %al,%eax
137  2b7: 80 cc 0e              or     $0xe,%ah
138  2ba: 66 ba 07 00 00 00     mov    $0x7,%edx
139  2c0: 66 89 d3              mov    %edx,%ebx
140  2c3: cd 10                 int    $0x10
141  2c5: 67 66 83 45 0c 01     addl   $0x1,0xc(%ebp)
142  2cb: 67 66 8b 45 0c        mov    0xc(%ebp),%eax
143  2d0: 67 66 0f b6 00        movzbl (%eax),%eax
144  2d5: 84 c0                 test   %al,%al
145  2d7: 0f 95 c0              setne  %al
146  2da: 84 c0                 test   %al,%al
147  2dc: 75 cb                 jne    2a9 <_ZN2Io5printEPKc+0x9>
148  2de: 66 5b                 pop    %ebx
149  2e0: 66 5d                 pop    %ebp
150  2e2: 66 c3                 retl   

ref: