GDB和ELF
关于GDB和ELF.
GDB
gdb是GNU项目的调试工具,允许程序员看到程序执行过程中内部正在发生的事情。gdb支持的语言有很多,绝不止C++一种,关于gdb的更多信息读者可以在其官方介绍中查阅。
.core文件
在debug环境下,linux系统上当软件异常退出时候,内核会在当前工作目录下生成一个core文件。使用gdb来查看core文件,可以帮助还原异常时候发生的一些情况,比如代码crash的文件和行数。
执行ulimit -c
可以查看当前系统生成.core文件的开关是否打开,如果返回0代表关闭状态。使用ulimit -c filesize
命令,可以限制core文件的大小(filesize的单位为kbyte)。
生成core调试文件
当我设置了ulimit -c 1024
,使用了-g
生成可执行文件,执行了程序,发生了预期的crash,但是在执行目录中,仍然看不到.core
相关的文件。
这种情况可以试下下面的方法。
1 | cat /proc/sys/kernel/core_pattern |
输出结果
xxxxxx@personal-ubuntu-0007:~/a$ cat /proc/sys/kernel/core_pattern
|/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g – %E
这种情况表明,文件的结果会被Linux的一个进程apport
吞掉,拿去检查是不是系统的bug,所以看不到core文件了。如果想要产生core文件,需要关闭该程序。
1 | sudo service apport stop |
再次cat /proc/sys/kernel/core_pattern
可以看到
xxxxxx@personal-ubuntu-0007:~/a$ cat /proc/sys/kernel/core_pattern
core
这个时候就正常可以调试生成core文件了。
core文件常见的信号
在 Unix/Linux 系统上,有许多不同的信号可以在不同的情况下发送给进程,以通知它们发生了某些事件。以下是一些常见的 core 信号及其含义:
SIGSEGV(Segmentation Fault):
表示进程试图访问其没有权限访问的内存区域,如非法内存访问、访问空指针等。通常会导致程序崩溃并生成 core 文件。
SIGILL(Illegal Instruction):
表示进程执行了非法指令。可能是因为代码错误、平台不兼容或者二进制文件损坏导致的。
SIGFPE(Floating Point Exception):
表示浮点运算异常,如除以零或无效的浮点操作。通常会导致程序崩溃。
SIGABRT:
由调用 abort() 函数引发,表示进程发生了致命错误。通常用于程序自身检测到无法继续执行的错误情况。
SIGBUS:
表示非法内存访问,类似于 SIGSEGV,但是涵盖了一些硬件相关的内存错误。
SIGILL:
表示进程执行了非法的指令,通常是由于代码错误或者二进制文件损坏引起的。
SIGPIPE:
当进程向一个已关闭的管道(或者 Socket)写数据时,操作系统会发送这个信号,通常用于处理进程之间的通信。
SIGSYS:
表示进程试图执行不支持的系统调用。可能是因为程序依赖于某些特定的操作系统特性,而这些特性在当前环境中不可用。
SIGTRAP:
通常用于调试器与被调试程序之间的通信。当被调试程序执行断点或陷阱指令时,会发送这个信号。
这只是一些常见的 core 信号,实际上还有许多其他信号,每个信号都有不同的含义和用途。根据需要,可以使用系统头文件 <signal.h> 中定义的常量来处理这些信号,以便在程序中适当地响应和处理这些事件。
gdb调试
- gdb
- file
- run
- core-file
- bt
- quit(q)
通过gdb
命令进入gdb调试环境。
在调试环境中,file
读取要执行文件的符号,run(r)
运行可执行文件,core-file
可以查看core文件,bt
打印出出错的堆栈信息数据,使用quit
退出gdb环境。
ELF
ELF介绍
并非所有二进制文件都可以使用gdb进行调试,前面提到我们需要使用-g
参数来生成可执行文件,因为这会在输出文件中添加debug信息。
为了加深理解,有必要对二进制文件进一步探索。下面看看什么是ELF文件格式。
在linux平台,执行man elf
可以查看到ELF非常详细的介绍。你会在开头看到这句话:
elf - format of Executable and Linking Format (ELF) files
ELF是一种数据格式,用来表示可执行文件和链接文件。
并不是所有系统平台都使用ELF作为可执行文件格式,目标可执行文件在不同系统平台表现为不同的格式。
Windows使用可移植可执行(PortableExecutable,PE)格式。
Mac OS-X使用Mach-O格式。
现代x86-64Linux和Unix系统使用可执行可链接格式(Executable and Linkable Format,ELF)。
ELF格式的文件在Linux系统下有.axf、 .bin、 .elf、 .o、 .prx、 .puff、 .ko、 .mod和.so等等
比如常见的可重定位目标文件格式如下.
这里面.debug包含了一个调试符号表,使用了-g参数编译的结果会带有这张表。
链接中的这张图比较好的展示了ELF的相关信息
查看ELF文件
使用readelf工具,使用命令readelf -h
可以查看该工具的使用方法。
下面列举几个最常用的命令。
- -a Equivalent to: -h -l -S -s -r -d -V -A -I
- -h Display the ELF file header
- -l Display the program headers
- -S Display the sections’ header
- -g Display the section groups
- -t Display the section details
- -e Equivalent to: -h -l -S
- -s Display the symbol table
section 和 Segmant
Section称为节,是指在汇编源码中经由关键字section或segment修饰、逻辑划分的指令或数据区域,汇编器会将这两个关键字修饰的区域在目标文件中编译成节,也就是说”节”最初诞生于目标文件中。
节区描述了文件的组成,可以通过readelf -S
来查看。节是从链接角度来看文件组成的,链接器将.o
文件链接成一个elf文件,同时生成了一个表,该表记录了各个section的区域。
Segment称为段,是链接器根据目标文件中属性相同的多个Section合并后的Section集合,这个集合称为Segment,也就是段,链接器把目标文件链接成可执行文件,因此段最终诞生于可执行文件中。我们平时所说的可执行程序内存空间中的代码段和数据段就是指的Segment。