简介
调试代码的时候离不开调试信息,代码有代码的规范,调试信息也同样有调试信息格式的规范。我们以elf64文件格式为例来讲解dwarf和stabs两种调试信息格式对gdb的影响。
环境
docker : Ubuntu 16.04镜像
nasm : 2.13.02 // 一款编译器
gdb:8.1.0 // 调试器
源码
global _start
section .text
_start:
mov rax, 1
mov rbx, 2
cmp rax, rbx
jne .exit
inc rax
.exit:
mov rax, 60
xor rdi, rdi
syscall
上面的代码的意思是:如果寄存器rax与rbx的值不相等,则退出。如果相等,则将rax的值自加。
调试信息的格式
我们可以输入一下命令来查看elf64格式文件支持什么调试信息格式
root@000d3fada0b3:~/go/src/asm# nasm -f elf64 -y
valid debug formats for 'elf64' output format are ('*' denotes default):
dwarf ELF64 (x86-64) dwarf debug format for Linux/Unix
stabs ELF64 (x86-64) stabs debug format for Linux/Unix
我们可以看到,elf64 文件格式支持 dwarf 和 stabs 两种调试信息格式。
我们分别生成两种调试信息的可执行文件来进行对比。
dwarf格式
编译文件
root@000d3fada0b3:~/go/src/asm# nasm -f elf64 -F dwarf -o dwarf_jmp.o jmp.s // 编译 -f 指定文件格式,-F 指定调试信息格式。-o 输出目标文件
root@000d3fada0b3:~/go/src/asm# ld -o dwarf_jmp dwarf_jmp.o
root@000d3fada0b3:~/go/src/asm# ls
dwarf_jmp dwarf_jmp.o jmp.s
可以看到,我们生成了dwarf_jmp可执行文件。并指定了该可执行文件的调试信息格式为 dwarf
使用gdb调试
- 进入 gdb 调试命令行
gdb dwarf_jmp
- 在 _start 标签处设置断点
(gdb) b _start
- 执行
(gdb) r
- 单步执行断点
(gdb) n
7 mov rbx, 2
(gdb) n
9 cmp rax, rbx
(gdb)
通过以上命令,我们可以看到,gdb可以单步进行调试。
stabs格式
编译文件
root@000d3fada0b3:~/go/src/asm# nasm -f elf64 -F stabs -o stabs_jmp.o jmp.s
root@000d3fada0b3:~/go/src/asm# ld -o stabs_jmp stabs_jmp.o
root@000d3fada0b3:~/go/src/asm# ls
dwarf_jmp dwarf_jmp.o jmp.s stabs_jmp stabs_jmp.o
可以看到,我们生成了stabs_jmp可执行文件。并指定了该可执行文件的调试信息格式为 stabs
使用gdb调试
- 进入 gdb 调试命令行
gdb stabs_jmp
- 在 _start 标签处设置断点
(gdb) b _start
- 执行
(gdb) r
- 单步执行断点
(gdb) n
Single stepping until exit from function _start,
which has no line number information.
0x0000000000400092 in _start.exit ()
通过以上步骤,我们发现,单步调试并没有生效。
到底是什么原因导致了这种情况发生?
对比段(Section)头 的信息
dwarf 格式
root@000d3fada0b3:~/go/src/asm# readelf -S dwarf_jmp
There are 12 section headers, starting at offset 0x3b0:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000400080 00000080
000000000000001c 0000000000000000 AX 0 0 16
[ 2] .debug_aranges PROGBITS 0000000000000000 0000009c
0000000000000030 0000000000000000 0 0 1
[ 3] .debug_pubnames PROGBITS 0000000000000000 000000cc
0000000000000012 0000000000000000 0 0 1
[ 4] .debug_info PROGBITS 0000000000000000 000000de
0000000000000047 0000000000000000 0 0 1
[ 5] .debug_abbrev PROGBITS 0000000000000000 00000125
000000000000001b 0000000000000000 0 0 1
[ 6] .debug_line PROGBITS 0000000000000000 00000140
000000000000003e 0000000000000000 0 0 1
[ 7] .debug_frame PROGBITS 0000000000000000 00000180
0000000000000004 0000000000000000 0 0 8
[ 8] .debug_loc PROGBITS 0000000000000000 00000184
0000000000000010 0000000000000000 0 0 1
[ 9] .symtab SYMTAB 0000000000000000 00000198
0000000000000168 0000000000000018 10 11 8
[10] .strtab STRTAB 0000000000000000 00000300
000000000000002b 0000000000000000 0 0 1
[11] .shstrtab STRTAB 0000000000000000 0000032b
000000000000007e 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
stabs 格式
root@000d3fada0b3:~/go/src/asm# readelf -S stabs_jmp
There are 7 section headers, starting at offset 0x278:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000400080 00000080
000000000000001c 0000000000000000 AX 0 0 16
[ 2] .stab PROGBITS 0000000000000000 0000009c
0000000000000084 0000000000000014 3 0 4
[ 3] .stabstr STRTAB 0000000000000000 00000120
0000000000000007 0000000000000000 0 0 1
[ 4] .symtab SYMTAB 0000000000000000 00000128
00000000000000f0 0000000000000018 5 6 8
[ 5] .strtab STRTAB 0000000000000000 00000218
000000000000002b 0000000000000000 0 0 1
[ 6] .shstrtab STRTAB 0000000000000000 00000243
0000000000000030 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
对比两种格式的段(Section)头的信息,我们可以发现 dwarf 调试信息格式的文件比 stabs 调试信息格式的文件多了以下debug相关的段(Section)内容:
debug_info: 包含所有 DIE 的 DWARF 核心信息。包括函数
debug_abbrev:debug_info段(Section)中所用的缩写信息。
debug_line:行号信息
debug_aranges:内存地址与编译的映射信息。
debug_pubnames:全局对象和函数的查找表
debug_frame: 堆栈信息
参考
https://www.taodocs.com/p-14959260.html
https://blog.csdn.net/js072110/article/details/44153303
网友评论