在 Linux 可以用 gdb 来调试应用程序,当然前提是用 gcc 编译程序时要加上 -g 参数。我这篇文章里将讨论一下用 gdb 来调试动态链接库的问题。
首先,假设我们准备这样的一个动态链接库:
引用库名称是: ggg
动态链接库文件名是: libggg.so
头文件是: get.h ,提供这样两个函数调用接口:
int get ();int set (int a);
要生成这样一个动态链接库,我们首先编写这样一个头文件:
/************关于本文档*********************************************filename: get.h*********************************************************************/int get ();int set (int a);
然后准备这样一个生成动态链接库的源文件:
/************关于本文档*********************************************filename: get.c*********************************************************************/#include <stdio.h>#include "get.h"static int x=0;int get (){printf ("get x=%d\n", x);return x;}int set (int a){printf ("set a=%d\n", a);x = a;return x;}
然后我们用 GNU 的 C/C++ 编译器来生成动态链接库,编译命令如下(gcc 编译程序时要加上 -g 参):
gcc get.c -shared -g -DDEBUG -o libggg.sogcc get.c -shared -g -fPIC -DDEBUG -o libggg.so (64位机器)
这样我们就准备好了动态链接库了,下面我们编写一个应用程序来调用此动态链接库,源代码如下:
/************关于本文档*********************************************filename: pk.c*********************************************************************/#include <stdio.h>#include "get.h"int main (int argc, char** argv){int a = 100;int b = get ();int c = set (a);int d = get ();printf ("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d);return 0;}
编译此程序。
如果已经把上面生成的 libggg.so 放到了库文件搜索路径指定的文件目录,比如 /lib 或 /usr/lib 之类的,就用下面这条命令:
gcc pk.c -o app -Wall -g -lggg
否则就用下面这条命令:
gcc pk.c -o app -Wall -g -lggg -L\`pwd\`
下面我们就开始调试上面命令生成的 app 程序吧。如果已经把上面生成的 libggg.so 放到了库文件搜索路径指定的文件目录,比如 /lib 或 /usr/lib 之类的,调试就顺利完成,如下:
#gdb ./appGNU gdb 6.4-debianCopyright 2005 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".(gdb) b main /* 这是在程序的 main 处设置断点 */Breakpoint 1 at 0x804853c: file pk.cpp, line 7.(gdb) b set /* 这是在程序的 set 处设置断点 */Function "set" not defined.Make breakpoint pending on future shared library load? (y or [n]) y /* 这里必须选择 y 调试程序才会跟踪到动态链接库内部去 */Breakpoint 2 (set) pending.(gdb) run /* 开始运行我们的程序,直到遇见断点时暂停 */Starting program: /data/example/c/appBreakpoint 3 at 0xb7f665f8: file get.cpp, line 11.Pending breakpoint "set" resolvedBreakpoint 1, main (argc=1, argv=0xbf990504) at pk.cpp:77 int a = 100;(gdb) n /* 继续执行程序的下一行代码 */8 int b = get ();(gdb) n /* 程序执行到了我们断点所在的动态链接库了 */get x=09 int c = set (a);(gdb) nBreakpoint 3, set (a=100) at get.cpp:1111 printf ("set a=%d\n", a);(gdb) list /* 查看当前代码行周围的代码,证明我们已经跟踪到动态链接库的源代码里面了 */6 printf ("get x=%d\n", x);7 return x;8 }9 int set (int a)10 {11 printf ("set a=%d\n", a);12 x = a;13 return x;14 }(gdb) nset a=10012 x = a;(gdb) n13 return x;(gdb) n14 }(gdb) nmain (argc=1, argv=0xbf990504) at pk.cpp:1010 int d = get ();(gdb) nget x=10011 printf ("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d);(gdb) na=100,b=0,c=100,d=10012 return 0;(gdb) cContinuing.Program exited normally.(gdb) quit /* 程序顺利执行结束 */
如果我们没有把动态链接库放到指定目录,比如 /lib 里面,调试就会失败,过程如下:
# gdb ./appGNU gdb 6.4-debianCopyright 2005 Free Software Foundation, Inc.GDB is free software, covered by the GNU General Public License, and you arewelcome to change it and/or distribute copies of it under certain conditions.Type "show copying" to see the conditions.There is absolutely no warranty for GDB. Type "show warranty" for details.This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".(gdb) b mainBreakpoint 1 at 0x804853c: file pk.cpp, line 7.(gdb) b setFunction "set" not defined.Make breakpoint pending on future shared library load? (y or [n]) yBreakpoint 2 (set) pending.(gdb) run /* 虽然调试操作都一样,但程序执行失败 */Starting program: /data/example/c/app/data/example/c/app: error while loading shared libraries: libggg.so: cannot open shared object file: No such file or directoryProgram exited with code 0177.(gdb) quit
调试失败的原因是因为 gdb 不能找到 libggg.so,可以通过下面的方法解决:
- 将库路径加到 LD_LIBRARY_PATH 里
- 执行:ldconfig YOUR_LIB_PATH
- 在 / etc/ld.so.conf 里加入库所在路径。然后执行:ldconfig
上面 3 个方法任意一个都可以,然后再去用 gdb 调试就没有问题了。
另:
1、假设我的可执行程序是 ServerName,共享库为 worker.so
2、我用 gdb 调试 ServerName,想在 B 的某个源文件(比如 worker.cpp,worker.cpp 与 ServerName 不在同一个目录下)中设置断点,使用下面的命令行 break worker.cpp:123
若找不到源文件可使用如下命令设定源文件目录:
设定 gdb 环境变量 LD_PRELOAD,在执行程序前先把共享库代码 load 进来
指定你的链接库的位置,可以通过设定环境变量 LD_LIBRARY_PATH 来实现
拷贝到标准的 lib 搜寻目录下,例如 /usr/lib 等
b main, r, 然后再设置断点就可以了,共享库只有当程序运行才开始加载的
原文:https://www.cnblogs.com/ybgame/archive/2012/03/23/2414078.html
