首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Linux调试工具|Addr2line

addr2line translates addresses into file names and line numbers. Given an address in an executable or an offset in a section of a relocatable object, it uses the debugging information to figure out which file name and line number are associated with it.

描述:addr2line将地址转换为文件名和行号。给定可执行文件中的地址或可重定位对象部分中的偏移量,它会使用调试信息来确定与之相关的文件名和行数。

一、前言

通过addr2line的描述可知,在使用addr2line将地址转换为函数、文件名或行号时,其有两种使用方法:即对于可执行文件,addr2line后直接跟十六进制的地址值;对于可重定位对象文件,addr2line后直接跟十六进制的地址偏移量。从而实现正确输出需要的信息,否则会导致地址无法解析,输出。

提示:可通过file命令区分可执行文件与可重定位对象

二、用法

addr2line用于得到程序指令地址所对应的函数,以及函数所在的源文件名和行号。如果没有在命令行中给出地址,就从标准输入中读取它们。

基本用法:addr2line [选项] [地址]

三、程序崩溃场景

1、用户态普通程序

使用方法:addr2line -e 进程名 IP指令地址 -f

用户态程序有时可能因为各种原因导致崩溃,发生段错误,比如空指针等。如果没有靠谱的工具,我们就只能靠猜哪里的代码可能存在问题,这里通过Linux自带的addr2line工具调试程序,能够快速直接帮我们准确定位到文件、异常函数名以及行号。

segfault.c源文件:

使用gcc进行编译,如下:

dmesg查看报错信息,如下:

该例子说明addr2line能够直观程序发生段错误的函数以及文件和行号。

:如果编译程序时没有加上-g参数(即程序不含调试信息),就只能显示出函数名,显示不出具体所在文件的位置,如下:

提示:如何区分程序是否还有调试信息,可通过file命令,如果输出结果中显示with debug_info则表示程序含有调试信息,否则表示不含有调试信息。

2、动态库程序

在动态库中发生段错误,也可以使用addr2line进行定位。

使用方法:addr2line -e 动态库名 IP指令地址-基地址 -f

test.c源文件:

foo.h:

foo.c:

先编译动态库, 再编译主程序, 让它链接动态库, 最后运行之:

查看dmesg日志,如下:

根据日志可知,段错误发生的位置是在test进程调用的库里,我们先使用ldd找到动态库的位置,如下:

为程序崩溃点指令地址,使用dmesg消息中ip指令地址减去动态库基址值(), 差值为错误点在动态库的地址,调用addr2line, 注意-e参数后文件名改为动态库名:

动态库基址值(模块载入地址)即dmesg消息语句中的值。

3、内核模块程序

使用方法:addr2line -e xxx.ko 地址偏移量 -f

当内核模块程序异常时,可能会导致机器直接死机重启,这种情况下定位bug可能就比较麻烦,dmesg日志在机器重新启动后,新的日志会覆盖掉原来的报错日志,从而对定位内核异常问题造成麻烦。常见

提示:Linux内核错误

1、panic 当内核遇到严重错误的时候,内核,立马崩溃。死机。

2、Oops 是内核遇到错误时发出的提示“声音”,有时候会触发,有时候不会,而是直接杀死当前进程,系统可以继续运行。

比如说内核态下的段错误,当内核设置了的时候,会触发。【的值在内核编译的时候配置,可以在查看值,同时可以使用修改】

当的时候,如果错误发生在中断上下文,也会触发。如果错误只是发生在进程上下文,这个时候只需要kill当前进程。【中断上下文包括以下情况:硬中断、软中断、NMI】。的时候内核还可以运行,只是可能不稳定,这个时候,内核会调用printk打印输出内核栈的信息和寄存器的信息。

本人所用主机即属于一旦发生,就会触发,因此总是无法查看时的dmesg日志,经查阅资料,发现是内核参数的原因导致的,因为该参数被设置为1,所以会触发,从而导致机器总是死机重启,无法查看时的dmesg日志。下面提供两种方法修改内核参数,使其不会在的时候触发导致死机重启。

方法一:修改 下内核参数文件内容,临时生效,重启后失效。

方法二:修改 文件的内核参数来永久更改。

设置好Oops内核参数以后,让我们来构造生成Oops的程序,源代码如下:

oops.c源文件

编译内核模块的Makefile文件

编译以及安装oops.ko模块,出现killed,由于错误未发生在中断上下文,未导致死机重启。

加载模块将生成以下消息,dmesg查看信息如下:

消息解析:

Oops: 0002 -- 错误码

Oops: [#1] -- Oops发生的次数

CPU: 34 -- 表示Oops是发生在CPU34上

关键信息如下,这里提示在操作函数do_oops的时候出现异常,地址偏移量0x5:

为什么这条信息关键,因为其含有指令指针;指令指针的基本功能是指向要执行的下一条地址。在 8位微处理器上的寄存器名称是(,程序计数器),从起,被称为(,指令指针)。主要区别在与指向正在执行的指令,而IP指向下一条指令。在64位模式下,指令指针是RIP寄存器。这个寄存器保存着下一条要执行的指令的64位地址偏移量。64位模式支持一种新的寻址模式,被称为相对寻址。使用这个模式,有效地址的计算方式变为(指向下一条指令)加上位移量。

由此可以看出内核执行到这个地址的时候出现异常,我们只需要找到这个地址对应的代码即可。

打印格式: 即: : 符号 :地址偏移量 :函数的长度 : 所属内核模块

指示了是在函数中出现的异常, 表示出错的地址偏移量, 表示函数的大小。使用file查看内核模块文件类型:

上述结果显示该内核模块文件为可重定位对象文件,因此使用addr2line直接跟十六进制的地址偏移量定位文件名、行号和行数,如下:

可以看到异常代码在文件第7行,根据源代码,可知该行代码访问非法内存地址。

四、总结

Addr2line 工具(它是标准的 GNU Binutils 中的一部分)是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具。这在应用程序和内核程序执行过程中出现崩溃时,可用于快速定位出出错的位置,进而找出代码的bug。一般适用于 debug 版本或带有 symbol 信息的库。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230617A03P7Z00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券