2011-10-08 52 views
11

pimpl idiom通常用於允許更改動態鏈接庫中的代碼而不破壞ABI兼容性,並且必須重新編譯依賴於庫的所有代碼。添加私有成員變量如何破壞C++ ABI兼容性?

大部分explanations我看到提到添加一個新的私有成員變量會改變班級中公共和私人成員的偏移量。這對我來說很有意義。我不明白的是,實際上這是如何打破依賴庫的。

我已經做了很多關於ELF文件的閱讀以及動態鏈接是如何實際工作的,但是我仍然沒有看到如何改變共享庫中的類大小會破壞事情。

E.g.下面是測試應用(a.out的)我寫從測試共享庫(libInterface.so)使用代碼(Interface::some_method):

[email protected]:~/pimpl$ objdump -d -j .text a.out 
08048874 <main>: 
... 
8048891: e8 b2 fe ff ff   call 8048748 <[email protected]> 

some_method呼叫使用程序鏈接表(PLT):

[email protected]:~/pimpl$ objdump -d -j .plt a.out 

08048748 <[email protected]>: 
8048748: ff 25 1c a0 04 08  jmp *0x804a01c 
804874e: 68 38 00 00 00   push $0x38 
8048753: e9 70 ff ff ff   jmp 80486c8 <_init+0x30> 

隨後前進到全局偏移表(GOT),其中地址0x804a01c包含:

[email protected]:~/pimpl$ readelf -x 24 a.out 

Hex dump of section '.got.plt': 
    0x08049ff4 089f0408 00000000 00000000 de860408 ................ 
    0x0804a004 ee860408 fe860408 0e870408 1e870408 ................ 
    0x0804a014 2e870408 3e870408 4e870408 5e870408 ....>...N...^... 
    0x0804a024 6e870408 7e870408 8e870408 9e870408 n...~........... 
    0x0804a034 ae870408       .... 

然後這是,動態連接器WO通過LD_LIBRARY_PATH中的共享庫中包含的所有符號查找它,並在libInterface.so中找到Interface::some_method並將其代碼加載到GOT中,因此在隨後調用some_method時,GOT中的代碼實際上是來自共享的代碼段圖書館。

或沿着這些線。

但是鑑於上述情況,我仍然不明白共享庫的類大小或其方法偏移量如何在這裏發揮作用。據我所知,上述步驟對班級人數不可知。它看起來像庫中方法的名稱名稱包含在a.out中。當鏈接器將代碼加載到GOT中時,任何類大小變化都應該在運行時解決,否?

我在這裏錯過了什麼?

回答

15

主要的問題是,當你分配一個類的新實例(無論是在堆棧上,還是通過new),調用代碼都需要知道對象的大小。如果稍後更改對象的大小(通過添加私有成員),這會增加所需的大小;不過,您的來電者仍在使用舊尺寸。所以你最終沒有分配足夠的空間來容納對象,然後對象的構造函數繼續破壞堆棧(或堆),因爲它假定它有足夠的空間。

此外,如果您有任何內聯成員函數,它們的代碼(包括成員變量的偏移量)可以內聯到調用代碼中。如果你添加私人成員而不是結束,那麼這些偏移將是不正確的,這也會導致內存損壞(注意:即使你添加到最後,大小不匹配仍然是一個問題)。

+0

啊哈!得到它了。事實上,在調用Interface的ctor之前,在反彙編中稍微看一下,我可以看到它爲該對象分配了空間(在本例中爲4字節)。 – adg