12

我正在尝试调试一个使用 GDB 从多个共享库构建的应用程序。

gdb 的开始:

prompt$ gdb
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-50.el6)
Copyright (C) 2010 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-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.

告诉 GDB 程序要调试:

(gdb) file /home/me/build-path/my-program
Reading symbols from /home/me/build-path/my-program...done.

在应用程序中设置断点:

(gdb) my-program-src.cpp:57
breakpoint 1 at 0x819df9b: file src/my-program-src.cpp, line 57

运行程序:

(gdb) run 
 Starting program: /home/me/build-path/my-program

程序在断点处停止,正如预期的那样:

 Breakpoint 1 MyClass:func(this-0xffffc1c0) at src/my-program-src.cpp:235

第 235 行my-program-src.cpp是一个构造函数调用,class DerivedMySharedLib1.so.

'class Derived' 派生自 'class Base',它位于MySharedLib2.so

如果我现在迈出一步,程序会在 `MySharedLib2.so' 中退出,并带有一个 SIG SEGV(这是我要调试的),即:

Program received signal SIGSEGV, Segmentation fault.
0x0024c2fa in osal::MsgQMsg::id(unsigned int) () from /home/me/build-path/lib/libMySharedLib2.so

GDB 没有进入任何一个共享库。

bt给出发生问题的函数的名称,但list显示代码my-program-src.cpp

所有代码都使用以下选项编译:

gcc -MD -D__LINUX__  -g -Wall -Wextra -Iinc -m32 -fpic -I../../public_inc /home/me/src/file.c -o /home/me/build-path/obj/file.o

共享库与以下选项链接:

gcc -o /home/me/build-path/lib/libMySharedLib1.so -shared /home/me/build-path/obj/file.o -L/home/me/build-path/lib/ -m32

如果我更改 Makefile 以便构建档案库(即.a),我可以按预期进入功能。

更多信息:

如果我手动尝试从共享库中添加符号,我会得到以下信息:

(gdb) add-symbol-file  /home/me/build-path/lib/libMySharedLib2.so
The address where /home/me/build-path/lib/libMySharedLib2.so has been loaded is missing

add-symbol-file(注意:一旦断点被​​击中,我会得到相同的响应)

如果我可以在共享库中的函数中设置断点,GDB 会按预期中断,但如果我键入listGDB,则会在主应用程序代码中显示调用行(即不在共享库中的调用函数)。GDB 不会抱怨找不到源文件。

为什么我不能进入我的共享库?

为什么我不能单步执行共享库中的代码?

4

3 回答 3

5

如果共享库没有调试符号/信息,gdb 默认会跳过它而不是进入它。您可以使用stepi(abbrev si) 来代替单条指令。我发现 .gdbinit 文件中的以下命令很有用:

define sx
  si
  x /1i $pc
end
document sx
    Step one instruction and print next instruction
end
define nx
  ni
  x /1i $pc
end
document nx
    Step one instruction running through calls and print next instruction
end

它定义了命令sx/nx以单条指令逐步执行,然后反汇编要运行的下一条指令。在尝试通过没有调试信息的机器代码进行调试时非常有用。

于 2019-12-14T00:36:21.760 回答
2

这可能是一个错字,但您在尝试加载符号时使用libMySharedLib2.so2not a 。1

在任何情况下,您都应该使用g++来编译和链接 c++ 代码。此外,主程序不必是图片,尽管它可能不会受到伤害。

它对我有用,如下所示:

$ cat >lib.h
class Base
{
        int _x;
    public:
        Base(int);
};

class Derived : public Base
{
    public:
        Derived(int x);
};
$ cat >lib.cpp
#include "lib.h"

Base::Base(int x)
{
    _x = *reinterpret_cast<int*>(x);
}

Derived::Derived(int x) : Base(x)
{
}
$ cat >main.cpp
#include "lib.h"

int main(int, char**)
{
    Derived d(0);
    return 0;
}
$ g++ -shared -fpic -m32 -g -Wall -o libMySharedLib1.so lib.cpp
$ g++ -m32 -g -Wall -L. -l MySharedLib1 main.cpp
$ LD_LIBRARY_PATH=$PWD gdb ./a.out
GNU gdb (GDB) 7.3.50.20111117-cvs-debian
Copyright (C) 2011 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".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from a.out...done.
(gdb) r
Starting program: a.out     

Program received signal SIGSEGV, Segmentation fault.
0xf7fdb552 in Base::Base (this=0xffffd83c, x=0) at lib.cpp:5
5           _x = *reinterpret_cast<int*>(x);
(gdb) bt
#0  0xf7fdb552 in Base::Base (this=0xffffd83c, x=0) at lib.cpp:5
#1  0xf7fdb5ba in Derived::Derived (this=0xffffd83c, x=0) at lib.cpp:8
#2  0x08048591 in main () at main.cpp:5
(gdb) br main
Breakpoint 1 at 0x804857d: file main.cpp, line 5.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: a.out     

Breakpoint 1, main () at main.cpp:5
5           Derived d(0);
(gdb) s
Derived::Derived (this=0xffffd83c, x=0) at lib.cpp:8
8       Derived::Derived(int x) : Base(x)
(gdb) s
Base::Base (this=0xffffd83c, x=0) at lib.cpp:5
5           _x = *reinterpret_cast<int*>(x);

(gdb 输出略有编辑)

于 2012-12-19T22:30:27.087 回答
1

这是使用未使用调试语句编译的库时通常遇到的问题。通常,gdb 将跳过这些函数,并且回溯将仅在库调用的加载内存中提供一个位置。这是因为没有这些符号,gdb 就没有可映射的内容。

如果您有库本身的源代码,请在激活调试标志的情况下重新编译它。根据您正在调试的内容,这可能会将时序伪影引入您的分析中。例如,如果您正在调试数据流入的某个缓冲区中的段错误,则符号的存在将引入足够的延迟以使缓冲区正确填充。

如果您没有源代码,这将非常具有挑战性,但在许多情况下并非不可能。如果库没有被剥离,回溯确实提供了足够的信息来了解发生错误时哪些函数正在执行。有了这个,您需要对执行进行逆向工程,以便您可以查看可用的任何源文件。根据您对 ISA 的熟悉程度,您可以通过查看组装说明自己梳理其中的一些细节。

最坏的情况是如果库被完全剥离。这是一个配对所有已编译代码的过程,以便删除其中的所有人类可读(真正的文本字符串)引用。即使在没有调试语句的情况下,编译器仍然会留下带有函数名称的标签,但strip会用唯一的整数替换那些标签(它会留下其他东西,但我想你可以理解我在这里的意思)。在这种情况下,gdb 甚至无法解析回溯中的函数名。这就是您对 ISA 的理解需要大放异彩的地方,而且您首先必须通过严格的逆向工程项目,然后才能解决错误本身的原因。

在主要的麻烦类中(即您没有源代码),您需要问自己一个重要问题:我是否负责修复不是我的代码的部分?在大多数情况下,专注于确保错误是可重现的并提交错误报告;让该库的维护者弄清楚。如果您仍然负责修复整体错误,请专注于您可以影响的内容并黑盒您无法影响的内容;查看是否需要以可以适当防止或处理错误的方式限制对这些库调用的输入。

于 2019-12-19T18:41:46.183 回答