You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下打印栈回溯代码异常,addr2line输出全为?? ??:0求助

问题描述

我尝试在代码触发异常后打印栈回溯信息,获取回溯的代码如下:

constexpr size_t MAX_STACK_FRAMES{ 64 };
static void* stack_traces[MAX_STACK_FRAMES];
void posix_print_stack_trace()
{
    int i, trace_size = 0;
    char** messages = (char**)NULL;
    char buffer[1024]{};    // buffer for error message


    trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
    messages = backtrace_symbols(stack_traces, trace_size);

    /* skip the first couple stack frames (as they are this function and
       our handler) and also skip the last frame as it's (always?) junk. */
       // for (i = 3; i < (trace_size - 1); ++i)
       // we'll use this for now so you can see what's going on
    for (i = 0; i < trace_size; ++i)
    {
        if (addr2line(global_program_name, stack_traces[i]) != 0)

我通过调用addr2line命令的addr2line函数尝试格式化回溯信息,代码如下:

/* have addr2line map the address to the relevant line in the code */
#ifdef __APPLE__
  /* apple does things differently... */
    sprintf(addr2line_cmd, "atos -o %.256s %p", program_name, addr);
#else
    sprintf(addr2line_cmd, "addr2line -f -p -e %.256s %p", program_name, addr);
#endif

但得到的输出全为:

?? ??:0
?? ??:0
?? ??:0
?? ??:0
?? ??:0
?? ??:0
?? ??:0

我确定问题出在addr2line的调用方式上,若需调整传入的地址,请问该如何操作?

解决方案
  • 修正地址输出格式
    %p 输出的是带0x前缀的地址,而addr2line/atos需要的是纯十六进制数字(无前缀)。将地址转换为无符号长整型,用%lx格式输出:
#ifdef __APPLE__
    sprintf(addr2line_cmd, "atos -o %.256s %lx", program_name, (unsigned long)addr);
#else
    sprintf(addr2line_cmd, "addr2line -f -p -e %.256s %lx", program_name, (unsigned long)addr);
#endif
  • 抵消ASLR的地址偏移
    系统开启ASLR时,backtrace返回的是虚拟地址,需要减去程序的加载基址才能得到正确的偏移地址:

    • Linux下读取/proc/self/maps获取基址:
      unsigned long get_base_address() {
          FILE* maps = fopen("/proc/self/maps", "r");
          char line[256];
          unsigned long base = 0;
          if (maps) {
              if (fgets(line, sizeof(line), maps)) {
                  sscanf(line, "%lx-", &base);
              }
              fclose(maps);
          }
          return base;
      }
      
      调用时使用(unsigned long)addr - get_base_address()作为传入addr2line的地址。
    • macOS下通过dyld接口获取基址:
      #include <mach-o/dyld.h>
      unsigned long get_base_address() {
          return _dyld_get_image_vmaddr_slide(0);
      }
      
  • 确保编译时保留调试符号
    编译必须添加-g参数保留调试信息,Release版本可搭配-Og(保留调试信息的优化编译),避免符号被剥离导致addr2line无法解析。

  • 验证程序路径的正确性
    确认program_name是程序的绝对路径,可通过以下方式获取:

    • Linux:realpath("/proc/self/exe", NULL)
    • macOS:_NSGetExecutablePath接口获取绝对路径

内容的提问来源于stack exchange,提问作者David Partridge

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 07:57:18