• 未定义指令:故名思意就是没有定义的异常,CPU不认识的指令,当执行这些指令时CPU将会出错
  • 未定义指令有什么作用呢?
    • 很多时候,我们故意在代码里插入一些伪造的指令,故意让CPU执行到它时触发错误。
    • 这在调试时很有用,比如想打断点:怎么实现呢?有很多种方法:硬件监视点(watch point,数量有限)、软件断点(数量无限)。
    • 软件断点就是使用未定义指令来实现的
  • 产生未定义的原因有两个: 要么是程序出错,要么是故意插入未定义的指令
  • 想让程序执行到某个地址A时停下来,可以这样做:
    • 地址A上原来的指令是xxx
    • 我们故意把它改成yyy,改成一条CPU无法识别的指令
    • 当CPU执行到地址A上的yyy指令时,触发异常
    • 在异常处理函数里,打印更多调试信息(跟程序员交互,显示更多寄存器的值、状态等)
    • 调试完毕后,恢复地址A上的指令为xxx
    • 从地址A重新执行程序
  • A7的异常向量表:

    1. _start:
    2. b reset
    3. ldr pc, _undefined_instruction
    4. ldr pc, _software_interrupt
    5. ldr pc, _prefetch_abort
    6. ldr pc, _data_abort
    7. ldr pc, _not_used
    8. ldr pc, _irq
    9. ldr pc, _fiq
    • 从向量表可以看出,A7支持哪些异常
  • 可以在代码中插入一条未定义指令: .word 0xffffffff看看会发生什么
  • 因为我们将异常向量表放在程序最开头,程序烧写在flash上,然后会被片内ROM拷贝到内存的某个地址,而这个地址一般不为0(ARM9中,异常向量表的地址只能是0、0xFFFF0000;A7中可以通过VBAR修改异常向量表基地址),所以需要修改向量表的基地址

    1. MRC p15, 0, <Rt>, c12, c0, 0 ; Read VBAR into Rt
    2. MCR p15, 0, <Rt>, c12, c0, 0 ; Write Rt to VBAR
    3. // VBAR(vector base address)是一个协处理器(寄存器)
    4. /* 设置异常向量表基地址 : VBAR */
    5. ldr r0, =_start
    6. mcr p15, 0, r0, c12, c0, 0 //将异常向量表基地址设置为_start
  • image.png

  • 实现未定义指令异常: ```c //#define STACK_BASE (0xc0000000 + 0x100000) // stm32mp157

    define STACK_BASE (0x80000000 + 0x100000) // imx6ull

    define STACK_SIZE (2048)

.text .global _start _start:
b reset ldr pc, =do_undefined .word 0 // ldr pc, _software_interrupt //.word 0 : 在当前位置放一个word型的值,该值为0 .word 0 // ldr pc, _prefetch_abort .word 0 // ldr pc, _data_abort .word 0 // ldr pc, _not_used .word 0 // ldr pc, _irq .word 0 // ldr pc, _fiq

reset: / 设置sp / / 对于STM32MP157设置链接地址为0xC0200000, 对于IMX6ULL设为0x80200000 / ldr sp, =STACK_BASE

  1. adr r0, _start
  2. bl SystemInit
  3. bl uart_init
  4. /* 设置异常向量表基地址 : VBAR */
  5. ldr r0, =_start
  6. mcr p15, 0, r0, c12, c0, 0
  7. .word 0xffffffff
  8. /* 调用main函数 */
  9. //bl main
  10. ldr pc, =main

do_undefined: / 设置SP_und / ldr sp, =STACK_BASE - STACK_SIZE // 设置SP_und在SP_user的下方

  1. /* 保存现场 */
  2. stmdb sp!, {R0-R3,R12,LR} // 将寄存器进行压栈; und模式中, LR保存的地址是未定义指令地址+4(即下一条指令的地址)
  3. /* 调用处理函数 */
  4. bl do_undefined_c // 调用实际的处理函数(这里将会更新LR寄存器的值)
  5. /* 恢复现场 */
  6. ldmia sp!, {R0-R3,R12,PC}^ //恢复压栈的寄存器值 ^ : 还要将SPSR的值恢复到CPSR ; LR刚好指向未定义指令的下一条指令,所以不用再+offset

```

  • image.png