对ARM的异常处理
对ARM异常处理的研究务必要弄清楚以下几个方面: 异常类型,异常类型及处理该异常时CPU的执行模式,异常向量地址,异常处理过程 。
1、异常类型
(1)、复位(RESET)
a、当处理器复位引脚有效时,系统产生复位异常中断,程序跳转到复位异常中断处理程序处执行,包括系统加电和系统复位。
b、通过设置PC跳转到复位中断向量处执行称为软复位。
(2)、未定义的指令
当ARM处理器或者是系统中的协处理器认为当前指令未定义时,产生未定义的指令异常中断,可以通过改异常中断机制仿真浮点向量运算。
(3)、软件中断(SWI)
这是一个由用户定义的中断指令(SWI)。可用于用户模式下的程序调用特权操作指令。在实时操作系统中可以通过该机制实现系统功能调用。
(4)、指令与取终止(PrefechAbort)
如果处理器预取的指令的地址不存在,或者该地址不允许当前指令访问,当被预取的指令执行时,处理器产生指令预取终止异常中断。
(5)、数据访问终止(DATAABORT)
如果数据访问指令的目标地址不存在,或者该地址不允许当前指令访问,处理器产生数据访问终止异常中断。
(6)、外部中断请求(IRQ)
当处理器的外部中断请求引脚有效,而且CPSR的寄存器的I控制位被清除时,处理器产生外部中断请求异常中断。系统中个外设通过该异常中断请求处理服务。
(7)、快速中断请求(FIQ)
当处理器的外部快速中断请求引脚有效,而且CPSR的F控制位被清除时,处理器产生外部中断请求异常中断。
2、执行模式及向量地址
ARM的异常向量地址可以处于4G物理空间的低端(0x00000000起),也可以处于高端(0xffff0000起),具体是哪种情况,根据具体的CPU及其配置而定。ARMv4及其以上的版本,ARM异常向量表的地址受协处理器CP15的c1寄存器(controlregister)中V位的控制,如果V=1,则异常向量表的地址为0x00000000~0x0000001c;如果V=0,则为:0xffff0000~0xffff001c。表1是7种异常的执行模式及向量地址:
表1
异常类型 | 执行模式 | 向量地址 |
RESET异常 | Supervisor模式 | 0x00000000 (0xffff0000) |
未定义的指令 | Undefined模式 | 0x00000004(0xffff0004) |
软件中断 | Supervisor模式 | 0x00000008(0xffff0008) |
PrefechAbort | Abort模式 | 0x0000000c(0xffff000c) |
DATA Abort | Abort模式 | 0x00000010(0xffff0010) |
IRQ | IRQ模式 | 0x00000018(0xffff0018) |
FIQ | FIQ模式 | 0x0000001c(0xffff001c)
|
3、处理过程
处理过程包括两个部分:
进入:这个过程由CPU负责
退出:这个过程由OS负责
在捕获到某个异常后,启动“进入”过程,该过程内CPU执行如下动作:
(1)将当前PC的值(或PC+ 4,或PC+ 8)保存到R14的某个寄存器中。到底选择哪个寄存器由该异常的执行模式而定;另外R14影子寄存器的值同异常类型相关。比如DataAbort异常,对应的影子寄存器就是Abort模式的影子寄存器R14_abt,R14_abt的值为异常产生时PC值+ 8。
(2)将CPSR保存到CPSR的某个寄存器SPSR中,同样,具体选择哪个寄存器由该异常的执行模式而定。
(3)改写CPSR以切换到相应的异常模式和处理器状态(ARM状态);
(4)禁止IRQ(如果进入FIQ则禁止FIQ);
(5)跳转到相应异常向量表入口(例如IRQ跳转到IRQ_Handler入口);
【注】复位异常处理会禁止所有中断,另外由于不用返回,因此不需要作(1)(2)步。
4、示例异常处理程序
Example 4.1说明了一个自定义异常处理程序。假定将某些Fortran代码转换为C 代码。Fortran数值标准要求0除以 0 得1,而 IEEE754 规定 0 除以0 是“无效运算”,因此,缺省返回无提示NaN。Fortran代码可能会依赖于这种行为,让0除以 0 返回1 可能更方便一些,而不是对代码进行修改。
进行编译时,必须选择一种支持异常的浮点模型,例如--fpmode=ieee_full或--fpmode=ieee_fixed。
安装处理程序之后,0.0除以0.0 返回1.0。
Example 4.1. 自定义异常处理程序
#include<fenv.h>
#include<signal.h>
#include<stdio.h>
__softfp__ieee_value_t myhandler(__ieee_value_t op1, __ieee_value_t op2,
__ieee_edata_t edata)
{
__ieee_value_tret;
if((edata & FE_EX_FN_MASK) == FE_EX_FN_DIV)
{
if((edata & FE_EX_INTYPE_MASK) == FE_EX_INTYPE_FLOAT)
{
if(op1.f == 0.0 && op2.f == 0.0)
{
ret.f= 1.0;
returnret;
}
}
if((edata&FE_EX_INTYPE_MASK)== FE_EX_INTYPE_DOUBLE)
{
if(op1.d == 0.0 && op2.d == 0.0)
{
ret.d= 1.0;
returnret;
}
}
}
/*For all other invalid operations, raise SIGFPE as usual */
raise(SIGFPE);
}
intmain(void)
{
floati, j, k;
fenv_tenv;
fegetenv(&env);
env.statusword|= FE_IEEE_MASK_INVALID;
env.invalid_handler= myhandler;
fesetenv(&env);
i= 0.0;
j= 0.0;
k= i/j;
printf(“kis %f\n“, k);
}
5.linux下的异常捕获和处理
清单 1.在异常对象构造函数中生成一个堆栈跟踪
//Sample Program: #include<execinfo.h> #include<exception> usingnamespace std; / classExceptionTracer free(symbols); |
清单 2中定义的SignalExceptionClass,提供了表示内核可能发出信号的C++异常的抽象。SignalTranslator是一个基于SignalExceptionClass的模板类,它通常用来实现到C++异常的转换。在任何瞬间,只能有一个信号处理程序处理一个活动进程的一个信号。因此,SignalTranslator采用了singleton设计模式。整体概念通过用于SIGSEGV的SegmentationFault类和用于SIGFPE的FloatingPointException类得到了展示。
清单 2.将信号转换成异常
template<class SignalExceptionClass> classSignalTranslator staticvoid SignalHandler(int) public: //An example for SIGSEGV SignalTranslator<SegmentationFault>g_objSegmentationFaultTranslator; //An example for SIGFPE SignalTranslator<FloatingPointException>g_objFloatingPointExceptionTranslator |
清单 3.处理构造函数中的异常
classExceptionHandler staticvoid Handler() //ifthis is a thread performing some coreactivity public: // classA //Before defining any global variable, we define a dummyinstance // intmain(int argc, char* argv[]) |