1st edit: 2012/09/17
env:
g++ 4.7.1
git commit: 6d785159984cd0587afdff69b05d41a3634777f9
(path: simple_os/cpp_runtime/global_object/dos_cpp)
研究
c++ runtime 之後, 我才了解 c++ compiler 的邪惡, 它為了我們的程式碼插了很多額外的 code, 令人又愛又恨。在大部份狀況下, 我們不用管這些事情, 搞懂這些東西對我來說純粹是個機緣。有點中毒, 愈挖愈深。
建議先看這篇:
C++ 虚函数表解析
這次的東西很麻煩, 我花了好些功夫才從反組譯中搞懂。
Io::fun1(), DerivIo::fun1() 是 virtual function。根據 c++ 書籍提到的, 會有一個 virtual table 簡稱 vtable。我們來找看看它在哪裡。
virtual function table 在 compile time 就決定好了, 在 cppb.bin L145, L150。
186 348: 67 66 c7 00 50 0a 00 movl $0xa50,(%eax) # virtual function table address
610 950: 67 66 c7 00 00 0a 00 movl $0xa00,(%eax) # virtual function table address
在 Io::Io(), DeriveIo::DeriveIo()
會設定這個 vtable 的位址, constructor 似乎比我們從 c++ code 看到的還要多一些東西, 你的 c++ code 又不知不覺加大了。
0xa50, 0xa00 分別是 io, d_io 的 vtable 位址, 查看 cppb.bin offset 0x900, 0x950 可以看到, 有點亂, 還是一個一個來好了 ...
local object io: 0xa50 (vtable address) 對應到 cppb.bin offset 0x950, 為什麼差了 0x100, 因為這是一個在 dos 下執行的 com 執行檔, 會被載入到 %cs:0x100 的地方, 所以 0x950 被載入到 ram 時, 就會在 0xa50。為什麼 0x950 我敢說它是 vtable 的位址呢?仔細看前 4 byte 是 86090000 -> 00000986 (little endian),
632 00000986 <_ZN2Io4fun1Ev>:
搭配 objdump 可以看到這是 io::fun1() 的位址, 看和陈皓的文章有點不同的是, 我看不到結束符號 (所以我有可能搞錯了, 如果我搞錯, 請告知我)。
同樣方式:
local object d_io vtable address: 0xa00, 查看 cppb.bin 0x900 的前 4 個 byte:
0a090000 -> 0000090a ()
588 0000090a <_ZN8DeriveIo4fun1Ev>:
constructor 有一部份的程式碼就是在初始化 vtable。
186 348: 67 66 c7 00 50 0a 00 movl $0xa50,(%eax) # virtual function table address
610 950: 67 66 c7 00 00 0a 00 movl $0xa00,(%eax) # virtual function table address
把 vtable address 複製到 d_io 的位址上, 後續才會是其他 member data 的部份, 呼應了陈皓的文章。
再來就是看看如何使用 base class pointer 指向 derive class 來喚起 derive class virtual function。
138 28b: 67 66 8b 45 f4 addr32 mov -0xc(%ebp),%eax # d_io
139 290: 67 66 8b 00 addr32 mov (%eax),%eax
140 294: 67 66 8b 10 addr32 mov (%eax),%edx
141 298: 67 66 8b 45 f4 addr32 mov -0xc(%ebp),%eax
142 29d: 67 66 89 04 24 addr32 mov %eax,(%esp)
143 2a2: 66 ff d2 calll *%edx
d_io 的位址的開始 4 byte 就是 vtable address, L141 則是 this pointer, 這段 code 就是將 vtable address 的第一個 function 取出來執行。請細細品嘗, 組合語言得要你自己看懂才行, 我該說明的已經都說明完了。
所以也知道了 virtual function 為什麼會慢了, 在程式上不是對應一個 call 指令, 而是需要存取 vtable, 再找出該 virtual function 位址, 然後才是 call 指令。
objdump 的反組譯程式, 我加上部份註解, 相信可以幫助你理解, 很難懂嗎?我當時可花了好幾天才看懂, 又花了好幾天才有這篇文章, 若要能馬上看懂可能有點困難, 希望可以降低你的理解時間。
知道這能幹嘛, 說實在, 沒什麼用處, 只要知道 c++ 書上寫的東西, 就可以使用 virtual function, 所以看不懂也沒關係, 我自己只是純粹「想知道」而已。
ref:
http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/
沒有留言:
張貼留言
使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。
我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。