当前位置: 首页 > 工具软件 > GNU Binutils > 使用案例 >

GNU binutils工具集(1):addr2line

令狐宜民
2023-12-01

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

在binutils工具集中,以下工具是我们在做嵌入式软件开发时需要掌握的。

  • as:是汇编编译器,用于将汇编代码转换为目标文件。
  • addr2line:用来把程序地址转换为文件名和行号。
  • ar:用于创建和修改档案文件,以及从档案文件中抽取文件。静态库(.a文件)就是一~种档案文件,需要用它生成和管理。
  • ld:是链接器。
  • nm:用于列出程序文件中的符号及符号在内存中的(开始)地址。符号包含C程序中的函数名和变量名。
  • objcopy:可以用来从程序文件中拷贝出我们所指定的段,在将引导加载器烧制到闪存中时,有时需要通过从程序中抽取段的方式生成烧写文件,这时objcopy工具就能派上用场。
  • objdump:能显示程序文件的相关信息和对程序文件进行反汇编。
  • ranlib:用于生成一个档案文件的内容索引,以加快对档案文件的查找速度。将该工具运用于静态库能提高库参与链接的效率。
  • size:用于了解程序文件中各段的大小。
  • strings:用于查看程序文件内的可显示字符串。
  • strip:用于剥去程序文件的调试信息,以减小程序文件所占用的存储空间。这个工具对于存储空间有限的嵌入式系统尤为有用。

 addr2line有什么作用呢? 可别小瞧它, 它可以定位到代码出错的位置。 下面, 我们来看看这个简单的代码:

#include <stdio.h>
 
int main()
{
        int *p = NULL;
        *p = 0;
 
        printf("bad\n");
        return 0;
}

这个程序很小, 我们可以一眼就看出程序在运行期出错。 但是, 如果是大程序, 在运行期出错, 我们该如何定位呢?  那就必须依赖于工具, 而不是你我的肉眼。

我们以上述小程序为例, 进行如下操作:

$ gcc -o test -g test.c 
$ ./test 
Segmentation fault (core dumped)
$ dmesg | grep test
[181184.273345] test[9021]: segfault at 0 ip 00000000004004fd sp 00007fff405fda60 error 6 in test[400000+1000]
$ addr2line -e test 00000000004004fd
/home/tmp/test.c:6
$ cat -n test.c
     1	#include <stdio.h>
     2	
     3	int main()
     4	{
     5		int *p = NULL;
     6		*p = 0;
     7	
     8		printf("bad\n");
     9		return 0;
    10	}

我来解释一下:

  1. gcc -o test -g test.c:生成带有调试信息的可执行文件taogeSeg
  2. dmesg | grep test :获得执行taogeSeg后的出错信息, 你可以将结果理解为日志, 其中的080483c9是一个地址, 正是这个地址出错了
  3. addr2line -e test 00000000004004fd :将出错地址转换成源代码对应的行, 结果为6, 也就是说源代码第6行有问题。 一看, 果然是,万恶的*p=0;被揪出来了。

有人可能会说GDB也可以呀,确实可以,下面把基本操作步骤写出来。

  • ulimit -c:由于系统默认是把core文件大小设置为0,那么当法错误时,就不会产生core文件,解决就是用这条命令指定core文件大小就可以了。
  • gcc fun.c -g:生成带有调试信息的可执行文件。
  • gdb a.out core:用gdb来调试
# ulimit -c
0
# ulimit -c 1024
# ulimit -c
1024
# gcc fun.c -g
# ls
a.out  fun.c
# ./a.out 
段错误 (核心已转储)
# gdb a.out core 
GNU gdb (Ubuntu 7.7-0ubuntu3.1) 7.7
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
[New LWP 10092]
Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00000000004004fd in main () at fun.c:6
6		*p = 1;                                   //《----出错位置
(gdb) bt
#0  0x00000000004004fd in main () at fun.c:6     //《----出错位置
(gdb) quit

 

总结一下:

针对上面的内容有两个问题:

1:在实际中, 有很多问题是概率发生的, 很难重现,这时该怎么办。

addr2line和上面gdb core方法都可以解决问题,但是普通的gdb,由于没有利用core文件,所以无法找到出错概率小的的错误。

2:由于我们所有的编译都加了-g这个选项, 主要是为了调试(保存了调试所需的调试信息),但是实际的项目中,会用strip来去除调试信息(脱衣服),那么就不能用core + 可执行文件(去除了调试信息) 来gdb调试了,也不能用addr2line。

这时候就需要这么办:

把有调试信息的可执行文件A复制一份得到B

用strip去除B的调试信息

如果设备中发生错误,还是会产生core文件,这时把core文件从设备复制到PC中,用A来定位错误。

下面就是操作步骤

# gcc fun.c -g
# ls
a.out  fun.c
# cp a.out b.out   
# strip b.out             //b.out被去除符号 
# nm b.out
nm: b.out:无符号
# nm a.out            //a.out有完整的符号表
00000000004004ed T main
0000000000400460 t register_tm_clones
0000000000400400 T _start
0000000000601038 D __TMC_END__
# ./b.out 
段错误 (核心已转储)
# gdb a.out core 
GNU gdb (Ubuntu 7.7-0ubuntu3.1) 7.7
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.

warning: core file may not match specified executable file.
[New LWP 10510]
Core was generated by `./b.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00000000004004fd in main () at fun.c:6
6		*p = 1;
(gdb) quit

 

 

 

 

 

 

 

 类似资料: