linux调试技巧

如果使用gcc作为编译器,那么开发过程中一定离不开使用与之配套的一个工具集(tool chain),即binutils。工具集中的部分工具除了被gcc在后台使用为我们创建程序文件外,其他的则有助于方便开发和调试。

一些常见的嵌入式软件开发工具如下:

as 是汇编编译器,用于将汇编代码转换为目标文件。

addr2line 用于得到程序指令地址所对应的函数,以及函数所在的源文件名和函数。

ar 用于创建和修改档案文件,以及从档案文件中抽取文件。静态库(.a文件)就是一种档案文件,需要用它生成和管理。

ld 是链接器。

nm 用于列出程序文件中的符号及符号在内存中的(开始)地址。符号包含C程序中的函数名和变量名。

objcopy 可以用来从程序文件中拷贝出我们所指定的的段,在将引导加载器烧制到闪存中时,有时需要通过从程序中抽取段的方式生成烧写文件,这时objcopy工具就能派上用场了。  

objdump 能显示程序文件的相关信息和对程序文件进行反汇编

ranlib 用于生成一个档案文件的内容索引,以加快对档案文件的查找速度。将该工具运用于静态库能提高库参与链接的效率。

size 用于了解程序文件中各段的大小

strings 用于查看程序文件内的可显示字符串

strip 用于剥去程序文件的调试信息,以减小程序文件所占用的存储空间。这个工具对于存储空间有限的嵌入式系统尤为有用。

addr2line用于得到程序指令地址所对应的函数,以及函数所在的源文件名和行号。

 

在不少嵌入式开发环境中,编译器的名称往往不是gcc,而是像arm-rtems-gcc这样的,对于这种命名形式的编译器,读者通常可以找到arm-rtems-addr2line ,arm-rtems-objdump等相应名称的工具,这是GNU工具集的一种命名惯例。

 

本文并不是binutils工具集的完整参考手册,当需要得到更为详细的帮助信息时,可以参照对应工具的man和info信息。另一个更为简单的方法时运行相应的工具并指定--help参数,可以获得该工具的简单帮助信息。

 

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

将地址转换成文件名/行号对。

如果没有在命令行中给出地址,就从标准输入中读取它们

选项是:

@<file> 读取选项从 <file>

-a --addresses 显示地址

-b --target=<bfdname> 设置二进位文件格式

-e --exe=<executable><name> 设置输入文件名称(默认为 a.out)

-i --inlines 解开内联函数

-j --section=<name> 读取相对于段的偏移而非地址

-p --pretty-print 让输出对人类更可读

-s --basenames 去除目录名

-f --functions 显示函数名

-C --demangle[=style] 解码函数名

-h --help 显示本帮助

实战操练:

建立main.c文件如下:

输入:

 

gcc -g main.c -o app -Wall

一定要加-g选项,不然没有调试信息。

编译没有任何警告(作为gcc来说不应该啊^_^)

运行

显示段错误。使用dmesg(该命令可以参考https://linux.cn/article-3587-1.html)追踪:

注意最后一行 app 可执行文件的描述:segfault(段错误)ip为4004e6这就是程序执行出错的位置。这时,addr2line登场:

显示在在我的目录下 main.c的第七行出现段错误,现在回过头去看第一幅图片,你应该会发现addr2line准确地得到了我们想要调试的东西。

一般常用的两个option为-f和-e。

下面,我们再来做一件有意思的事情:

main.c如下:

运行如下:

这里的地址是通过程序打印而获得,在现实工作中往往是程序崩溃是通过某种方式获得地址,比如上面的dmesg,或者在嵌入式系统的一些IDE上也有类似功能。通过nm -n指令可以看到函数对应的开始地址:只取foo和main函数截图如下:

测试结果表明,addr2line可以通过函数的任一地址找到所属函数的相关信息。

根据地址找到了函数名,现在我们写一个c++程序main.cpp如下图:

运行如下:

首先和C语言一样,addr2line找到了foo函数入口在main.cpp的第五行,(也可以看出函数调用是从大括号处开始),但是函数名却是_Z3f00v,这就有点奇怪了,怎么不是foo呢?这是因为c++的重载使得这一现象产生。从C语言的角度来看c++的重载函数名称必须不同,为了做到这一点,c++编译器的处理方法就是对于每一个函数,将根据输入参数采取一定的编码方式,形成不同的c函数名,这个过程就是名字分裂过程。如上的 _Z3f00v就是c++程序中foo()函数的名字分裂后的形式。毕竟c++是从C语言上发展而来的。

 

我们可以通过--demangle选项获得函数名:

--demangle=gnu-v3用于还原foo()函数。

 

附录:Android反编译方法汇总

一、堆栈信息:
test.ko
[<ffffff905d134c28>] skb_encry+0x94/0x9e4 [test]

二、反编译工具:gdb、objdump、addr2line
安卓对应的为:(表示arch64位架构,linux系统)
aarch64-linux-android-objdump
aarch64-linux-android-gdb
aarch64-linux-android-addr2line

三、编译是加入调试信息:
(1)make COPTS-g
(2)gcc -g

四、反编译ko文件
(1)Gdb使用方法:
执行以下语句打开test.ko
./aarch64-linux-android-gdb test.ko
根据堆栈信息,使用 l *(l是L的小写)即可反编译定位到出错位置
如:l*skb_encry+0x94

(2)Objdump能够将整个ko反编译出来,能够看到所有ko里的代码和汇编代码,Objdump使用方法:
./aarch64-linux-android-objdump -S test.ko > test.s
反编译得到test.s文件,就可以使用文本工具查看,根据堆栈信息和偏移量找到错误行

(3)Add2line使用方法:
堆栈信息
[<ffffff905d134c28>] skb_encry+0x94/0x9e4 [test]
地址ffffff905d134c28
用法:
./aarch64-linux-android-addr2line -e test.ko ffffff905d134c28

五、定位堆栈信息在内核中的位置
使用vmlinux:按照第一节的方法,将test.ko对应替换成内核对应的vmlinux即可,它是kernel的中间产物。
vmlinux路径:obj/KERNEL_OBJ/vmlinux

我们拿到panic信息后,pc即为程序挂掉的地方,我们可以从这里得到相关的信息。

prebuilts/gcc/linux-x86/cortex-a/aarch64-linux-android-4.9/bin/aarch64-linux-android-gdb vmlinux
list *(函数名+函数内偏移)

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值