You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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) 

原因分析

  1. PIE(位置无关可执行文件)编译:gcc 7.5.0默认启用-pie选项,生成的是位置无关可执行文件。这类文件的ELF中记录的是链接时的相对偏移,而非进程运行时的实际虚拟地址。程序加载时,会被映射到一个随机的基地址上,而非ELF文件中默认的0x0基址。
  2. ASLR(地址空间布局随机化):Linux内核默认开启ASLR,会进一步随机化PIE程序的加载基址,每次运行程序的内存映射地址都可能不同,导致ELF中的固定偏移地址在运行时没有被映射为程序代码。
  3. 无调试符号:编译时未添加-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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:13:16