6.11解析异常栈信息

本文详细介绍了异常发生时如何通过栈信息定位问题,涉及函数调用、栈帧结构、call和ret指令作用,以及如何利用gcc编译器的特性处理异常,包括使用结构体捕获寄存器值。作者以实例说明了如何通过调试和反汇编分析来确认异常处理过程中的寄存器值。
摘要由CSDN通过智能技术生成

目的:发生异常时候,我们需要知道异常发生在哪个位置,通过解析栈的信息可以知道出现异常的位置

这需要搞清楚函数之间调用时候通过栈传参的细节

图1:intel编程手册关于pusha的解释

图2:pusha压入的1寄存器,Temp指的是压入ESP

图3:函数栈的基本信息

结合王道的ppt(补一下底层的基础知识)

王道基础知识

call指令的作用

ret指令的作用

图3:栈所处的逻辑结构

栈是倒过来的由高地址向低地址发展

图4:访问栈帧的两种方法

图5:call指令的两条操作

函数调用切换栈帧

图6:保存绿色函数的PC旧值,压入绿色函数ebp的值

图7:ebp指向黄色函数,和esp重合

图8:栈帧切换完整图

图9:函数调用完整逻辑图

函数调用传递参数

gcc编译器将每个栈帧大小设置为16B的整数倍(当前函数的栈帧除外),因此栈帧内可能出现空闲未使用的区域。

通常将局部变量集中存储在栈帧底部区域

通常将调用参数集中存储在栈帧顶部区域

栈帧最底部一定是上一层栈帧基址(ebp旧值)

栈帧最顶部一定是返回地址(当前函数的栈帧除外)

图10:函数栈帧内容

函数调用的例子

图11:程序例子

图12:这个程序的栈帧

图13:完整过程

王道这些解析我感觉是最通俗易懂的

回归工程

异常处理函数

咱们这个异常处理函数,需要知道是哪条指令出了异常,然后再作下一步处理(打印出来之类的,像编辑器提示你哪句代码错了也是依靠下面的方法)

图14:函数栈信息

图15:出错的信息在那个EIP栈里面

我们需要拿到EIP栈里面的内容,为了使判错更加精准,一般也需要用到通用寄存器里面的值

我们怎么拿到寄存器的值呢?

两种方案

方案一:

要这么多形参,太复杂了,放弃

方案二:

因为这些值都是挨在一起的,所以搞个结构体,结构体的值都是挨一起的,所以能一一对应,这个设计很巧妙

//irq.h
typedef struct _exception_frame_t {
    // 结合压栈的过程,以及pusha指令的实际压入过程
    int gs, fs, es, ds;   //push压入的
    int edi, esi, ebp, esp, ebx, edx, ecx, eax;  //pusha压入的
    // int num;
    // int error_code;  //错误码暂时不加
    int eip, cs, eflags;//硬件自动压入的
}exception_frame_t;
//irq.c
void do_handler_unknown (exception_frame_t * frame) {
	do_default_handler(frame, "Unknown exception.");
}

咱们传参给函数do_handler_unknown,do_handler_unknown函数会去找调用do_handler_unknown函数的函数的栈顶附件,找到那个函数传过来的实参

exception_handler_unknown函数call了do_handler_unknown函数,相当于exception_handler_unknown调用了do_handler_unknown,那么do_handler_unknown就去exception_handler_unknown函数栈的栈顶位置找传过来的参数,我们自己压入那个结构体的起始地址,如下图

因为栈的地址是由高地址往低地址发展,所以定义结构体时候要把GS段寄存器放第一位,然后这个结构体的起始地址就是GS的地址(就像数组的第一个元素的地址可以当数组的起始地址),在没有执行 call do_handler_unknown之前,还是处于exception_handler_unknown函数栈里面,所以压入esp栈顶指针

exception_handler_unknown:
	// 保存所有寄存器
	pusha  // 保存通用寄存器
	// 保存段寄存器
	push %ds
	push %es
	push %fs
	push %gs

	push %esp
	call do_handler_unknown
	add $(1*4), %esp		// 丢掉esp,以保护下面pop的正确性,没有用pop指令,这块代码需要重用,见下图
		// 恢复保存的寄存器
	pop %gs
	pop %fs
	pop %es
	pop %ds
	popa

	iret  //中断返回指令,所以不能用c语言去写

call指令执行结束回到函数exception_handler_unknown(),esp指向的是形参,并不是部分寄存器的值(见图10:函数栈帧内容)

调试结果

看看结构体能不能正确兜住这些寄存器的值

在这个位置打断点

监控这个结构体,发现这个结构体中eip的值是0x00010267

去除0异常那个代码那里看看反汇编的代码

确实是10267

但是我发现在这个断点处的cpu寄存器信息并不是10267

因为此时此刻执行的是do_handler_unknown函数,并没有异常

看网上解释说eip就是PC的值

  • 9
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值