2016-05-04 26 views
0

我正在使用函數bfd_find_nearest_line來查找函數的源位置(從帶有調試符號的可執行文件 - 編譯爲-g)。自然的論據之一是函數指針我想定位:使BFD庫找到類成員函數的位置

boolean 
_bfd_elf_find_nearest_line (abfd, 
       section, 
       symbols, 
       offset, 
       filename_ptr, 
       functionname_ptr, // <- HERE! 
       line_ptr) 

https://sourceware.org/ml/binutils/2000-08/msg00248.html

後相當多的(純C)鍋爐板,我管理這個有正常功能的工作(其中正常功能指針被輸出到*void)。

例如,此工作原理:

int my_function(){return 5;} 

int main(){ 
    _bfd_elf_find_nearest_line (..., 
       (void*)(&my_function), 
       ...); 
} 

的問題是,如果bfd_find_nearest_line可以用來定位一個類的成員函數的源代碼。

struct A{ 
    int my_member_function(){return 5.;} 
}; 

_bfd_elf_find_nearest_line (..., 
       what_should_I_put_here??, 
       ...) 

類的成員函數(在這種情況下,如果類型int (A::*)())不是函數,一個特別不能轉換到的任何函數的指針,甚至不void*。看到這裏:https://isocpp.org/wiki/faq/pointers-to-members#cant-cvt-memfnptr-to-voidptr

我完全理解這個背後的邏輯,成員函數指針是如何從中獲得成員函數信息的唯一句柄,以便使BFD識別函數。我不希望這個指針調用一個函數。

我知道更多或更少的C++如何工作的,編譯器會悄悄地產生相當的自由-C功能,

__A_my_member_function(A* this){...} 

但我不知道如何訪問這個免費的功能,或者如果該地址甚至可能,以及bfd庫是否能夠通過該指針定位原始的my_member_function的源位置。 (就目前而言,至少我在虛函數不感興趣。)

換句話說,

1)我需要知道,如果bfd將能夠找到一個成員函數,

2 ),並且如果它可以將int (A::*)()類型的成員函數指針映射到bfd可以採用的參數(void*)。


我知道通過其他方式(堆棧跟蹤)指針存在,比如我可以得到免費的函數被調用在這種情況下_ZN1A18my_member_functionEv,但問題是我如何從&(A::my_member_function)得到這個。

+0

謝謝,我試圖避免外部工具。所有的外部工具都使用'bfd'後。 – alfC

回答

0

好吧,我找到了一種方法。首先,我發現bfd是非常開心的檢測成員函數從成員指針調試信息,只要指針可以轉換爲void*即可。

我正在使用clang,它不允許我將成員函數指針轉換爲任何類型的指針或整數。 GCC允許這樣做,但會發出警告。 甚至有一個標誌允許指向名爲-Wno-pmf-conversions的成員演員指針。

考慮到我做了我最好的一個成員函數指針轉換成void*和我結束了做這個工會使用這些信息。

struct A{ 
    int my_member_function(){return 5.;} 
}; 

union void_caster_t{ 
    int (A::*p)(void) value; 
    void* casted_value; 
}; 
void_caster_t void_caster = {&A::my_member_function}; 

_bfd_elf_find_nearest_line (..., 
       void_caster.casted_value, 
       ...) 

最後bfd能夠給我一個成員函數的調試信息。


我沒弄明白是什麼呢,是如何得到的指針構造函數和析構函數的成員函數。

例如

void_caster_t void_caster = {&A::~A}; 

會產生編譯錯誤:「你不能把析構函數的地址」。

對於構造函數,我甚至無法找到正確的語法,因爲這會失敗,因爲語法錯誤。

void_caster_t void_caster = {&A::A}; 

同樣都未能背後的邏輯涉及無意義的回調,但這是不同的,因爲我要的指針(或地址)來獲取調試信息,而不是回調。

0

好的,有好消息和壞消息。

好消息:它可能。壞消息:這並不是直截了當的。

您將需要c++filt實用程序。

而且,可以通過某種方式讀取可執行文件的符號表,如readelf。如果您可以用bfd_*調用枚舉[mangled]符號,則可以爲自己節省一個步驟。

而且,這裏是一個大問題:你會在文本字符串需要你的符號的C++名稱。因此,對於&(A::my_member_function),您需要使用以下格式:"A::my_member_function()"這應該不會太困難,因爲我認爲您的數量有限。

您需要從readelf -s <executable>獲取符號及其地址列表。準備分析這個輸出。您需要解碼字符串中的十六進制地址以獲取其二進制值。

這些將是重名的名字。對於每個符號,執行c++filt -n mangled_name並將輸出(即管道)捕獲到某些內容中(例如nice_name)。它會給你返回demangled的名字(也就是你喜歡的漂亮的C++名字)。

現在,如果nice_name與「A:my_member_function()」匹配,您現在有一個匹配項,您已經有了錯位的名稱,但更重要的是符號的十六進制地址。訂閱這個十六進制值適當投]到BFD你在哪裏餡functionname_ptr


注:上述作品,但可以用c++filt

一個更快的方式重複調用慢是要做到這一點捕捉到的管道輸出:

readelf -s <executable> | c++filt 

它還[可能]容易做這種方式,因爲你只需要解析濾波輸出,並尋找匹配好聽的名字。另外,如果您有多個您關心的符號,您可以在一次調用中獲得所有地址。

+0

謝謝,在嘗試不使用外部工具後,所有這些外部工具都將使用'bfd'庫。看我的解決方案。看起來不可能做的是取出構造函數或析構函數的地址。對於你的解決方案似乎使它成爲可能。 – alfC