gdb调试(1):info frame的含义解读

本文深入解析了汇编语言中的寄存器作用及函数调用机制,通过实例展示了如何利用GDB工具进行栈帧分析,理解函数参数传递与局部变量存储方式,适合初学者及进阶学习者。

一个代码:
在这里插入图片描述
输入info frame 的结果:
在这里插入图片描述

解读这个输出之前,需要先明白汇编中,函数的调用形式。

寄存器的英文全称:
AH&AL=AX(accumulator):累加寄存器
BH&BL=BX(base):基址寄存器
CH&CL=CX(count):计数寄存器
DH&DL=DX(data):数据寄存器
SP(Stack Pointer):堆栈指针寄存器
BP(Base Pointer):基址指针寄存器
SI(Source Index):源变址寄存器
DI(Destination Index):目的变址寄存器
IP(Instruction Pointer):指令指针寄存器
CS(Code Segment)代码段寄存器
DS(Data Segment):数据段寄存器
SS(Stack Segment):堆栈段寄存器
ES(Extra Segment):附加段寄存器

(1)ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
(2)EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。

了解几个常见的寄存器名字,记住eax一般用来保存函数的返回值,记住esp是栈顶指针寄存器,ebp是栈底指针寄存器。

对于在汇编指令上如何调用,强烈推荐这篇文章:
https://www.cnblogs.com/clover-toeic/p/3755401.html

介绍的很清楚,截图来说:
函数的形式参数就存储在单独的一个stack里面(红色)
在这里插入图片描述

另一个info frame 的解读
(gdb) info f

        Stack level 0, frame at 0xbffff5d4:

这个0xbffff5d4是你当前程序所分配使用的栈地址

         eip = 0x804845d in func (tst.c:6); saved eip 0x8048524

0x804845d为被调用的函数地址,0x8048524为调用函数的代码的地址
也就是:
0x8048524 : call 0x804845d

         called by frame at 0xbffff60c
         source language c.
         Arglist at 0xbffff5d4, args: n=250

arglist就是函数参数在栈中的地址0xbffff5d4

         Locals at 0xbffff5d4,

0xbffff5d4是函数中定义的变量在栈中的位置,从这里看来你的Arglist应该是空的

Previous frame’s sp is 0x0
因为在flat模式下,这个Previous frame’s sp 始终是0的,没意义

         Saved registers:
          ebp at 0xbffff5d4, eip at 0xbffff5d8

函数进入以后,首先会压参数列表,然后压ebp,再压寄存器以保护。从这里可以看出,
你的ebp只比eip小4,再次证明你的函数的参数列表为空

英文例子解读:
https://stackoverflow.com/questions/5144727/how-to-interpret-gdb-info-frame-output

对于不同位数的机器,这些寄存器有写不一样
在这里插入图片描述

Stack level 0, frame at 0x7fffffffe110:

	frame0表示是第一栈帧,这个0x7fffffffe110是你当前程序所分配使用的栈地址,

rip = 0x4004fd in main (func.c:11); saved rip = 0x7ffff7a2d830

0x4004fd 为被调用的函数地址,0x7ffff7a2d830 为调用函数的代码的地址
在执行这个代码之前,加了break main,也就是在main函数这里断点。

source language c.

使用语言是c

Arglist at 0x7fffffffe100, args:
Locals at 0x7fffffffe100, Previous frame’s sp is 0x7fffffffe110
Saved registers:
rbp at 0x7fffffffe100, rip at 0x7fffffffe108

代码输出的文档:

在这里插入图片描述
文档解读

<think>我们正在解决GDB调试中使用bt命令时出现"PC not available"的问题。根据之前的讨论,核心转储文件可能缺少寄存器信息(特别是程序计数器PC),导致无法回溯调用栈。以下是系统化的解决方案: ## 问题原因 - **寄存器信息缺失**:核心转储未保存寄存器状态(常见于非标准方式生成的核心文件) - **栈帧损坏**:程序崩溃时栈被破坏(如缓冲区溢出) - **调试信息不匹配**:可执行文件与核心转储版本不一致 ## 解决方案 ### 方法1:强制GDB从核心文件读取寄存器 ```gdb # 启动GDB后立即执行 (gdb) set use-deprecated-index-sections on (gdb) core-file /path/to/core.dump # 重新加载核心文件 (gdb) bt # 再次尝试回溯 ``` ### 方法2:手动恢复寄存器状态(高级) 当方法1失效时,需从核心文件内存区域重建寄存器: ```gdb (gdb) info files # 查找内存段起始地址 (gdb) x/64xw 0x7ffc00000000 # 扫描栈内存区域 (gdb) set $pc = 0x4005fa # 根据反汇编手动设置PC (gdb) set $sp = 0x7ffc0000 # 设置栈指针 (gdb) bt ``` ### 方法3:使用原始内存数据重建栈帧 ```gdb (gdb) set logging on # 开启日志记录 (gdb) maintenance print psymbols # 打印部分符号 (gdb) maintenance print msymbols # 打印所有符号 # 通过内存地址逆向分析: (gdb) x/100i $pc - 50 # 反汇编当前指令区域 ``` ## 预防措施 1. **确保完整核心转储** ```bash ulimit -c unlimited echo 1 | sudo tee /proc/sys/kernel/core_uses_pid ``` 2. **编译时添加调试信息** ```bash gcc -g3 -fno-omit-frame-pointer -o program program.c ``` 3. **运行时捕获信号** ```gdb (gdb) handle SIGSEGV nostop noprint pass # 确保信号传递 ``` ## 验证修复 ```gdb (gdb) p $_siginfo._sifields._sigfault.si_addr # 检查错误地址 (gdb) info frame 0 # 检查最顶层栈帧 ``` > **关键点**:`PC not available`通常意味着寄存器上下文完全丢失,上述方法本质是通过内存数据逆向重建执行状态[^1]。 §§相关问题§§ 1. 如何解读GDB中的核心转储内存映射信息? 2. 当程序发生栈溢出时如何调试? 3. GDB有哪些高级命令可以分析损坏的栈帧? </think> ### 解决 GDB 的 "PC not available" 问题 当在 GDB 中使用 `bt` 命令时出现 **"PC not available"** 错误,通常表示调试器无法访问程序计数器(Program Counter)的值,这会导致堆栈回溯失败。以下是系统化的解决方案: --- #### 问题原因分析 1. **寄存器信息缺失** - 核心转储文件未包含完整的寄存器状态(常见于非正常生成的 core 文件) - 进程在崩溃时寄存器状态未被正确保存 - 调试环境与程序架构不匹配(如 32 位程序用 64 位 GDB 调试) 2. **栈帧损坏** - 缓冲区溢出或内存损坏导致调用栈被破坏 - 函数返回地址被覆盖(常见于 `$pc` 寄存器无效) 3. **信号处理问题** - 程序在信号处理函数中崩溃时,上下文可能不完整(参考引用[1]) --- #### 解决方案步骤 **第一步:确保正确加载符号和核心文件** ```bash # 启动 GDB 时同时加载可执行文件和核心转储 gdb -q ./your_program core.dump ``` **第二步:手动恢复寄存器上下文** ```gdb # 检查信号信息(确定崩溃点) (gdb) p $_siginfo._sifields._sigfault.si_addr # 尝试设置 $pc 寄存器(需结合反汇编) (gdb) disas /m main # 查看函数地址 (gdb) set $pc = 0x4005fa # 设置为已知有效指令地址 # 强制重建栈帧 (gdb) set $sp = $sp - 64 # 微调栈指针尝试恢复 ``` **第三步:逐帧手动分析(当 `bt` 失效时)** ```gdb # 查看当前栈帧寄存器 (gdb) info frame # 手动遍历栈帧(假设 x86 架构) (gdb) x/8a $sp # 检查栈中可能的返回地址 (gdb) frame 0x7fffffffe3a0 # 跳转到指定栈帧地址 # 使用内联汇编检查(高级技巧) (gdb) printf "EIP: 0x%x\n", $eip ``` **第四步:预防性编译选项(未来避免问题)** ```bash # 编译时添加帧指针和调试信息 gcc -g3 -fno-omit-frame-pointer -o program program.c # 关闭栈保护(仅用于调试复现) gcc -fno-stack-protector ... ``` --- #### 特殊情况处理 1. **栈完全损坏时**: ```gdb (gdb) maintenance info sections # 定位内存段 (gdb) x/256a $esp-128 # 扫描栈区域寻找有效地址 ``` 2. **信号处理函数崩溃**(参考引用[1]): ```gdb (gdb) handle SIGSEGV pass # 确保信号传递给程序 (gdb) catch signal SIGSEGV # 在信号触发时中断 ``` 3. **使用增强型堆栈分析**(参考引用[2][3]): ```gdb (gdb) set debug frame 1 # 启用栈帧调试 (gdb) bt full # 显示完整局部变量 (gdb) thread apply all bt # 多线程堆栈回溯 ``` > **验证成功**:正确修复后,`bt` 命令应显示完整的函数调用栈[^2],例如: > ``` > #0 0x00007f... in __libc_start_main () > #1 0x000055... in main () at main.c:30 > ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值