文章目录
调试信息
调试信息是在编译或构建程序时生成的额外数据,用于辅助开发者在调试过程中理解代码的执行流程和状态。这部分信息不直接影响程序的功能,但在调试阶段至关重要。调试信息通常包含以下内容:
- 源代码行号:对应每段可执行代码的源代码位置,使得调试器能够在源代码中定位到具体执行点。
- 局部变量和函数参数的名字及类型:允许在调试时查看和修改变量的值。
- 源代码路径:帮助调试器找到原始源文件,以便显示代码上下文。
- 编译器宏定义:有助于理解代码中的条件编译部分。
- 程序的静态结构信息:如类、结构体、枚举等的定义,有助于理解数据结构。
通过这些信息,调试器能够提供丰富的上下文视图,使得开发者能够逐步执行代码、设置断点、查看和修改变量值,从而追踪错误根源。
符号表(Symbol Table)
符号表是编译器或汇编器在编译期间创建的数据结构,它存储了程序中定义的所有符号(如变量名、函数名、类名等)及其对应的地址或内存位置。符号表的主要作用包括:
- 地址解析:在生成目标代码时,将源代码中的符号引用转换为目标代码中的实际地址。这对于链接过程特别重要,因为它帮助链接器解决外部引用。
- 调试辅助:提供给调试器使用,使得调试器能够根据符号名查找变量或函数的确切内存地址,进而查看或修改其值,或者跳转到特定函数的开始处执行。
- 错误报告:编译时的错误和警告信息通常会引用符号表中的符号名,提供更易理解的错误描述。
- 性能分析:在性能分析工具中,符号表帮助映射内存地址回源代码中的符号,便于理解哪些函数或代码块消耗了更多资源。
.symtab
和.dynsym
在ELF(Executable and Linkable Format)文件结构中,.symtab
(符号表)和.dynsym
(动态符号表)分别存储着不同用途的符号信息,下面通过例子来具体解释这两种符号表的差异。
.symtab
(符号表)
.symtab
包含了程序中所有的符号信息,无论是本地(局部)静态符号还是全局符号。这些符号信息在链接阶段用于解决符号引用,并且在运行时可能用于调试目的。这里,“本地静态符号”指的是只在当前编译单元内部可见的符号,而“全局符号”则是可以在整个程序范围内访问的符号。
例子:
假设有一个C程序,包含两个源文件:main.c
和helper.c
。在helper.c
中定义了一个全局函数void helper_function()
,并在main.c
中调用了它。在这个场景中:
helper_function
的符号信息会作为全局符号出现在.symtab
中,因为它是跨编译单元可见的。- 假设
main.c
中还有一个局部静态变量static int local_var;
,它的符号信息也会被加入到.symtab
,但是作为本地静态符号,仅在编译main.c
时可见,不会在链接后程序的其他地方暴露。
.dynsym
(动态符号表)
相比之下,.dynsym
只包含那些动态链接时需要的符号,比如动态库中导出的函数和全局变量。动态链接器(如Linux下的ld.so
)在程序启动时会使用.dynsym
来解析动态库中被程序引用的符号。
例子:
如果你的程序使用了像libm.so
(数学库)中的sin()
函数,那么sin
的符号信息就会出现在.dynsym
中,而不是.symtab
。这是因为sin
函数是由动态库提供的,只有在程序运行时动态链接器需要知道去哪里找到这个函数的实现。
总结
.symtab
是全面的,包含程序中的所有符号信息,既包括本地静态符号也包括全局符号,主要用于链接和调试。.dynsym
则更加精简,仅记录那些动态链接过程中需要用到的符号,通常这些符号属于动态链接库或者需要被其他共享对象链接的全局符号。
编译相关选项
https://workerwork.github.io/posts/sub-bin/
实际调试
查看是否有符号表或调试信息
readelf -S vuln | grep debug #查看是否有调试信息
readelf -s vuln #查看是否去除符号表
确实,.symtab
和 .dynsym
是ELF(Executable and Linkable Format,可执行与可链接格式)文件中的两个重要节(section),它们各自承担不同的角色,并共同作用于程序的链接与运行时符号解析过程。下面简要解释这两个节以及它们如何帮助我们查看 libc 或其他库中变量的类型。
-
.symtab(Static Symbol Table): 这是一个包含程序中所有符号定义和引用的完整符号表,无论是本地(local)符号、全局(global)符号还是外部(external)符号。它主要用于链接时的符号解析以及调试目的。
.symtab
包含了符号的名称、类型(如函数、对象、TLS等)、在文件中的地址以及符号的绑定属性(局部或全局)。静态链接器会使用这部分信息来解析未定义的符号引用。然而,在程序运行时,.symtab
并不直接用于动态链接,因此通常在生成最终发布版本的程序时通过strip
命令去除以减小文件大小。 -
.dynsym(Dynamic Symbol Table): 这个节只包含了动态链接所需的信息,即那些在运行时可能需要从共享库中解析的全局符号和外部符号。
.dynsym
不包括局部符号,因为它关注的是跨模块边界的符号查找。在程序执行期间,动态链接器会参考这个表来定位并加载共享对象中定义的符号。.dynsym
同样记录了符号的类型和地址,对于动态库特别重要。
去除符号表(.symtab
),没有调试信息
Debugging symbols,即调试符号,在编译程序时由编译器生成并嵌入到可执行文件或库文件中的一系列信息。它们主要用于提供源代码级别的调试信息,使得调试器(如GDB)能够:
- 显示源代码行号与指令之间的对应关系,让你知道每条机器指令对应于源代码的哪一行。
- 显示变量名称及其在内存中的位置,包括局部变量、全局变量及结构体成员等,这对于监视和修改变量值至关重要。
- 提供函数名和它们在内存中的地址,有助于设置断点、跟踪函数调用堆栈。
- 展示类型信息,这对于理解复杂数据结构和正确显示复杂类型(如结构体、类)的内容特别重要。
缺少调试符号时,虽然仍可以使用某些调试器加载并运行程序,但很多高级调试功能(如列出源代码、查看变量值等)将不可用或受限,使得调试过程更加困难。
去glibc-all-in-one 下载不同版本glibc二进制库和调试信息
libc6_2.31-0ubuntu9.15_amd64.deb
和 libc6-dbg_2.31-0ubuntu9.15_amd64.deb
都是Debian软件包格式的二进制包文件,也就是.deb
文件。它们包含了预编译的二进制代码、库文件、配置文件以及其他支持文件,旨在简化软件的安装和管理过程。用户无需从源代码编译,直接使用包管理器(如APT)就可以安装这些软件包。
-
libc6_2.31-0ubuntu9.15_amd64.deb
包含了GNU C Library(glibc)的共享库文件,这是许多Linux程序运行所必需的基础库。 -
libc6-dbg_2.31-0ubuntu9.15_amd64.deb
则包含了与glibc相关的调试符号信息,这对于开发和调试使用这些库的应用程序非常重要,因为它允许调试器(如GDB)提供详细的堆栈跟踪、变量值和源代码级别的调试信息。
下载到libs/2.31-0ubuntu9.15_amd64和libs/2.31-0ubuntu9.15_amd64/.debug中去了,需要显示隐藏文件才能看到
导入调试信息
感谢flyyy师傅指点了下,不然忘记了导入调试信息和导入查找源码路径的命令不同了,搞得我之前用dir导入了半天还是失败
show debug-file-directory
set debug-file-directory /home/llk/Desktop/tools/glibc-all-in-one/libs/2.35-0ubuntu3.7_amd64/.debug/
info share 再次查看发现导入成功
下载源码
https://mirrors.tuna.tsinghua.edu.cn/gnu/glibc/?spm=5176.28103460.0.0.4a8b3da2UN07VI
https://ftp.gnu.org/gnu/glibc/?spm=5176.28103460.0.0.4a8b3da2UN07VI
directory 源码路径
导入源码
当GDB或其他调试器报告缺少调试信息时,即使你有源码文件,调试器也不能在运行时准确地与执行的指令对应到源码行。没有调试信息,你可能只能看到汇编代码(通过disassemble
命令),而不能直接跳转到或查看具体的C源码行。