分析binutils之addr2line

本文介绍如何在ARM板上实现实时打印文件名和函数名信息,通过编译binutils并利用addr2line工具简化流程。文章详细记录了配置过程及原理,并提供了源代码示例。

由于实时打印bt缺少symbol信息,为了在arm板实时打印出file and function info, 减少手动使用addr2line工具,参数binutils中的addr2line源码实现实时打印出file and function 信息。如下步骤:

binutils-2.26 configure说明:
    build 就是你现在使用的机器。
    host 就是你编译好的程序能够运行的平台。
    target 编译程序能够处理的平台。一般都用在构建编译本身的时候(gcc), 才用target, 也就是说平时我们所说的交叉编译用不到target.

    比如: 在386的平台上编译可以运行在arm板的程序 ./configure –build=i386-linux,–host=arm-linux就可以了.
    因为一般我们都是编译程序而不是编译工具.
    如果我们编译工具,比如gcc,这个target就有用了.如果我们需要在一个我们的机器上为arm开发板编译一个可以处理 mips程序的gcc,那么target就是mips

1.下载binutils-2.26
2.编译binutils-2.26,当前arm工具链为:arm-linux-xxx。预先要export PATH=&PATH:xxxx,其中xxx代表arm交叉工具链(arm-linux-xxx)所在目录
cd binutils-2.26
./configure --build=i386-linux --host=arm-linux --prefix=/work/binutils/binutils-2.26_arm/out
make

3.在binutils-2.26/bfd/下找到找到目标库libbfd.a libiberty.a libz.a
root@libz:/work/binutils/binutils-2.26# find ./ -name \*.a
./opcodes/.libs/libopcodes.a
./opcodes/libopcodes.a
./libiberty/libiberty.a
./zlib/libz.a
./ld/.libs/libldtestplug3.a
./ld/.libs/libldtestplug.a
./ld/.libs/libldtestplug2.a
./bfd/libbfd.a
./bfd/.libs/libbfd.a

为了使用BFD,需要包括'bfd.h'并且连接的时候需要和静态库 libbfd.a libiberty.a libz.a

gcc test.c libbfd.a libiberty.a libz.a -ldl -I./


原理:遍历目标文件中的所有所查找的地址属于哪个SECTION。如下为一个SECTION,其开始地址为0x33855, 长度为0x4f8,如果所查找的目标地址属于此范围,那目标函数便是_ZL19translate_addressesP3bfdPKcPci:

00033855 l     F .text  000004f8              _ZL19translate_addressesP3bfdPKcPci

Addr2Line.cpp

#include <stdlib.h>
#include "config.h"
#include "bfd.h"
#include "libiberty.h"
#include "demangle.h"
#include "elf-bfd.h"
#include <string.h>

#include "Addr2Line.h"

///Symbol table
static asymbol **syms = NULL;
#define FATAL printf
///Read in the symbol table
static bfd_boolean
slurp_symtab (bfd *fbfd)
{
    long storage = 0;
    long symcount = 0;
    bfd_boolean dynamic = FALSE;

    if (0 == (bfd_get_file_flags (fbfd) & HAS_SYMS))
    {
        FATAL("unkonw symbol error(%s)\n", bfd_get_filename (fbfd));
        return FALSE;
    }

    storage = bfd_get_symtab_upper_bound(fbfd);
    if (storage == 0)
    {
        storage = bfd_get_dynamic_symtab_upper_bound (fbfd);
        dynamic = TRUE;
        storage = 1;
    }

    if (storage < 0)
    {
        FATAL("(-1)unkonw symbol error(%s)\n", bfd_get_filename (fbfd));
        return FALSE;
    }

    if (NULL == (syms = (asymbol **) xmalloc (storage)))
    {
        FATAL("can't malloc position to storage symbol info error\n");
        return FALSE;
    }

    if (dynamic)
    {
        symcount = bfd_canonicalize_dynamic_symtab (fbfd, syms);
    }
    else
    {
        symcount = bfd_canonicalize_symtab (fbfd, syms);
    }

    if (symcount < 0)
    {
        FATAL("can't get symbol from file(%s)\n", bfd_get_filename (fbfd));
    }

    ///try again
    if (symcount == 0 &&
        ! dynamic     &&
        (storage = bfd_get_dynamic_symtab_upper_bound (fbfd)) > 0)
    {
        free (syms);
        syms = NULL;
        if (NULL == (syms = (asymbol **) xmalloc (storage)))
        {
            FATAL("can't malloc position to storage symbol info error\n");
            return FALSE;
        }
        symcount = bfd_canonicalize_dynamic_symtab (fbfd, syms);
    }

    if (symcount <= 0)
    {
        free (syms);
        syms = NULL;
        return FALSE;
    }

    return TRUE;
}


static bfd_boolean
translate_addresses (bfd *fbfd, const char *str_addr, char *out_buf, const int buf_len)
{
    static char buf[1024];
    const char *filename = NULL;
    const char *functionname = NULL;
    unsigned int line = 0;
    unsigned int discriminator = 0;
    asection *sect = NULL;
    unsigned int i = 0;

    if ((NULL == str_addr) || (NULL == out_buf) || (0 >= buf_len))
    {
        FATAL("address is null error, str_addr(%p), out_buf(%p), buf_len(%d)\n", str_addr, out_buf, buf_len);
        return FALSE;
    }

    bfd_vma pc = bfd_scan_vma (str_addr, NULL, 16);
    if (bfd_get_flavour (fbfd) == bfd_target_elf_flavour)
    {
        const struct elf_backend_data *bed = (elf_backend_data *)(((fbfd)->xvec)->backend_data);
        bfd_vma sign = (bfd_vma) 1 << (bed->s->arch_size - 1);

        pc &= (sign << 1) - 1;
        if (bed->sign_extend_vma)
        pc = (pc ^ sign) - sign;
    }

    memset(buf, 0x0, sizeof(buf));
    bfd_sprintf_vma(fbfd, buf, pc);
    snprintf (out_buf, buf_len, "0x%s :", buf);

    ///check all sections to find out current address belony to which section
    for (sect = fbfd->sections; sect != NULL; i++, sect = sect->next)
    {
        bfd_vma vma;
        bfd_size_type size;

        if ((sect->flags & SEC_ALLOC) == 0)
        {
            continue;
        }

        ///section start position
        if (pc < sect->vma)
        {
            continue;
        }

        ///section end position
        if (pc >= sect->vma + sect->size)
        {
            continue;
        }

        if (TRUE == bfd_find_nearest_line_discriminator (fbfd, sect, syms, pc - sect->vma,
                                               &filename, &functionname,
                                               &line, &discriminator))
        {
            break;
        }
    }

    if (i > fbfd->section_count)
    {
        FATAL("(%d)can't find the address(%s) info in all section(%d)\n", i, str_addr, fbfd->section_count);
        strncpy(buf, out_buf, sizeof(buf));
        snprintf (out_buf, buf_len, "%s ?? ??:0", buf);
        return FALSE;
    }

    ///luckly, found the address infomation, to format the output string
    while (TRUE)
    {
        const char *name = functionname;
        char *alloc = NULL;

        ///format function name
        if (name == NULL)
        {
        	return FALSE;
        }
		
		if (*name == '\0')
		{
			return FALSE;
		}

		alloc = bfd_demangle (fbfd, name, DMGL_ANSI | DMGL_PARAMS);
		if (alloc != NULL)
		{
			name = alloc;
		}

        strncpy(buf, out_buf, sizeof(buf));
        snprintf (out_buf, buf_len, "%s %s  at", buf, (NULL != name) ? name : "??");

        if (alloc != NULL)
        {
            free (alloc);
        }

        strncpy(buf, out_buf, sizeof(buf));
        snprintf (out_buf, buf_len, "%s %s:", buf, (NULL != filename) ? filename : "??");

        strncpy(buf, out_buf, sizeof(buf));
        if (line != 0)
        {
            if (discriminator != 0)
            {
                snprintf (out_buf, buf_len, "%s%u (discriminator %u)", buf, line, discriminator);
            }
            else
            {
                snprintf (out_buf, buf_len, "%s%u", buf, line);
            }
        }
        else
        {
            snprintf (out_buf, buf_len, "%s?", buf);
        }

        if (FALSE == bfd_find_inliner_info (fbfd, &filename, &functionname, &line))
        {
            break;
        }

        ///inline info
        strncpy(buf, out_buf, sizeof(buf));
        snprintf (out_buf, buf_len, "%s (inlined by) ", buf);
    }

    return TRUE;
}

int
addr2line (const char *file_name, const char *str_addr, const int index)
{
    bfd *fbfd = NULL;
    bfd_boolean ret = FALSE;
#define LEN (2048)
    static char out_buf[LEN];
    const int buf_len = LEN;
#undef LEN

    if (NULL == file_name)
    {
        FATAL("file name is null error\n");
        return FALSE;
    }

    bfd_init();
    fbfd = bfd_openr (file_name, NULL);
    if (NULL == fbfd)
    {
        FATAL("faild to open bfd file(%s)\n", file_name);
        return FALSE;
    }

    ///check file format
    fbfd->flags |= BFD_DECOMPRESS;
    if (bfd_check_format (fbfd, bfd_archive))
    {
        FATAL("not support to analyse staic file(%s) error\n", file_name);
        goto __EXIT;
    }
    else if (!bfd_check_format (fbfd, bfd_object))
    {
        FATAL("file(%s) no symbol detech\n", file_name);
        goto __EXIT;
    }

    if (FALSE == slurp_symtab (fbfd))
    {
        FATAL("file(%s) start up symtab error\n", file_name);
        goto __EXIT;
    }

    memset(out_buf, 0x0, sizeof(out_buf));
    ret = translate_addresses (fbfd, str_addr, out_buf, buf_len);
    if (syms != NULL)
    {
      free (syms);
      syms = NULL;
    }

__EXIT:
    bfd_close (fbfd);
	if (TRUE == ret)
	{
		printf("#%d  %s\n", index, out_buf);
	}
    return ret;
}
#undef FATAL

#if (0)
main(int argc, char *argv[])  
{
    char *filename = NULL;
    char *str_addr = NULL;
    if (argc<3) exit(-1);

    filename = argv[1];
    str_addr = argv[2];
    process_file (filename, str_addr);
}
///http://www.mssl.ucl.ac.uk/swift/om/sw/help/man/bfd.html
///http://www.mssl.ucl.ac.uk/swift/om/sw/help/man/bfd_toc.html#TOC70
#endif

LOG:

all alloc memory info:
<<      (1) memory address:     0x946b2b0, size: 200, thread_id(0xb730f700), p_id(0x6f39)       >>
        (1) backtrace information(6):
#0  0x000314a9 : get_backtrace_info(char**&, unsigned int*)  at /work/memleak/CCheckMemoryLeak.cpp:85
#1  0x000315a5 : save_malloc_pointer_info(void const*, unsigned int)  at /work/memleak/CCheckMemoryLeak.cpp:119
#2  __wrap_realloc at (Unknow Source File):0
#3  main() at (Unknow Source File):0
#4  __libc_start_main() at (Unknow Source File):0
#5  ./TestChkMemLeak() [0x80488d1]() at (Unknow Source File):0
== all unfree memory time(1) size(0xc8) ==

源码:

http://download.youkuaiyun.com/detail/lbo4031/9546584

<think>我们正在讨论的是如何从binutils源码中单独编译addr2line工具。通常,binutils的编译过程会编译整个工具集(as,ld,objdump,addr2line等)。但有时我们可能只需要其中某一个工具,比如addr2line。根据binutils的构建系统,我们可以通过配置和编译时指定目标来单独编译某个工具。然而,需要注意的是,binutils的各个工具之间有一些依赖关系,单独编译可能会遇到依赖问题。但我们可以尝试通过配置和make目标来只构建addr2line。步骤:1.获取binutils源码并进入源码目录。2.配置binutils,但这里我们可以通过配置来指定构建的目录,并且只构建addr2line。3.使用make命令指定只编译addr2line及其依赖。但是,binutils的Makefile并没有为每个工具单独提供编译目标(如makeaddr2line)。但是,我们可以通过指定要构建的子目录来实现。经过查阅binutils的Makefile,addr2line位于binutils目录下。因此,我们可以尝试只构建binutils子目录。具体步骤:1.下载并解压binutils源码:```bashwgethttps://ftp.gnu.org/gnu/binutils/binutils-2.40.tar.gztarxfbinutils-2.40.tar.gzcdbinutils-2.40```2.配置binutils,指定安装目录(这里我们假设只安装addr2line):```bash./configure--prefix=/opt/addr2line```注意:我们不需要指定目标(--target),因为addr2line是主机工具,用于处理目标文件,但本身是运行在主机上的。3.编译binutils子目录(binutils子目录下包含addr2line):```bashmake-Cbinutilsaddr2line```或者进入binutils目录并编译:```bashcdbinutilsmakeaddr2line```4.安装addr2line(需要先创建安装目录):```bashmkdir-p/opt/addr2line/bincpbinutils/addr2line/opt/addr2line/bin/```但是,上述方法可能不会成功,因为addr2line依赖于一些库和头文件,可能在编译整个项目时才会生成。因此,更可靠的做法是编译整个binutils,但只安装addr2line。另一种方法是使用makeinstall-target,但binutils并没有为每个工具提供单独的安装目标。因此,我们可以采用以下方法:1.配置并编译整个binutils(但不安装):```bash./configure--prefix=/opt/addr2linemake```2.然后只安装addr2line工具。由于makeinstall会安装所有工具,我们可以先不执行makeinstall,而是手动复制addr2line:```bashcpbinutils/addr2line/opt/addr2line/bin/```3.同时,addr2line可能依赖一些库(如libiberty),需要将相关库也复制过去。可以通过ldd命令查看依赖:```bashlddbinutils/addr2line```然后复制相应的库到安装目录的lib下(如果动态库不在系统路径中)。但是,为了减少编译时间,我们也可以只编译binutils目录(以及它所依赖的目录)。根据binutils的依赖关系,通常需要先编译libiberty和bfd等库。因此,我们可以按顺序编译依赖的库,然后再编译binutils目录:1.编译libiberty:```bashmake-Clibiberty```2.编译bfd:```bashmake-Cbfd```3.编译opcodes(可能不需要):```bashmake-Copcodes```4.编译binutils:```bashmake-Cbinutils```然后就可以在binutils目录下得到addr2line。但是,这种方法可能需要手动处理依赖关系,并且可能因为不同版本而有所不同。考虑到简便性和可靠性,如果条件允许,建议编译整个项目然后只复制addr2line。如果希望最小化编译,可以尝试以下步骤(在binutils-2.40中测试):1.进入binutils源码目录。2.运行configure:```bash./configure```3.编译必要的库:```bashmakeall-libibertymakeall-bfdmakeall-opcodes```4.进入binutils目录并编译:```bashcdbinutilsmakeaddr2line```这样,在binutils目录下就会生成addr2line可执行文件。注意:如果遇到头文件找不到的问题,可能需要先编译include目录(通常不需要,因为头文件是源码的一部分,但有些是在配置过程中生成的)。总结:单独编译addr2line需要先编译其依赖的库(libiberty,bfd,opcodes等),然后进入binutils目录编译addr2line。下面给出完整步骤:```bash#下载并解压wgethttps://ftp.gnu.org/gnu/binutils/binutils-2.40.tar.gztarxfbinutils-2.40.tar.gzcdbinutils-2.40#配置./configure#编译依赖库makeconfigure-subdirmake-Clibibertymake-Cbfdmake-Copcodes#编译binutils目录中的addr2linemake-Cbinutilsaddr2line#检查生成的addr2linefilebinutils/addr2line```然后就可以使用生成的addr2line了。如果希望安装到指定目录,可以手动复制:```bashmkdir-p/opt/addr2line/bincpbinutils/addr2line/opt/addr2line/bin/```注意:这种方法编译出来的addr2line是主机平台(比如x86_64)的工具,用于分析主机平台的目标文件。如果需要交叉平台的addr2line(比如运行在x86上但分析ARM目标文件),则需要在配置时指定目标平台(--target=arm-linux-gnueabi),并确保有对应的目标文件支持。另外,如果只需要addr2line,其他工具不会被编译,这样可以节省时间。但是,由于不同版本的binutils可能有不同的依赖,如果遇到编译错误,可能需要根据错误信息调整编译顺序或编译其他目录。最后,关于引用:在回答中我们参考了用户提供的引用,其中引用[3]提到版本兼容性问题,所以在使用自己编译的addr2line时,要确保用它来分析对应版本binutils编译出的二进制文件,避免出现问号问题[^3]。</think>### 如何单独编译 binutils 中的 addr2line 工具 要单独编译 binutils 中的 addr2line 工具,需通过源码编译并指定构建目标。以下是详细步骤: --- #### 1. **获取源码并配置** ```bash # 下载 binutils 源码 wget https://ftp.gnu.org/gnu/binutils/binutils-2.40.tar.gz tar xvf binutils-2.40.tar.gz cd binutils-2.40 # 配置编译环境(关键步骤) ./configure \ --enable-targets=all \ # 支持所有目标格式 --disable-nls \ # 禁用本地化 --disable-gdb \ # 禁用 GDB 相关工具 --disable-binutils \ # 禁用 binutils 其他工具 --disable-ld \ # 禁用链接器 ld --disable-gas \ # 禁用汇编器 --enable-addr2line # 明确启用 addr2line ``` #### 2. **仅编译 addr2line** ```bash # 进入 addr2line 源码目录 cd binutils # 仅编译 addr2line make addr2line ``` #### 3. **验证和安装** ```bash # 验证生成的 addr2line file addr2line # 应显示 ELF 可执行文件 # 安装到指定目录(可选) sudo cp addr2line /usr/local/bin/ ``` #### 4. **使用自定义编译的 addr2line** ```bash # 检查版本兼容性(重要!) ./addr2line --version # 使用示例(分析目标文件) addr2line -e your_binary -f -C 0x401000 ``` --- ### ⚠️ 关键注意事项 1. **版本匹配问题** 编译的 addr2line 版本必须与目标文件的编译工具链一致,否则会出现 `??:0` 错误[^3]。 **解决方案**:使用相同版本的 binutils 编译 addr2line 和目标文件。 2. **依赖处理** 若编译失败,需安装基础开发库: ```bash sudo apt-get install texinfo flex bison libz-dev ``` 3. **精简编译选项** 通过 `--disable-*` 系列参数禁用不需要的工具,可显著减少编译时间: ```bash --disable-gprof \ # 禁用 gprof --disable-gold \ # 禁用 gold 链接器 --disable-libctf \ # 禁用 CTF 库 ``` 4. **交叉编译场景** 如需分析跨平台目标文件,需指定目标架构: ```bash ./configure --target=arm-linux-gnueabi --enable-addr2line ``` --- ### 验证工具兼容性 ```bash # 检查能否解析符号 echo "0x401000" | ./addr2line -e your_binary -f # 成功输出示例 main /path/to/source.c:10 ``` 若输出 `??:0`,表明版本不匹配或调试信息缺失[^3]。 --- ### 性能优化建议 1. **静态链接**:编译时添加 `LDFLAGS="-static"` 避免动态库依赖 2. **调试信息**:保留目标文件的调试符号(编译时加 `-g` 选项) 3. **C++ 支持**:使用 `-C` 参数解析 C++ 修饰函数名[^4] > 完整选项参考:`addr2line --help`[^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值