test environment:
linux 32bit (ubuntu 64 bit chroot debian 32 bit)
g++ 4.7.1
在
c++ runtime - static object (0) 那篇後, 我們可以 compile local static object, 接著用以下的程式碼來測試 local static object。
來看看結果:
git commit: 68191b2311d900492397c2456ea0b0fbd9b66e8f
Io::Io() 畫面上的
Io cotr: 字串竟然被喚起了兩次, dtor 也是, 這不就和 local object 一樣了嗎?和 c++ 定義的 static object 只能有一次的初始化動作 (喚起一次 ctor) 似乎相違背。一定是有哪裡搞錯了, 我們合理懷疑是 __cxa_guard_acquire(), __cxa_guard_release() 這兩個 function 寫錯了。
原本的寫法:
56 int __cxa_guard_acquire()
57 {
58 return 1;
59 }
60
61 int __cxa_guard_release()
62 {
63 return 1;
64 }
改成這樣:
結果就正常了。
Io:Io() 只會被喚起一次,這是什麼樣的魔法呢?組合語言面前沒有秘密, 一如往常, 我們來看看反組譯的結果, 以下是兩份反組譯的程式碼, 對照著看幫助理解, 一份是 g++ -S 產生, 一份是 objdump 產生。
看 void test_static_obj() 的部份就可以了。
開始苦工了 ...
參考 cppb.s L50 ~ L94, L71, L79 分別傳 $_ZGVZ15test_static_objvE2io 給 __cxa_guard_acquire, __cxa_guard_release。
只執行一次 ctor 的秘密在 L85 .L5, 什麼時候會跳到 .L5?
69 jne .L5
75 je .L5
77 call _ZN2IoC1Ev
是呼叫 ctor,而 L77 要只執行一次。所以看完這段組合語言之後, 就知道要怎麼填入 __cxa_guard_acquire, __cxa_guard_release 的內容了。當然我沒那麼強, 我是看
http://wiki.osdev.org/C%2B%2B 這篇文章再反推回來的。
66 movl $_ZGVZ15test_static_objvE2io, %eax
67 movzbl (%eax), %eax
68 testb %al, %al
69 jne .L5
這是第一關, $_ZGVZ15test_static_objvE2io 指的位址必須是 0 才行, 有這麼剛好的事情嗎?當然沒有, 這是經過某種環節產生的, 在我的平台上, $_ZGVZ15test_static_objvE2io 位於 bss section, 而我的 bss init function 會把 bss section 都初始為 0, 所以過了第一關。
69 jne .L5
70 movl $_ZGVZ15test_static_objvE2io, (%esp)
71 call __cxa_guard_acquire
72 testl %eax, %eax
73 setne %al
74 testb %al, %al
75 je .L5
現在程式可以來到 __cxa_guard_acquire, 因為 $_ZGVZ15test_static_objvE2io 指的位址是 0, 所以 __cxa_guard_acquire 回傳 1 (%eax = 1), 而 L75 就不會跳到 .L5, L76, 77 就執行 ctor。
我在 bochs 反組譯看到的是 jz 而不是 je。
78 movl $_ZGVZ15test_static_objvE2io, (%esp)
79 call __cxa_guard_release
呼叫 __cxa_guard_release 把 $_ZGVZ15test_static_objvE2io 指的位址設為 1, 這就是 local static object 只會執行一次 ctor 的關鍵, 回憶一下 L66 ~ L69, 等到第二次呼叫 void test_static_obj(), 這時候 $_ZGVZ15test_static_objvE2io 指的位址設為 1, 直接跳到 .L5, 不會再執行 __cxa_guard_acquire(), __cxa_guard_release()。
寫的很亂, 這不太好描述, 又參雜了兩份不同的反組譯 code, 不過概念就是這樣, 要真的理解, 得仔細的看這些組合語言程式碼, 我花了不少時間看, 所以有了這篇文章 (事實上我做的事情比文章上還多), 但要真能理解, 也必須要做一樣的苦工。
看不懂其實沒有關係 (這篇大概是這系列以來寫的最亂的), 這不能說是很重要的知識, 只要知道怎麼填 __cxa_guard_acquire, __cxa_guard_release 這兩個 function, 就足以完成 local static object 的動作。
source code:
git@github.com:descent/simple_os.git
git checkout origin/cpp_static_obj -b cpp_static_obj
git commit: e80d208503a883a37020ed41e96b721dad1103fe
git checkout e80d208503a883a37020ed41e96b721dad1103fe
cd cpp_runtime/global_object/dos_cpp
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。