在进行嵌入式Linux应用程序开发时,经常会用到gdb对崩溃日志进行分析,一般情况下,可以直接定位到崩溃的位置。但有时分析core文件时,却看不到有意义的崩溃栈,这时问题就有点复杂了,出现这种现象的原因可能有这么几个:
- 应用程序在编译时没有指定-g选项,导致可执行程序没有调试信息。
- 应用程序所依赖的动态库和静态库没有调试信息。
- 应用程序的运行时环境没有调试信息,比如libc等。
- 嵌入式Linux系统应用程序运行时环境和交叉编译工具链的运行时环境版本不一致,比如,嵌入式Linux环境中所使用的libc库和交叉编译器所使用的libc库版本不一致。
对于1-3条所列出的问题,可以很容易的通过gdb提供的提示信息获得,比如,如果应用程序test编译时没有添加-g选项,当使用gdb调试时,会提示如下信息:
Reading symbols from /home/jetpack/test/test...(no debugging symbols found)...done.
对于动态库提示信息也是类似的。
今天,重点说一下,第四种情况。
下面以一个具体的场景来说明这个问题。
问题场景
第4条说的是,Linux系统运行时环境与交叉工具链环境不一致,这里就以libc中的strlen为例来说明问题。
strlen用于计算c字符串的长度,其中不包括字符串结束符'\0'。但是,strlen传入NULL指针时,会导致段错误。我们通过模拟这种场景来看一下,系统运行环境与编译环境不一致带来的问题。
下面是会导致崩溃的代码:
#include<string.h> int main(int argc, char* argv[]) { int rlen = strlen(NULL); return 0; } 注意:编译时指定-g,否则test没有调试信息。
嵌入式Linux运行环境libc信息:
root@zpd /lib$ ll libc.so.6 lrwxrwxrwx 1 root root 12 Jan 1 00:00 libc.so.6 -> libc-2.13.so root@zpd /lib$ ll libc-2.13.so -rwxr-xr-x 1 root root 1409189 Jan 1 22:08 libc-2.13.so
交叉编译器libc信息:
lrwxrwxrwx 1 jetpack jetpack 12 3月 9 12:07 libc.so.6 -> libc-2.13.so* -rwxr-xr-x 1 jetpack jetpack 1496962 3月 15 2012 libc-2.13.so*
将test编译之后,拷贝到嵌入式Linux环境中运行。注意,需要重新配置shell环境的ulimit关于core文件的限制,否则不会出现core文件。具体命令如下:
ulimit -c unlimited ./test Segmentation fault (core dumped)
将core文件拷贝到开发环境,使用gdb查看core文件信息。
1. Reading symbols from /home/jetpack/test/test...done. [New Thread 852] 2. warning: .dynamic section for "/home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/libc.so.6" is not at the expected address (wrong library or version mismatch?) Reading symbols from /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/libc.so.6...done. Loaded symbols for /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/libc.so.6 Reading symbols from /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/ld-linux.so.3...done. Loaded symbols for /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/ld-linux.so.3 Core was generated by `./test'. Program terminated with signal 11, Segmentation fault. #0 0x76e8d864 in tr_freehook () from /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/libc.so.6 (gdb) bt 3. #0 0x76e8d864 in tr_freehook () from /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/libc.so.6 #1 0x7ec97dd4 in ?? () #2 0x7ec97dd4 in ?? () Backtrace stopped: previous frame identical to this frame (corrupt stack?)
上面是gdb通过core得到的信息,注意下面几点:
- test加载成功,并且其有调试信息。
- warning: .dynamic section for "/home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/libc.so.6" is not at the expected address (wrong library or version mismatch?),说的是通过core文件的信息在交叉编译环境的libc中找不到.dynamic section,最后的结论是wrong library or version mismatch?即,库文件被损坏或者版本不一致。这是解决问题的关键。
- 这时如果通过bt,查看崩溃栈信息,会得到一些莫名其妙的信息,比如这里的tr_freehook。如果没有注意到2中提示的警告信息,可能会沿着这个错误的栈信息去查找问题原因,这时就会走很多的弯路,最终无功而返。
解决思路
通过第二节对于core的分析,可以看出问题的关键是运行环境和编译环境的libc版本库不一致导致的,那么解决问题的思路就明显了:
- 要么修改交叉编译的运行时环境。
- 要么修改运行时环境。
很明显,肯定是第2种思路,将交叉编译器的libc-2.13.so库拷贝到嵌入式Linux环境,替换之前的libc-2.13.so,再次运行test,得到core文件,gdb调试之,core文件信息如下:
1. Reading symbols from /home/jetpack/test/test...done. [New Thread 858] 2. Reading symbols from /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/libc.so.6...done. Loaded symbols for /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/libc.so.6 Reading symbols from /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/ld-linux.so.3...done. Loaded symbols for /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/ld-linux.so.3 Core was generated by `./test'. Program terminated with signal 11, Segmentation fault. 3. #0 0x76e5e864 in strlen () from /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/libc.so.6 (gdb) bt #0 0x76e5e864 in strlen () from /home/jetpack/work/OKMX6UL-C2/fsl-linaro-toolchain/arm-fsl-linux-gnueabi/multi-libs/lib/libc.so.6 #1 0x00008390 in main (argc=1, argv=0x7ef85dc4) at test.c:5
对照上面的1、2、3条,可以看到第2条可以正确的加载libc.so.6库;第3条可以看到由于崩溃位置在strlen。
总结
本文通过一个案例,简要说明了在嵌入式Linux应用开发时,由于运行时环境和交叉编译环境不一致导致的core文件分析失败的问题,并说明了如何解决这个问题。有的人会问,为什么会导致环境不一致呢?在构建嵌入式Linux环境时,肯定会涉及到根文件系统的构建过程,这时会将交叉编译器的环境整体拷贝到根文件系统中,这样的话就不会出现上述问题。是的,如果按照这个流程中应该不会有问题,但是,实际情况是,我们拿的是一个构建好的Linux系统,如果这时交叉编译工具链升级了,而嵌入式Linux运行时环境未升级,那问题就出现了。这时,就需要根据各种信息,来分析问题的根源了,这里就是gdb分析core时的一些警告信息。
所以,这里再次强调一下,开发过程中的任何警告信息都不能轻易放过,必须仔细分析,问题的解决线索往往蕴含其中。