在 Linux 可以用 gdb 来调试应用程序,当然前提是用 gcc 编译程序时要加上 -g 参数。我这篇文章里将讨论一下用 gdb 来调试动态链接库的问题。

    首先,假设我们准备这样的一个动态链接库:
    引用库名称是: ggg
    动态链接库文件名是: libggg.so
    头文件是: get.h ,提供这样两个函数调用接口:

    1. int get ();
    2. int set (int a);

    要生成这样一个动态链接库,我们首先编写这样一个头文件:

    1. /************关于本文档********************************************
    2. *filename: get.h
    3. *********************************************************************/
    4. int get ();
    5. int set (int a);

    然后准备这样一个生成动态链接库的源文件:

    1. /************关于本文档********************************************
    2. *filename: get.c
    3. *********************************************************************/
    4. #include <stdio.h>
    5. #include "get.h"
    6. static int x=0;
    7. int get ()
    8. {
    9. printf ("get x=%d\n", x);
    10. return x;
    11. }
    12. int set (int a)
    13. {
    14. printf ("set a=%d\n", a);
    15. x = a;
    16. return x;
    17. }

    然后我们用 GNU 的 C/C++ 编译器来生成动态链接库,编译命令如下(gcc 编译程序时要加上 -g 参):

    1. gcc get.c -shared -g -DDEBUG -o libggg.so
    2. gcc get.c -shared -g -fPIC -DDEBUG -o libggg.so (64位机器)

    这样我们就准备好了动态链接库了,下面我们编写一个应用程序来调用此动态链接库,源代码如下:

    1. /************关于本文档********************************************
    2. *filename: pk.c
    3. *********************************************************************/
    4. #include <stdio.h>
    5. #include "get.h"
    6. int main (int argc, char** argv)
    7. {
    8. int a = 100;
    9. int b = get ();
    10. int c = set (a);
    11. int d = get ();
    12. printf ("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d);
    13. return 0;
    14. }

    编译此程序。

    如果已经把上面生成的 libggg.so 放到了库文件搜索路径指定的文件目录,比如 /lib 或 /usr/lib 之类的,就用下面这条命令:

    1. gcc pk.c -o app -Wall -g -lggg

    否则就用下面这条命令:

    1. gcc pk.c -o app -Wall -g -lggg -L\`pwd\`

    下面我们就开始调试上面命令生成的 app 程序吧。如果已经把上面生成的 libggg.so 放到了库文件搜索路径指定的文件目录,比如 /lib 或 /usr/lib 之类的,调试就顺利完成,如下:

    1. #gdb ./app
    2. GNU gdb 6.4-debian
    3. Copyright 2005 Free Software Foundation, Inc.
    4. GDB is free software, covered by the GNU General Public License, and you are
    5. welcome to change it and/or distribute copies of it under certain conditions.
    6. Type "show copying" to see the conditions.
    7. There is absolutely no warranty for GDB. Type "show warranty" for details.
    8. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
    9. (gdb) b main /* 这是在程序的 main 处设置断点 */
    10. Breakpoint 1 at 0x804853c: file pk.cpp, line 7.
    11. (gdb) b set /* 这是在程序的 set 处设置断点 */
    12. Function "set" not defined.
    13. Make breakpoint pending on future shared library load? (y or [n]) y /* 这里必须选择 y 调试程序才会跟踪到动态链接库内部去 */
    14. Breakpoint 2 (set) pending.
    15. (gdb) run /* 开始运行我们的程序,直到遇见断点时暂停 */
    16. Starting program: /data/example/c/app
    17. Breakpoint 3 at 0xb7f665f8: file get.cpp, line 11.
    18. Pending breakpoint "set" resolved
    19. Breakpoint 1, main (argc=1, argv=0xbf990504) at pk.cpp:7
    20. 7 int a = 100;
    21. (gdb) n /* 继续执行程序的下一行代码 */
    22. 8 int b = get ();
    23. (gdb) n /* 程序执行到了我们断点所在的动态链接库了 */
    24. get x=0
    25. 9 int c = set (a);
    26. (gdb) n
    27. Breakpoint 3, set (a=100) at get.cpp:11
    28. 11 printf ("set a=%d\n", a);
    29. (gdb) list /* 查看当前代码行周围的代码,证明我们已经跟踪到动态链接库的源代码里面了 */
    30. 6 printf ("get x=%d\n", x);
    31. 7 return x;
    32. 8 }
    33. 9 int set (int a)
    34. 10 {
    35. 11 printf ("set a=%d\n", a);
    36. 12 x = a;
    37. 13 return x;
    38. 14 }
    39. (gdb) n
    40. set a=100
    41. 12 x = a;
    42. (gdb) n
    43. 13 return x;
    44. (gdb) n
    45. 14 }
    46. (gdb) n
    47. main (argc=1, argv=0xbf990504) at pk.cpp:10
    48. 10 int d = get ();
    49. (gdb) n
    50. get x=100
    51. 11 printf ("a=%d,b=%d,c=%d,d=%d\n",a,b,c,d);
    52. (gdb) n
    53. a=100,b=0,c=100,d=100
    54. 12 return 0;
    55. (gdb) c
    56. Continuing.
    57. Program exited normally.
    58. (gdb) quit /* 程序顺利执行结束 */

    如果我们没有把动态链接库放到指定目录,比如 /lib 里面,调试就会失败,过程如下:

    1. # gdb ./app
    2. GNU gdb 6.4-debian
    3. Copyright 2005 Free Software Foundation, Inc.
    4. GDB is free software, covered by the GNU General Public License, and you are
    5. welcome to change it and/or distribute copies of it under certain conditions.
    6. Type "show copying" to see the conditions.
    7. There is absolutely no warranty for GDB. Type "show warranty" for details.
    8. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
    9. (gdb) b main
    10. Breakpoint 1 at 0x804853c: file pk.cpp, line 7.
    11. (gdb) b set
    12. Function "set" not defined.
    13. Make breakpoint pending on future shared library load? (y or [n]) y
    14. Breakpoint 2 (set) pending.
    15. (gdb) run /* 虽然调试操作都一样,但程序执行失败 */
    16. Starting program: /data/example/c/app
    17. /data/example/c/app: error while loading shared libraries: libggg.so: cannot open shared object file: No such file or directory
    18. Program exited with code 0177.
    19. (gdb) quit

    调试失败的原因是因为 gdb 不能找到 libggg.so,可以通过下面的方法解决:

    1. 将库路径加到 LD_LIBRARY_PATH 里
    2. 执行:ldconfig YOUR_LIB_PATH
    3. 在 / 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