0

我正在使用该函数bfd_find_nearest_line来查找函数的源位置(来自带有调试符号的可执行文件 --compiled with -g)。自然,其中一个参数是指向我要定位的函数的指针:

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??, 
                ...)

类成员函数(在这种情况下如果 type int (A::*)())不是函数,尤其不能转换为任何函数指针,甚至不能转换为void*. 见这里:https ://isocpp.org/wiki/faq/pointers-to-members#cant-cvt-memfnptr-to-voidptr

我完全理解这背后的逻辑,但成员函数指针是我获得成员函数信息的唯一句柄,以便让 BFD 识别函数。我不希望这个指针调用一个函数。

我或多或少知道 C++ 是如何工作的,编译器会默默地生成一个等效的 free-C 函数,

__A_my_member_function(A* this){...}

但我不知道如何访问这个自由函数的地址,或者是否有可能,以及bfd库是否能够my_member_function通过这个指针定位原始的源位置。(至少目前我对虚函数不感兴趣。)

换句话说,

1)我需要知道是否bfd能够找到成员函数,

2)如果可以,我如何将类型的成员函数指针映射int (A::*)()bfd可以采用(void*)的参数。


我通过其他方式(堆栈跟踪)知道指针存在,例如我可以得到在这种情况下调用了 free 函数_ZN1A18my_member_functionEv,但问题是我如何从&(A::my_member_function).

4

2 回答 2

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)中。它会给您返回去错的名称(即您想要的漂亮的 c++ 名称)。

现在,如果nice_name匹配“A:my_member_function()”,你现在有了一个匹配,你已经有了损坏的名称,但更重要的是,符号的十六进制地址。将此十六进制值 [适当地转换] 提供给您填充的 ​​bfdfunctionname_ptr


注意:上述工作,但重复调用可能会很慢c++filt

一种更快的方法是捕获以下管道输出:

readelf -s <executable> | c++filt

这样做也 [可能] 更容易,因为您只需要解析过滤后的输出并查找匹配的好名称。

此外,如果您有多个您关心的符号,您可以在一次调用中获取所有地址。

于 2016-05-05T02:38:47.467 回答
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};

再次,所有无法背后的逻辑都涉及无意义的回调,但这是不同的,因为我希望指针(或地址)获取调试信息,而不是回调。

于 2016-05-05T19:08:59.780 回答