符号表是什么,它如何集成到可执行文件中?

19

当我尝试调试一个可执行文件时:

(gdb) break +1
No symbol table is loaded.  Use the "file" command.

那到底是什么意思呢?

符号表是否附加到可执行文件中?

4个回答

33

gdb使用两组符号。

-g是调试符号,有了它会使得调试代码和查看变量变得更加容易。

另一组符号在编译时默认被包含。这些是链接符号,存储在ELF(可执行链接格式)符号表中。相比于调试符号,这些符号仅包含最重要的信息,如可执行文件(或库或对象文件)中各个元素的地址。如果没有这些信息,gdb甚至都不知道main函数在哪里,所以(gdb) break main的断点设置会失败。

如果你没有调试符号(-g),那么你仍然可以(gdb) break main,但是gdb将无法理解源文件中的代码行。当你尝试逐行调试代码时,每次只能前进1条机器指令而不是一行代码。

strip命令通常用于从可执行文件(或其他对象文件)中削减符号。如果你不想让别人看到符号或想要节省文件空间,那么就经常使用这个命令。符号表可能很大。strip将删除调试符号和链接符号,但它有几个命令行开关可以限制它删除的内容。

如果你在程序上运行file命令,则其中一个输出将告诉你可执行文件是否已剥离符号。

$ gcc my_prog.c -o my_prog
$ file my_prog
my_prog: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped
$ strip my_prog
my_prog: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, stripped
$

你确定链接器符号(.dynsym,对吧?)会被 strip 命令删除吗?根据这篇文章,它们不会被删除。 - Alexander Malakhov
2
“strip”有选项可以控制哪些符号需要被删除,哪些不需要。默认行为通常不会破坏程序。 - nategoose
@nategoose 能否告诉我们调试符号是否包括 ELF 符号表中的符号。换句话说,当应用程序使用 -g 编译时,GDB 是否仅使用调试符号? - Manuel Selva
2
@ManuelSelva ELF格式是与DWARF调试符号格式一起生成的,与许多旧的目标文件格式不同,它内置了对调试符号的支持。旧的目标文件格式通过滥用格式的其他功能来添加调试符号设施。-g标志意味着“请提供调试符号”对于许多编译器来说是必须的,但是他们可以在没有该标志的情况下添加符号或者不生成它们。这些符号包含信息,例如“代码地址=>(函数:行号)”,以及“在这个代码地址上,X的最新值在这个寄存器中”。 - nategoose

8

这是因为你没有开启调试编译。尝试使用 gcc -g file.c 进行编译。


你的意思是gdb不能用来调试任意程序吗? - user198729
2
@user198729:你可以使用GDB来调试任意程序,但如果程序没有符号表,你需要付出更多的努力。基本上,符号表告诉调试器有关函数、变量、行号和源文件的信息 - 如果缺失了这些信息,你只剩下汇编代码了。 - Jonathan Leffler
@Jonathan Leffler,看起来您的意思是即使程序没有符号表,仍然可以调试它,您能举个例子吗? - user198729
1
我是说这非常难 - 难到我都不想尝试。但有些人可以使用汇编语言工作并调试任意程序。我认为他们疯了,但在某些方面他们可能也比我更优秀 - 至少如此。你最终会一条汇编指令一条汇编指令地逐步执行代码。 - Jonathan Leffler

4
符号表包含调试信息,告诉调试器哪些内存位置对应原始源代码文件中的哪些符号(如函数名和变量名)。符号表通常存储在可执行文件中。
gdb告诉您它找不到该表。如果您使用gcc编译,除非使用了-g标志,否则不会将符号表包含在文件中。最简单的方法可能是使用-g重新编译文件。然后,gdb应自动查找符号表信息。
要么将-g标志添加到gcc的命令行参数中,要么将其添加到用于编译程序的Makefile中。(很多时候,在Makefile中会有一个名为CFLAGS或类似名称的变量)。
如果您正在尝试调试任意第三方程序,则很多时候信息将被“剥离”出来。这样做是为了使反向工程更加困难,并使可执行文件的大小更小。除非您可以访问源代码并可以自己编译程序,否则在其上使用gdb将非常困难。

为什么像SoftICE这样的其他工具可以调试任意程序,它们不是使用与GDB相同的技术吗? - user198729
大致上使用相同的技术,是的。您可以使用gdb执行类似的操作,例如在系统调用上设置断点、检查汇编代码、查看寄存器值或特定内存地址的内容,即使没有符号表也可以。如果您使用Linux,还可以尝试使用strace或ltrace。 - RarrRarrRarr
但无论我键入什么命令,gdb总是会回复“未加载符号表。使用“文件”命令。”似乎不可能在没有符号表的情况下调试程序。或者是我忽略了什么? - user198729
1
@user198729:没有符号表,你就无法进行符号调试。在创建目标文件和链接时,必须包含正确的编译选项 - 大多数情况下,都是“-g”用于对象生成和链接。您还不能指定“-s”(剥离),也不能将程序提交给“strip”。 - Jonathan Leffler
1
@Jonathan Leffler,您能否设计一个没有符号表的helloworld演示程序来调试程序? - user198729

3
找到应用程序的入口点。
objdump -f main

    main:     file format elf32-i386
    architecture: i386, flags 0x00000112:
    EXEC_P, HAS_SYMS, D_PAGED
    start address 0x08048054

使用GNU调试器在那里设置一个断点

gdb

    exec-file main
    break *0x8048054
    set disassemble-next-line on
    run

然后逐步执行代码。
gdb

    stepi

特别说明

如果您正在使用最新版本的Ubuntu,则不受此影响,但如果您正在运行Ubuntu 10.04或更早版本,则可能遇到此错误。

https://bugs.launchpad.net/ubuntu/+source/gdb/+bug/151518G

解决方案是从入口地址加一开始调试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接