linux 如何检测函数指针是来自共享库还是来自主可执行文件?

lmyy7pcs  于 2024-01-06  发布在  Linux
关注(0)|答案(1)|浏览(125)

我使用backtracebacktrace_symbols在程序中抛出自定义异常时获取堆栈跟踪(目前,在Linux.

  1. trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
  2. messages = backtrace_symbols(stack_traces, trace_size);

字符串
从堆栈跟踪中,我能够循环遍历每个帧,并提取程序在该帧的地址。
根据函数定义的位置,我得到两种类型的地址:
1.图书馆地址:

  1. /path/to/library.so(mangled_function_name+0x75) [0x7f02bcdfa849]
  2. ^^^^^^^^^^^^^^


1.可执行地址:

  1. /path/to/executable(mangled_function_name?+0x90) [0x403336]
  2. ^^^^^^^^


[0x7f02bcdfa849][0x403336]是我关心的问题。
我想把这些地址传递给addr2line
然而,对于属于共享库的地址,这将不起作用,我了解到我首先需要计算解析地址与库加载的基址的偏移量。

  1. addr2line -e /path/to/library.so 0x7f02bcdfa849 # won't work
  2. # or
  3. addr2line -e /path/to/executable 0x403336 # works just fine


我知道我可以使用::dladdr来找到基址,并从中找到偏移量。
但是,在运行时,我如何判断我是否需要进行这种转换?比如,我如何判断一个地址在运行时是否属于一个共享库?
我试着简单地检查.so是否显示在堆栈跟踪的框架中,但感觉......不对。

k5ifujac

k5ifujac1#

::dladdr1有我需要的信息。
如果我有addr_ptr,它是共享库或共享exe中的地址,我可以通过调用::dladdr1并使用返回的::link_map数据结构来获取要从addr_ptr中减去的基址。
::link_map有一个名为l_addr的字段,对于exe为0,对于链接库为非零。
因此,给定一个具有解析地址的addr_ptr

  1. #include <dlfcn.h> // for ::dladdr1
  2. #include <link.h> // for ::link_map
  3. ...
  4. const void* final_ptr = nullptr;
  5. ::Dl_info dli_;
  6. if (::link_map *lm = nullptr;
  7. ::dladdr1(addr_ptr, &dli_, reinterpret_cast<void **>(&lm), RTLD_DL_LINKMAP)) {
  8. if (lm != nullptr) {
  9. auto lib_delta_int = reinterpret_cast<std::uintptr_t>(addr_ptr) -
  10. reinterpret_cast<std::uintptr_t>(lm->l_addr);
  11. final_ptr = reinterpret_cast<const void *>(lib_delta_int);
  12. }
  13. }

字符串
final_ptr现在有正确的值传递给addr2line

展开查看全部

相关问题