2016-04-21 41 views
2

我正在調試一個有害的內存損壞錯誤,它看起來像一個指向vtable的指針正在被破壞。我想通過比較指針的值和指針的正確值(vtable的真實位置)在我的程序中的不同位置來檢測它何時發生。有什麼直接的方法可以在編譯時獲得一個vtable的地址嗎?

不幸的是,我還沒有找到一種方法來在編譯時獲得指針的正確值,所以我不得不實例化一個新的對象,只是爲了讀取它的指針vtable,這似乎是不必要的黑客行爲。

#include <iostream> 
#include <iomanip> 
#include <stdint.h> 

class Foo { 
    virtual void performVirtualAction() { 
     std::cout << "Foo's version" << std::endl; 
    } 
}; 
class Bar : public Foo { 
    virtual void performVirtualAction() { 
     std::cout << "Bar's version" << std::endl; 
    } 
}; 

int main(){ 
    Foo foo; 
    Bar bar; 

    std::cout << "Foo's vtable pointer " << std::hex << *(uint64_t*)(&foo) << std::endl; 
    std::cout << "Bar's vtable pointer " << std::hex << *(uint64_t*)(&bar) << std::endl; 
} 

是否有表達我想要的vtable的特定類型像FooBar位置的想法更直接的方式?

該方法不必是可移植的。它只適用於Linux上的gcc

+2

我想在嘗試這種類型的黑客之前,你已經跑過'valgrind','asan','ubsan'嗎? –

+0

我正在調試分佈式系統的部分。當我連接valgrind時,它改變了時間,所以我無法重現這個問題。我還沒有看到它聽到其他兩個,但會看看他們。 – merlin2011

+0

我會有興趣知道問題是否真的是vtable腐敗。無論如何,大多數情況下,儘管依賴於編譯器,但vtable位於對象的開始位置 - 例如,這些對象是作爲示例使用的。 (例如,當它不在開始時:當基類沒有虛擬化時)。無論如何,你是否有權訪問一個堆,在調試模式下,可以通過在堆分配前後分配警戒來檢測超限? (MS VC++有這種功能。)它不應該干擾很多時間(假設你不在機器RAM容量附近)。 – davidbak

回答

2

C++沒有定義如何實現多態性。 vtables只是事實上的標準,但如果任何編譯器供應商找到適當的不同方法,他可以自由地做任何他想做的事情。

因此,無論您嘗試以編程方式進行操作,它始終會保持一種骯髒的黑客行爲,因爲vtable是一種內部編譯器細節,不直接對C++代碼可見。

[編輯:需要糾正自己:gcc在多態類的開頭放置一個指向vtable右邊的指針。從此之後:簡單地將您的對象投射到uintptr_t(而不是uint64_t)(或指向uintptr_t*),您將直接獲得指向vtable的指針。如果你還想打印虛擬表本身(看看這個虛擬表是否損壞),這可能會有所幫助:What is the VTable Layout and VTable Pointer Location in C++ Objects in GCC 3.x and 4.x?]

其實,我不指望你會發現很多,因爲你可能會看到更改實際發生之後很長一段時間。如果您知道或可以找出哪些對象實例受到影響,則可以在gdb的寫訪問(https://sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html)上添加內存觀察點。這可以準確地揭示你的vtable何時和在哪裏被破壞(例如,如果你寫的超出了數組定義的邊界,將你的對象轉換爲錯誤的類型並寫入它,...)。

+0

請參閱有關可移植性問題底部的註釋。我知道vtable是一個實現細節。 – merlin2011

+1

我知道 - 關鍵是 - 由於是一個實現細節,你不會得到一個很好的C++方式來訪問它... – Aconcagua

相關問題