使BFD库找到类成员函数

时间:2016-05-04 18:58:52

标签: c++ function-pointers member-function-pointers bfd

我正在使用函数bfd_find_nearest_line来查找函数的源位置(来自带有调试符号的可执行文件 - 使用-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??, 
                ...)

类成员函数(在这种情况下,如果类型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库是否能够找到原始{{1}的源位置通过这个指针。 (目前至少我对虚函数不感兴趣。)

换句话说,

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

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

我通过其他方式(堆栈跟踪)知道指针存在,例如我可以得到在这种情况下void*调用自由函数,但问题是如何从{{1 }}

2 个答案:

答案 0 :(得分:0)

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

好消息: 可能。

坏消息:这不是直截了当的。

您需要c++filt实用程序。

并且,有些方法可以读取可执行文件的符号表,例如readelf。如果您可以通过bfd_*调用枚举[损坏的]符号,则可以为自己节省一步。

此外,还有 biggie :您需要在文本字符串中使用符号的c ++名称。因此,对于&(A::my_member_function),您需要采用以下形式:"A::my_member_function()"这不应该太困难,因为我认为您关注的数量有限。< / p>

您需要从readelf -s <executable>获取符号及其地址列表。准备解析此输出。您需要从字符串中解码十六进制地址以获取其二进制值。

这些将是受损的名字。对于每个符号,执行c++filt -n mangled_name并将输出(即管道)捕获到某些内容(例如nice_name)。它将返回解码后的名称(即您喜欢的漂亮的c ++名称)。

现在,如果nice_name匹配&#34; A:my_member_function()&#34;,您现在有匹配,您已经有了错位的名称,但更重要的是,符号的十六进制地址。将此十六进制值[适当强制转换]提供给填充functionname_ptr

的bfd

注意:上述工作有效,但重复调用c++filt

可能会很慢

更快的方法是捕获管道输出:

readelf -s <executable> | c++filt

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

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

答案 1 :(得分: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};

给出了编译错误:&#34;你不能获取析构函数的地址&#34;。

对于构造函数,我甚至无法找到正确的语法,因为这会因语法错误而失败。

void_caster_t void_caster = {&A::A};

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