【应用】Linux内存调试工具:gdb+gdbserver
一、测试环境:
运行环境:server: arm64 linux-4.14(开发板)
host: x86_64 ubuntu-20.04(主机)
工具版本:gdb-10.1
二、工具自我介绍:
看看官方怎么说,
GDB, the GNU Project debugger, allows you to see what is going on `inside' another program while it executes -- or what another program was doing at the moment it crashed.
GDB can do four main kinds of things (plus other things in support of these) to help you catch bugs in the act:
- Start your program, specifying anything that might affect its behavior.
- Make your program stop on specified conditions.
- Examine what has happened, when your program has stopped.
- Change things in your program, so you can experiment with correcting the effects of one bug and go on to learn about another.
Those programs might be executing on the same machine as GDB (native), on another machine (remote), or on a simulator. GDB can run on most popular UNIX and Microsoft Windows variants, as well as on Mac OS X.
gdb 工具可谓是应用开发过程中最常用的伙伴了,解决执行崩溃问题那是手到擒来。可以用它来分析程序崩溃后产生的 coredump 文件,找出崩溃的原因;也可以用它来单步调试程序,详细的分析程序运行过程中的第一个元素的变化。
coredump 方式相对来说比较简单,我一般的做就是:
1)配置 coredump 相关,如大小、附加 pid、生成名称路径等
ulimit -c unlimited
echo "1" > /proc/sys/kernel/core_uses_pid
echo "/tmp/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
2)配置库路径、查看栈回溯、查看反汇编等
set solib-search-path
info sharedlibrary
bt
disassemble
objdump –S
本篇还是以 gdbserver 为主,演示一下如何在主机上远程调试开发板上的程序。
三、工具下载与编译安装:
1. 下载
进入官网即可通过 http 或 ftp 即可下载最新版工具。
2. 编译
./configure --prefix=/tools/gdb/tmp/ CC=aarch64-linux-gnu-gcc --host=aarch64-linux-gnu LDFLAGS=-static
make -j24
尝试进行静态链接,这样的话 server 端就可以尽情的在远程运行,不依赖任何的库。
但结果不尽人意,链接时会有类似这样的提示: warning: Using 'getpwuid' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking ,大概意思是说,getpwuid 是在一个动态库里,你想静态链接这个程序是可以,但你在运行的时候要给它提供这个动态库。
关于这个问题,我找到一些讨论,这是一个比较有说服力的:
glibc uses libnss to support a number of different providers for address resolution services. Unfortunately, you cannot statically link libnss, as exactly what providers it loads depends on the local system's configuration.
然而这里可能大家可能有一个疑问,难道运行环境中没有这个动态库,程序就不能正常运行了?这个问题我们暂留到实验后讨论。
3. 安装
make install
一条命令,将可执行程序等安装至预设的路径,查看下成果物,
root : /tools/gdb# ls tmp/bin/
gcore gdb gdb-add-index gdbserver run
可以,齐全了,将 gdbserver 拷贝至开发板端,开始测试。
四、实际调试
准备一个例程,就使用上面那个 API 吧,,
1 #include <sys/types.h>
2 #include <stdio.h>
3 #include <pwd.h>
4
5 int main()
6 {
7 struct passwd *info = NULL;
8
9 info = getpwuid(getuid());
10 printf("Name: %s\n", info->pw_name);
11
12 return 0;
13 }
在主机上运行一下,得到如下输出:
Name: root
OK,程序正常,交叉编译静态链接再传到开发板运行,结果:
/ # ./getname
Segmentation fault
What ?,别急有 gdb,让我们来挣扎一下,用 gdb 分析下崩溃的原因,注意主机侧的程序编译时要加 -g 参数,以打入调试信息。
开发板端启动 gdbserver: ./gdbserver 10.25.206.228:1233 ./getname
主机侧进行连接并调试: aarch64-linux-gnu-gdb getname
在 main 处设个断点,单步运行,查看是不是 api 未定义或者是返回值是否为空,
(gdb) b main
Breakpoint 1 at 0x400b48: file test.c, line 7.
(gdb) c
Continuing.
Breakpoint 1, main () at test.c:7
7 struct passwd *info = NULL;
(gdb) n
9 info = getpwuid(getuid());
(gdb) n
10 printf("Name: %s\n", info->pw_name);
(gdb) p info
$1 = (struct passwd *) 0x0
哇,果然,getpwuid 返回了空指针,猜想一下应该是我的文件系统中没有登入用户导致的:
/ # whoami
whoami: unknown uid 0
到这里就用 gdb 完成了一次小型崩溃现场调试。
其实实验结束后也得出了上面那个猜想的结论,我在交叉编译时所用的命令是: aarch64-linux-gnu-gcc -static -g -o getname test.c ,编译器提示:
/tmp/ccCucfXo.o: In function `main':
/tools/gdb/test.c:9: warning: Using 'getpwuid' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
而且开发板的运行环境中也并没有 nss 相关的动态库:
/ # find /lib -name "*nss*"
/ # find /usr/lib -name "*nss*"
这一点是不是可以证明,虽然静态链接时有那个警告,但在实际的运行过程中不会去依赖相关的动态库了?其实不然,关于这个问题我深入到 glibc 简单的了解了一下流程,具体见下一篇。
简单使用和疑问解决到这里告一段落,后续深入再补充。
文末福利:GDB命令大全