本文是基于韦东山视频的学习笔记
und 异常(无定义异常)
我们在执行 main 函数之前加入句子
.word 0xdeadc0de //故意加入未定义句子
首先,代码执行至此,出现了 und 异常,根据异常向量表
出现 und 异常后会跳转到 0x00000004 地址去处理异常,所以我们需要在0x00000004 加入处理异常的跳转:
_start:
b reset /* 0x00000000 */
b und /* 0x00000004 */
而 und 写什么呢,根据上篇博文,大概笼统地可以理解为
- 保护现场
- 处理
- 恢复现场
_start:
b reset /* 0x30000000 */
// ldr pc, =do_und /* 0x30000004 */
ldr pc, und_addr
/* 这里为什么这么做
* ldr pc, und_addr 强制加载到这里,这里再强制加载到 do_und
* 如果不是,由于"ldr pc, =do_und" 是一条伪指令,do_und这个地址就会被编译器默认保存在编译文件最后的地址
* 修改后就能达到:“直接跳到接着上一句的地址执行,而不用跳到最后的地址”的目的
*/
und_addr:
.word do_und
do_und:
/* 重要!栈未设置,需要重新设置,指向一个不会被用到的地方 */
ldr sp, =0x34000000
/* 保护现场 */
stmdb sp!, {r0-r12, lr} //目前位置 lr 里面有异常前的下一条将要执行的指令,所以也要保存
/* 处理异常 */
mrs r0, cpsr //读cpsr的值,作为print_und_exception的第一个参数
ldr r1, =und_string //r1 载入字符串,作为print_und_exception的第二个参数
ldr pc, =print_und_exception
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
und_string:
.string "und exception!!!"
.align (4) // 4 zijie duiqi
swi 异常(软中断异常)
之前提过,除了usr模式之外,其他模式都可以都过寄存器任意切换,但恰恰app运行情况下一般情况都是usr模式。如果想切到其他模式,那就可以通过中断或者异常来实现,其中swi(软件异常)是最为常用的手段。软件异常进入的是svc模式。
事实上,swi 异常和 und 异常大同小异,照着印就可以。
swi 0x123 //加入swi异常
代码都是大同小异
swi_addr:
.word do_swi
do_swi:
/* 重要!栈未设置,需要重新设置,指向一个不会被用到的地方 */
ldr sp, =0x33e00000
/* 保护现场 */
stmdb sp!, {r0-r12, lr} //目前位置 lr 里面有被打断前的下一条将要执行的指令,所以也要保存
/* 调用c函数会破坏lr寄存器,所以要提前保护
* 根据ATPCS规则,c函数调用完会恢复r4-r11的寄存器,所以可以r4来保存
*/
mov r4, lr
/* 处理异常 */
mrs r0, cpsr //读cpsr的值,作为print_swi_exception的第一个参数
ldr r1, =swi_string //r1 载入字符串,作为print_swi_exception的第二个参数
bl print_swi_exception
sub r0, r4, #4 /* lr是保存异常前下一条指令的地址,减4后,r0就是swi异常的句子*/
bl print_swi_val
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
swi_string:
.string "swi exception!!!"
.align (4) // 前面是字串,后面必须4字节对齐
其中不同的是,swi异常的值是可以打印出来的,通过lr寄存器和print_swi_val函数实现的。
void print_swi_val(unsigned int *pswi)
{
puts("\n\rswi_val = ");
print_hex(*pswi & ~(0xff000000));
puts("\n\r");
}