2012年10月7日 星期日

c++ runtime - local static object (0)

env: g++ 4.7.1

local static object 比起 global object 複雜多了 (因為 global object 要做的事情 local static object 都要做), 我真的還要花心力在這上面嗎?我猶豫了, 很怕這是個無底洞。畢竟我想完成的是 os, 沒有一定要用 c++ 來完成。除了參考 osdev 上的文章之外, 幾乎都靠自己使用 objdump, nm, readelf 這些工具辛苦的查探。看著我不熟悉的組合語言, 這真的很辛苦, 而為了不讓辛苦的查探白費, 我又得更辛苦的紀錄下來。

而說實在的, 除了滿足我自己的求知慾望之外, 似乎對我沒什麼幫助, 我又不會寫 c++ compiler。不過像驢子般的耐力似乎是我的優點之一, 最後我還是決定繼續下去, 又由於我已經搞清楚了, 所以再 blog 一篇。若你願意看這篇文章, 那真是讓我高興, 我的文筆並不那麼優秀, 所以我知道這並不是很好讀。

以下是相關 c++ 程式碼:

cppb.cpp
 1 __asm__(".code16gcc\n");
 2 
 3 #include "io.h"
 4 #include "obj.h"
 5 
 6 typedef signed char s8;
 7 typedef signed short s16;
 8 typedef signed int s32;
 9 
10 typedef unsigned char u8;
11 typedef unsigned short u16;
12 typedef unsigned int u32;
13 
14 #define BOCHS_MB __asm__ __volatile__("xchg %bx, %bx");
15 
16 void s16_print_int(int i, int radix);
17 void print_str(const char   *s);
18 
19 //Io io;
20 
21 extern "C" int cpp_main(void)
22 {
23   BOCHS_MB
24   static Io io;
25 
26   return 0;
27 }

cpp_abi.cpp
 1 #include "cpp_abi.h"
 2 
 3 void *__dso_handle;
 4 
 5 static DObjs dobjs[DOBJS_SIZE];
 6 int obj_count=0;
 7 
 8 void print_str(const char   *s);
 9 void s16_print_int(int i, int radix);
10 
11 extern "C"
12 {
13 
14   int __cxa_atexit(void (*destructor) (void *), void *arg, void *__dso_handle)
15   {
16     __asm__ __volatile__("xchg %bx, %bx");
17     print_str("atexit\r\n");
18     s16_print_int(obj_count, 10);
19     print_str("\r\n");
20 
21 #if 0
22     dobjs[0].dtor_ = destructor;
23     dobjs[0].arg_ = arg;
24     dobjs[0].dso_handle_ = __dso_handle;
25 #else
26     //if (obj_count >= DOBJS_SIZE)
27       //return -1;
28      #if 1
29     dobjs[obj_count].dtor_ = destructor;
30     dobjs[obj_count].arg_ = arg;
31     dobjs[obj_count].dso_handle_ = __dso_handle;
32     #endif
33     ++obj_count;
34     //print_str("__cxa_atexit: register dtor here\r\n");
35 #endif
36     return 0;
37   }
38   void g_dtors(void)
39   {
40     print_str("g_dtors\r\n");
41     s16_print_int(obj_count, 10);
42     print_str("\r\n");
43     //print_str("g_dtors\r\n");
44     #if 1
45     for (int i=obj_count-1 ; i >= 0 ; --i)
46     {
47       //print_str("xx g_dtors\r\n");
48       //dobjs[i].dtor_(dobjs[i].arg_);
49       dobjs[i].dtor_(0);
50     }
51     #endif
52   }
53 }
54 
55 
56 int __cxa_guard_acquire()
57 {
58   return 1;
59 }
60 
61 int __cxa_guard_release()
62 {
63   return 1;
64 }

static object 除了和 global object 一樣要自己補上

int __cxa_atexit(void (*destructor) (void *), void *arg, void *__dso_handle)
__dso_handle

還需要補上

int __cxa_guard_acquire()
int __cxa_guard_release()

ref: cpp_abi.cpp L56 ~ L64, function prototype 我是亂寫的 (其實是錯的), 不過這不是這篇的重點, 化繁為簡才容易搞懂。

objdump
 1 000001cc <cpp_main>:
 2  1cc:   66 55                   push   %ebp
 3  1ce:   66 89 e5                mov    %esp,%ebp
 4  1d1:   66 83 ec 18             sub    $0x18,%esp
 5  1d5:   87 db                   xchg   %bx,%bx
 6  1d7:   66 b8 c0 07 00 00       mov    $0x7c0,%eax
 7  1dd:   67 66 0f b6 00          movzbl (%eax),%eax
 8  1e2:   84 c0                   test   %al,%al
 9  1e4:   75 5a                   jne    240 <cpp_main+0x74>
10  1e6:   67 66 c7 04 24 c0 07    movl   $0x7c0,(%eax,%eax,1)
11  1ed:   00 00 
L11 在 bochs 反組譯是這樣 $0x000007c0, %ss:(%esp) 比較正確, 傳個參數給 __cxa_guard_acquire
12  1ef:   66 e8 c3 02 00 00       calll  4b8 <__cxa_guard_acquire>
13  1f5:   66 85 c0                test   %eax,%eax
14  1f8:   0f 95 c0                setne  %al
15  1fb:   84 c0                   test   %al,%al
16  1fd:   74 41                   je     240 <cpp_main+0x74>
17  1ff:   67 66 c7 04 24 c8 07    movl   $0x7c8,(%eax,%eax,1)
18  206:   00 00 
19  208:   66 e8 3e 00 00 00       calll  24c <_ZN2IoC1Ev>
20  20e:   67 66 c7 04 24 c0 07    movl   $0x7c0,(%eax,%eax,1)
21  215:   00 00 
22  217:   66 e8 aa 02 00 00       calll  4c7 <__cxa_guard_release>
23  21d:   67 66 c7 44 24 08 e0    movl   $0x7e0,0x8(%eax,%eax,1)
24  224:   07 00 00 
25  227:   67 66 c7 44 24 04 c8    movl   $0x7c8,0x4(%eax,%eax,1)
26  22e:   07 00 00 
27  231:   67 66 c7 04 24 a0 02    movl   $0x2a0,(%eax,%eax,1)
28  238:   00 00 
29  23a:   66 e8 24 01 00 00       calll  364 <__cxa_atexit>
30  240:   66 b8 00 00 00 00       mov    $0x0,%eax
31  246:   66 c9                   leavel 
32  248:   66 c3                   retl   

12  1ef:   66 e8 c3 02 00 00       calll  4b8 <__cxa_guard_acquire>
22  217:   66 e8 aa 02 00 00       calll  4c7 <__cxa_guard_release>

這便是我們補上的那兩個 function。只要宣告了 local static object, g++ 便會為我們生成產生調用呼叫這兩個 function 的程式碼。哇!真是邪惡, 我們應當習慣這件事情了。

12  1ef:   66 e8 c3 02 00 00       calll  4b8 <__cxa_guard_acquire>
13  1f5:   66 85 c0                test   %eax,%eax
14  1f8:   0f 95 c0                setne  %al
15  1fb:   84 c0                   test   %al,%al
16  1fd:   74 41                   je     240 <cpp_main+0x74>
17  1ff:   67 66 c7 04 24 c8 07    movl   $0x7c8,(%eax,%eax,1)
18  206:   00 00 
19  208:   66 e8 3e 00 00 00       calll  24c <_ZN2IoC1Ev> 

從這裡可以看出會判斷 int __cxa_guard_acquire() 的傳回值 (compare %eax, function 的傳回值會紀錄在 %eax, L13 ~ 16), 所以要 return 1 才會 call L19 的 Io::Io(), 我一開始 return 0, 結果 Io::Io() 一直沒被呼叫, 看了反組譯的組合語言之後才理解。

我不貼執行結果了, 如果你跟著我的一系列文章到這裡, 應該可以自己完成測試, 如果不行, 我相信你應該會問我如何做這樣的測試, 我很樂意回答這樣的問題。

static object 這樣就結束了嗎? 當然沒這麼簡單, 這只是能讓 g++ 編譯器編譯過而已, 行為不是正確的, 還會有《c++ runtime - static object (1)》。

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。