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

如何通过dladdr()函数编程打印源文件名及行号?

问题

我尝试使用dladdr()函数打印回溯信息,当前代码中的info.dli_fname仅显示ELF文件的名称。请问是否可以不借助addr2line或gdb工具,通过编程方式解析并打印出源文件名及代码行号?

代码:

void print_caller(void)
{
    int       rc = -1;
    Dl_info info = {0};
    void *pc =  __builtin_return_address(0);

    rc = dladdr(pc, &info);

    printf(" ==> %p: %s (in %s)\n", pc, info.dli_sname, info.dli_fname);
}

输出:

$ ./a.out 
 ==> 0x55a6b04a1589: foo2 (in ./a.out)
回答

可以实现,但需要直接解析ELF文件中的DWARF调试信息——这也是addr2line和gdb背后依赖的核心数据。dladdr()本身只能获取符号名称和ELF文件路径,无法提供行号和源文件信息,因为这些数据存储在DWARF专用段中,需要手动解析。

具体实现思路:

  • 先通过dladdr()拿到目标ELF文件的路径,打开文件后定位到DWARF相关段(比如.debug_info、.debug_line)。
  • 将__builtin_return_address返回的虚拟地址转换为ELF文件内部的相对偏移(减去ELF的加载基址)。
  • 解析.debug_line段,该段存储了地址与源文件、行号的映射表,遍历匹配即可找到对应地址的行号信息。

需要注意的细节:

  • 编译程序时必须添加-g参数生成DWARF调试信息,否则没有可解析的行号数据。
  • 手动解析DWARF逻辑复杂,可借助轻量的DWARF解析库(比如libdwarf)简化开发,避免从零实现解析逻辑。

以下是依赖libdwarf的简化示例:

#include <dwarf.h>
#include <libdwarf.h>
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

void print_caller_with_line(void) {
    void *pc = __builtin_return_address(0);
    Dl_info info = {0};
    if (!dladdr(pc, &info)) {
        fprintf(stderr, "dladdr failed\n");
        return;
    }

    // 初始化libdwarf并打开目标ELF文件
    Dwarf_Debug dbg;
    int res = dwarf_init_path(info.dli_fname, NULL, DW_GROUPNUMBER_ANY, DW_DLC_READ, NULL, NULL, &dbg);
    if (res != DW_DLV_OK) {
        fprintf(stderr, "dwarf_init_path failed\n");
        return;
    }

    // 计算地址在ELF文件内的相对偏移(适配PIE可执行文件)
    Dwarf_Addr load_base = (Dwarf_Addr)info.dli_fbase;
    Dwarf_Addr addr_in_file = (Dwarf_Addr)pc - load_base;

    // 遍历编译单元查找对应行号
    Dwarf_Unsigned cu_header_length, abbrev_offset, next_cu_header;
    Dwarf_Half version, address_size;
    while (dwarf_next_cu_header(dbg, &cu_header_length, &version, &abbrev_offset, &address_size, NULL, &next_cu_header, NULL) == DW_DLV_OK) {
        Dwarf_Line *line_buf;
        Dwarf_Signed line_count;
        if (dwarf_srclines(dbg, &line_buf, &line_count) == DW_DLV_OK) {
            for (int i = 0; i < line_count; i++) {
                Dwarf_Addr line_addr;
                const char *filename;
                Dwarf_Unsigned line_num;
                if (dwarf_lineaddr(line_buf[i], &line_addr) == DW_DLV_OK &&
                    dwarf_lineno(line_buf[i], &line_num) == DW_DLV_OK &&
                    dwarf_linesrc(line_buf[i], &filename, NULL) == DW_DLV_OK) {
                    // 匹配目标地址所在的行区间
                    if (line_addr <= addr_in_file) {
                        if (i == line_count - 1 || (dwarf_lineaddr(line_buf[i+1], &line_addr) != DW_DLV_OK || addr_in_file < line_addr)) {
                            printf(" ==> %p: %s (in %s:%u)\n", pc, info.dli_sname, filename, line_num);
                            break;
                        }
                    }
                }
            }
            dwarf_dealloc_lines(dbg, line_buf, line_count);
        }
        dwarf_next_cu_header_skip(dbg);
    }

    dwarf_finish(dbg);
}

编译时需要链接libdwarf和dl库:gcc -g your_code.c -o a.out -ldl -ldwarf

这种方式无需调用外部工具,完全通过代码解析DWARF信息获取源文件和行号。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 12:36:24