使 BFD 库找到 class 成员函数的位置
Make BFD library find the location of a class member function
我正在使用函数 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
是否可以用来定位 class 成员函数的源代码。
struct A{
int my_member_function(){return 5.;}
};
_bfd_elf_find_nearest_line (...,
what_should_I_put_here??,
...)
Class 成员函数(在这种情况下,如果类型为 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
库是否能够找到原始 [=22] 的源位置=] 通过这个指针。
(至少目前我对虚函数不感兴趣。)
换句话说,
1) 我需要知道 bfd
是否能够定位成员函数,
2) 如果可以的话,我如何将类型 int (A::*)()
的成员函数指针映射到 bfd
可以接受的参数 (void*
).
我通过其他方式(堆栈跟踪)知道指针存在,例如我可以得到在这种情况下调用了自由函数 _ZN1A18my_member_functionEv
,但问题是我如何从 &(A::my_member_function)
.
好的,有好消息也有坏消息。
好消息:是可能的。
坏消息:这并不简单。
您需要 c++filt
实用程序。
还有一些方法可以读取您的 executable 的符号 table,例如 readelf
。如果您可以通过 bfd_*
调用枚举 [损坏的] 符号,您也许可以节省一步。
此外,这里有一个 大问题 :您需要在 文本字符串 中提供符号的 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
这样做 [可能] 更容易,因为您只需要解析过滤后的输出并寻找匹配的好听的名字。
此外,如果您有多个您关心的符号,您可以在一次调用中获取所有地址。
好的,我找到办法了。首先,我发现 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};
给出编译器错误:"you can't take the address of the destructor"。
对于构造函数,我什至找不到正确的语法,因为它作为语法错误而失败。
void_caster_t void_caster = {&A::A};
同样,无法实现的所有逻辑都涉及无意义的回调,但这是不同的,因为我希望指针(或地址)获取调试信息,而不是回调。
我正在使用函数 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
是否可以用来定位 class 成员函数的源代码。
struct A{
int my_member_function(){return 5.;}
};
_bfd_elf_find_nearest_line (...,
what_should_I_put_here??,
...)
Class 成员函数(在这种情况下,如果类型为 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
库是否能够找到原始 [=22] 的源位置=] 通过这个指针。
(至少目前我对虚函数不感兴趣。)
换句话说,
1) 我需要知道 bfd
是否能够定位成员函数,
2) 如果可以的话,我如何将类型 int (A::*)()
的成员函数指针映射到 bfd
可以接受的参数 (void*
).
我通过其他方式(堆栈跟踪)知道指针存在,例如我可以得到在这种情况下调用了自由函数 _ZN1A18my_member_functionEv
,但问题是我如何从 &(A::my_member_function)
.
好的,有好消息也有坏消息。
好消息:是可能的。
坏消息:这并不简单。
您需要 c++filt
实用程序。
还有一些方法可以读取您的 executable 的符号 table,例如 readelf
。如果您可以通过 bfd_*
调用枚举 [损坏的] 符号,您也许可以节省一步。
此外,这里有一个 大问题 :您需要在 文本字符串 中提供符号的 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
这样做 [可能] 更容易,因为您只需要解析过滤后的输出并寻找匹配的好听的名字。
此外,如果您有多个您关心的符号,您可以在一次调用中获取所有地址。
好的,我找到办法了。首先,我发现 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};
给出编译器错误:"you can't take the address of the destructor"。
对于构造函数,我什至找不到正确的语法,因为它作为语法错误而失败。
void_caster_t void_caster = {&A::A};
同样,无法实现的所有逻辑都涉及无意义的回调,但这是不同的,因为我希望指针(或地址)获取调试信息,而不是回调。