简单地说,就是CPU或协处理器不认识这条指令,执行这样的指令时就会产生“未定义指令异常”。如果CPU核尝试使用操作码执行一条指令(在ARM体系结构规范中描述UNDEFINED),或者执行了协处理器指令但没有协处理器将其识别为可以执行的指令,则会导致未定义的指令异常。
在某些系统中,代码可能包含用于协处理器(例如VFP协处理器)的指令,但是系统中不存在相应的VFP硬件。另外,VFP硬件有可能无法处理特定指令,而是想调用软件来对其进行仿真。或者,可能会禁用VFP硬件,采用异常处理,以便可以启用它,然后重新执行指令。
使用未定义的指令,可以实现一些仿真器。比如在你的芯片中,它并未支持某条硬件除法指令,但是你还可以在代码中使用它。当CPU执行这条指令时会发生异常,在异常处理函数中,你用软件来实现该指令的功能。对于不是特别设置的未定义指令,在异常处理函数中不能处理它时,通常做法是记录适当的调试信息,并杀死对应的应用程序。在某些情况下,未定义指令异常的另一个用途是实现用户断点:调试器去修改代码,替换断点位置的指令为一条未定义指令。
通过在代码段里里插入一个未定义指令(0xeeadc0de),从而产生未定义指令异常。在未定义异常指令异常的处理函数里,调用printException函数,打印出当前的CPSR值,和产生异常的原因。
通过指令,设置好异常向量的基地址:
mcr p15, 0, r0, c12, c0, 0
在异常向量表里,通过指令跳转到Undefined_Handler标签处
ldr pc, =Undefined_Handler
在Undefined_Handler里将r0-r12和lr保存在und模式的栈上,然后调用printException 打印当前的CPSR值,并打印一个字符串。最后将r0-r12从栈上恢复,lr从栈上弹出到PC,并同时将SPSR恢复到CPSR,从而返回去执行出现未定义异常指令的下一条指令。
start.s
.text
.global _start, _vector_table
_start:
_vector_table:
ldr pc, =Reset_Handler /* Reset */
ldr pc, =Undefined_Handler /* Undefined instructions */
//b Reset_Handler
//b Undefined_Handler
b halt//b SVC_Handler//ldr pc, =SVC_Handler /* Supervisor Call */
b halt//ldr pc, =PrefAbort_Handler /* Prefetch abort */
b halt//ldr pc, =DataAbort_Handler /* Data abort */
.word 0 /* RESERVED */
b halt//ldr pc, =IRQ_Handler /* IRQ interrupt */
b halt//ldr pc, =FIQ_Handler /* FIQ interrupt */
.align 2
Undefined_Handler:
/* 执行到这里之前:
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_und保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式
* 4. 跳到0x4的地方执行程序
*/
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!, {r0-r12, lr}
/* 保存现场 */
/* 处理und异常 */
mrs r0, cpsr
ldr r1, =und_string
bl printException
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
und_string:
.string "undefined instruction exception"
.align 2
Reset_Handler:
/* Reset SCTlr Settings */
mrc p15, 0, r0, c1, c0, 0 /* read SCTRL, Read CP15 System Control register */
bic r0, r0, #(0x1 << 13) /* Clear V bit 13 to use normal exception vectors */
bic r0, r0, #(0x1 << 12) /* Clear I bit 12 to disable I Cache */
bic r0, r0, #(0x1 << 2) /* Clear C bit 2 to disable D Cache */
bic r0, r0, #(0x1 << 2) /* Clear A bit 1 to disable strict alignment */
bic r0, r0, #(0x1 << 11) /* Clear Z bit 11 to disable branch prediction */
bic r0, r0, #0x1 /* Clear M bit 0 to disable MMU */
mcr p15, 0, r0, c1, c0, 0 /* write SCTRL, Write to CP15 System Control register */
cps #0x1B /* Enter undef mode */
ldr sp, =0x80300000 /* Set up undef mode stack */
cps #0x13 /* Enter Supervisor mode */
ldr sp, =0x80200000 /* Set up Supervisor Mode stack */
ldr r0, =_vector_table
mcr p15, 0, r0, c12, c0, 0 /* set VBAR, Vector Base Address Register*/
//mrc p15, 0, r0, c12, c0, 0 //read VBAR
bl clean_bss
bl system_init
und_code:
.word 0xeeadc0de /* undefine instruction */
//.word 0x00000000
bl main
halt:
b halt
clean_bss:
/* 清除BSS段 */
ldr r1, =__bss_start
ldr r2, =__bss_end
mov r3, #0
clean:
cmp r1, r2
strlt r3, [r1]
add r1, r1, #4
blt clean
mov pc, lr
Main函数中加入以下函数段:
void printException(unsigned int cpsr, char *str)
{
printf("Exception! cpsr is 0x%xrn", cpsr);
printf("%srn", str);
}
简单地说就是执行SVC这条汇编指令时就会触发这个异常,CPU就会跳转过执行SVC异常向量的代码。
supervisor call(SVC)通常在用户模式下使用,这使得用户模式的代码能够访问OS功能。例如,如果用户代码想要访问系统的特权部分(例如执行文件I/O),则通常将使用SVC指令执行此操作。在Linux中对文件的open/read/write等应用层的系统函数,它的本质都是执行SVC指令,从而进入Linux内核中预设的SVC异常处理函数,在内核里操作文件。
可以使用寄存器或者操作码中某个字段将参数传递给SVC处理程序。发生异常时,异常处理程序必须确定内核是处于ARM还是Thumb状态。特别是SVC处理程序,必须读取指令集状态。通过检查SPSR T位完成的。该位设置为Thumb状态,清除为ARM状态。
ARM和Thumb指令集都具有SVC指令。从Thumb状态调用SVC时,必须考虑以下因素:
•指令地址位于LR-2,而不是LR-4;
•指令本身是16位的,因此需要半字加载;
•SVC编号为8位而不是ARM状态下的24位。
在ARM9等比较老的芯片里,这个异常是SWI异常,对应的指令是SWI。
通过执行“swi 123”指令,触发SVC异常,程序跳转到异常向量表偏移0x8的地方执行,在异常向量表里通过如下指令跳转到SVC_Handler标签处执行。
ldr pc, =SVC_Handler
在SVC_Handler里将r0-r12和lr保存在SVC模式的栈上,然后将lr的值移动到R4,调用
printException函数打印出当前的CPSR值,和产生异常的原因。将R4减去4,赋值给R0,这是swi指令所在的地址,然后调用printSWIVal函数打印出swi指令的参数。最后将r0-r12 从栈上恢复,lr从栈上弹出到PC,并同时将SPSR恢复到CPSR,从而返回去执行swi指令的下一条指令。
start.s
.text
.global _start, _vector_table
_start:
_vector_table:
ldr pc, =Reset_Handler /* Reset */
ldr pc, =Undefined_Handler /* Undefined instructions */
ldr pc, =SVC_Handler /* Supervisor Call */
b halt//ldr pc, =PrefAbort_Handler /* Prefetch abort */
b halt//ldr pc, =DataAbort_Handler /* Data abort */
.word 0 /* RESERVED */
b halt//ldr pc, =IRQ_Handler /* IRQ interrupt */
b halt//ldr pc, =FIQ_Handler /* FIQ interrupt */
.align 2
Undefined_Handler:
/* 执行到这里之前:
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_und保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式
* 4. 跳到0x4的地方执行程序
*/
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!, {r0-r12, lr}
/* 保存现场 */
/* 处理und异常 */
mrs r0, cpsr
ldr r1, =und_string
bl printException
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
und_string:
.string "undefined instruction exception"
.align 2
SVC_Handler:
/* 执行到这里之前:
* 1. lr_svc保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_svc保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为10011, 进入到svc模式
* 4. 跳到0x08的地方执行程序
*/
/* 保存现场 */
/* 在swi异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!, {r0-r12, lr}
mov r4, lr
/* 处理swi异常 */
mrs r0, cpsr
ldr r1, =swi_string
bl printException
sub r0, r4, #4
bl printSWIVal
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
swi_string:
.string "swi exception"
.align 2
Reset_Handler:
/* Reset SCTlr Settings */
mrc p15, 0, r0, c1, c0, 0 /* read SCTRL, Read CP15 System Control register */
bic r0, r0, #(0x1 << 13) /* Clear V bit 13 to use normal exception vectors */
bic r0, r0, #(0x1 << 12) /* Clear I bit 12 to disable I Cache */
bic r0, r0, #(0x1 << 2) /* Clear C bit 2 to disable D Cache */
bic r0, r0, #(0x1 << 2) /* Clear A bit 1 to disable strict alignment */
bic r0, r0, #(0x1 << 11) /* Clear Z bit 11 to disable branch prediction */
bic r0, r0, #0x1 /* Clear M bit 0 to disable MMU */
mcr p15, 0, r0, c1, c0, 0 /* write SCTRL, Write to CP15 System Control register */
cps #0x1B /* Enter undef mode */
ldr sp, =0x80300000 /* Set up undef mode stack */
cps #0x13 /* Enter Supervisor mode */
ldr sp, =0x80200000 /* Set up Supervisor Mode stack */
ldr r0, =_vector_table
mcr p15, 0, r0, c12, c0, 0 /* set VBAR, Vector Base Address Register*/
//mrc p15, 0, r0, c12, c0, 0 //read VBAR
bl clean_bss
bl system_init
und_code:
.word 0xeeadc0de /* undefine instruction */
//.word 0xFFFFFFFF
swi_code:
swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */
bl main
halt:
b halt
clean_bss:
/* 清除BSS段 */
ldr r1, =__bss_start
ldr r2, =__bss_end
mov r3, #0
clean:
cmp r1, r2
strlt r3, [r1]
add r1, r1, #4
blt clean
mov pc, lr
Main函数中加入以下函数段:
void printException(unsigned int cpsr, char *str)
{
printf("Exception! cpsr is 0x%xrn", cpsr);
printf("%srn", str);
}
void printSWIVal(unsigned int *pSWI)
{
printf("SWI val = 0x%xrn", *pSWI & ~0xff000000);
}