如何在gdb堆栈跟踪充满'??'时调试分段错误?

时间:2010-03-10 17:30:29

标签: c++ linux coredump

我的可执行文件包含符号表。但似乎堆栈跟踪已被破坏。

如何从该核心获取更多信息?例如,有没有办法检查堆?查看填充堆的对象实例以获取一些线索。无论如何,任何想法都会受到赞赏。

8 个答案:

答案 0 :(得分:14)

我是一名生活中的C ++程序员,我遇到过这个问题的次数比我想承认的要多。您的应用程序正在粉碎堆栈中的巨大部分。机会是破坏堆栈的功能也会在返回时崩溃。原因是因为返回地址已被覆盖,这就是GDB的堆栈跟踪混乱的原因。

这是我调试此问题的方法:

1)逐步通过应用程序直到它崩溃。 (查找返回时崩溃的函数。)

2)确定函数后,在函数的第一行声明一个变量:

int canary=0;

(它必须是第一行的原因是该值必须位于堆栈的最顶层。这个“canary”将在函数的返回地址之前被覆盖。)

3)在金丝雀上放一个变量值表,通过函数和金丝雀!= 0,然后你发现你的缓冲区溢出了!另一种可能是它为canary!= 0设置变量断点并且只是正常运行程序,这有点容易,但不是所有IDE的支持变量断点。

编辑:我已经和我办公室的高级程序员交谈,以了解解析内存地址所需的核心转储。找出这些地址的一种方法是查看二进制文件的MAP文件,该文件是人类可读的。以下是使用gcc生成MAP文件的示例:

gcc -o foo -Wl,-Map,foo.map foo.c

这是一个难题,但仍然很难获得崩溃的函数的地址。如果您在现代平台上运行此应用程序,那么ASLR可能会使核心转储中的地址无效。 ASLR的一些实现将随机化二进制的函数地址,这使得核心转储绝对毫无价值。

答案 1 :(得分:4)

  1. 你必须使用一些调试器来检测,valgrind是好的
  2. 在编译代码时确保添加-Wall选项,它会使编译器告诉您是否存在某些错误(确保您的代码中有任何警告)。
  3. ex:gcc -Wall -g -c -o oke.o oke.c
    3.确保您还具有-g选项以生成调试信息。您可以使用某些宏调用调试信息。以下宏对我非常有用:

    __LINE__:告诉你

    __FILE__:告诉你源文件

    __func__:告诉你功能

    1. 我认为使用调试器是不够的,你应该习惯于最大化编译器的能力。
    2. 希望这会有所帮助

答案 2 :(得分:2)

TL; DR:函数中非常大的局部变量声明在堆栈上分配,在某些平台和编译器组合中,它可能会溢出并损坏堆栈。

只是为此问题添加另一个潜在原因。我最近调试了一个非常类似的问题。使用应用程序和核心文件运行gdb将产生如下结果:

Core was generated by `myExecutable myArguments'.
Program terminated with signal 6, Aborted.
#0  0x00002b075174ba45 in ?? ()
(gdb)

这是非常无益和令人失望的。经过几个小时的互联网搜索,我找到了一个论坛,讨论了我们使用的特定编译器(英特尔编译器)如何比其他编译器具有更小的默认堆栈大小,并且大型局部变量可能会超出并损坏堆栈。看看我们的代码,我找到了罪魁祸首:

void MyClass::MyMethod {
   ...
   char charBuffer[MAX_BUFFER_SIZE];
   ...

}

宾果!我发现MAX_BUFFER_SIZE设置为10000000,因此在堆栈上分配了 的10MB局部变量! 在更改实现以使用shared_ptr并动态创建缓冲区后,突然间程序开始完美运作。

答案 3 :(得分:1)

尝试使用Valgrind内存调试器运行。

答案 4 :(得分:1)

要确认,您的可执行文件是否在发布模式下编译,即没有调试符号....这可以解释为什么会有?尝试使用-g开关重新编译,其中包含调试信息并将其嵌入可执行文件中。除此之外,我不知道为什么你有'??'...

答案 5 :(得分:1)

不是真的。当然,你可以在记忆中挖掘并看看事物。但是如果没有堆栈跟踪,你就不知道你到底在哪里或参数值是什么。

但是,堆栈损坏这一事实告诉您需要查找写入堆栈的代码。

  • 覆盖堆栈数组。这可以通过显而易见的方式完成,也可以通过调用带有错误大小参数的函数或系统调用或错误类型的指针来完成。
  • 在该函数返回后使用指针或对函数的本地堆栈变量的引用。
  • 将指向堆栈值的指针强制转换为错误大小的指针并使用它。

如果你有一个Unix系统,“valgrind”是找到这些问题的好工具。

答案 6 :(得分:0)

我假设您说“我的可执行文件包含符号表”,您使用-g编译和链接,并且您的二进制文件未被剥离。

我们可以确认一下: strings -a | grep function_name_you_know_should_exist

还尝试在核心上使用pstack,看看它是否能更好地获取callstack。在这种情况下,听起来你的gdb与你的gcc / g ++版本相比已经过时了。

答案 7 :(得分:0)

听起来你没有在你的机器上使用相同的glibc版本,就像核心文件在生产时崩溃一样。获取“ldd ./appname”输出的文件并将它们加载到您的机器上,然后告诉gdb在哪里查看;

set solib-absolute-prefix /path/to/libs