如果使用gcc 作为编译器,那么在开发过程中一定离不开使用与之配套的一个工具集(tool chain),即binutils。工具集中的部分工具除了被gcc在后台使用为我们创建程序文件?外,其他的则有助于方便开发和调试。
在binutils工具集中,以下工具是我们在做嵌入式软件开发时需要掌握的。
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 }
我来解释一下:
有人可能会说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