Linux下使用GDB调试程序无法设置断点的原因咨询
问题:GDB调试无调试信息ELF时无法在显示地址设置断点
环境:Linux 4.15系统,gcc 7.5.0,通过gcc main.c -o main编译生成hello world可执行文件,使用GDB调试时遇到以下问题:
现象1:反汇编显示的是ELF偏移而非实际加载地址
GDB中反汇编main函数得到的是低地址:
root@ubuntu:/root# gdb -q ./main Reading symbols from ./main...(no debugging symbols found)...done. (gdb) disassemble main Dump of assembler code for function main: 0x000000000000063a <+0>: push %rbp 0x000000000000063b <+1>: mov %rsp,%rbp 0x000000000000063e <+4>: lea 0x9f(%rip),%rdi # 0x6e4 0x0000000000000645 <+11>: callq 0x510 <puts@plt> 0x000000000000064a <+16>: mov $0x0,%eax 0x000000000000064f <+21>: pop %rbp 0x0000000000000650 <+22>: retq End of assembler dump. (gdb)
通过info files查看段地址,同样是基于0x0的偏移:
(gdb) info files Symbols from "/root/main". Local exec file: `/root/main', file type elf64-x86-64. Entry point: 0x530 0x0000000000000238 - 0x0000000000000254 is .interp 0x0000000000000254 - 0x0000000000000274 is .note.ABI-tag 0x0000000000000274 - 0x0000000000000298 is .note.gnu.build-id 0x0000000000000298 - 0x00000000000002b4 is .gnu.hash 0x00000000000002b8 - 0x0000000000000360 is .dynsym 0x0000000000000360 - 0x00000000000003e2 is .dynstr 0x00000000000003e2 - 0x00000000000003f0 is .gnu.version 0x00000000000003f0 - 0x0000000000000410 is .gnu.version_r 0x0000000000000410 - 0x00000000000004d0 is .rela.dyn 0x00000000000004d0 - 0x00000000000004e8 is .rela.plt 0x00000000000004e8 - 0x00000000000004ff is .init 0x0000000000000500 - 0x0000000000000520 is .plt 0x0000000000000520 - 0x0000000000000528 is .plt.got 0x0000000000000530 - 0x00000000000006d2 is .text 0x00000000000006d4 - 0x00000000000006dd is .fini 0x00000000000006e0 - 0x00000000000006f1 is .rodata 0x00000000000006f4 - 0x0000000000000730 is .eh_frame_hdr 0x0000000000000730 - 0x0000000000000838 is .eh_frame 0x0000000000200db8 - 0x0000000000200dc0 is .init_array 0x0000000000200dc0 - 0x0000000000200dc8 is .fini_array 0x0000000000200dc8 - 0x0000000000200fb8 is .dynamic 0x0000000000200fb8 - 0x0000000000201000 is .got 0x0000000000201000 - 0x0000000000201010 is .data 0x0000000000201010 - 0x0000000000201018 is .bss (gdb)
现象2:无法在显示的地址设置断点
尝试在puts@plt的地址0x510设置断点,启动程序后提示无法访问内存:
(gdb) break *0x510 Breakpoint 1 at 0x510 (gdb) r Starting program: /root/main Warning: Cannot insert breakpoint 1. Cannot access memory at address 0x510 (gdb)
原因分析
- PIE(位置无关可执行文件)编译:gcc 7.5.0默认启用
-pie选项,生成的是位置无关可执行文件。这类文件的ELF中记录的是链接时的相对偏移,而非进程运行时的实际虚拟地址。程序加载时,会被映射到一个随机的基地址上,而非ELF文件中默认的0x0基址。 - ASLR(地址空间布局随机化):Linux内核默认开启ASLR,会进一步随机化PIE程序的加载基址,每次运行程序的内存映射地址都可能不同,导致ELF中的固定偏移地址在运行时没有被映射为程序代码。
- 无调试符号:编译时未添加
-g选项,GDB无法获取调试信息,默认显示的是ELF文件中的链接偏移地址,而非进程加载后的实际地址。
解决方法
方法1:关闭PIE编译
编译时添加-no-pie选项,生成非位置无关的可执行文件:gcc main.c -o main -no-pie这类程序的加载基址固定为ELF文件中记录的地址,GDB中显示的地址就是实际运行地址,断点可以直接设置。
方法2:获取实际加载地址后设置断点
启动程序后(执行start或run后按Ctrl+C暂停),用info proc mappings查看程序的实际内存映射:(gdb) info proc mappings找到
.text段的起始基地址(比如0x555555554000),将ELF中的偏移地址加上该基址,得到实际运行地址,再设置断点:(gdb) break *0x555555554510方法3:直接按函数名设置断点
即使没有调试符号,GDB支持按函数名或PLT表项设置断点,会自动处理PIE的地址转换:(gdb) break puts@plt (gdb) break main
内容的提问来源于stack exchange,提问作者CarloC
相关产品推荐
相关产品推荐

