s3c2440裸机-异常中断(二. und未定义指令异常)

1._und(未定义指令异常)介绍

我们之前分析过5种异常,那么如何进入未定义指令异常,当然是cpu读取指令发生异常,出现了指令解析异常。
我们先来看下当cpu解析到什么样的指令才会触发未定义指令异常呢?

从上面的arm指令格式中可知,只要指令码属于划线的格式,就属于未定义指令异常。

2.汇编向c函数传参

我们知道汇编给C语言函数传参是通过r0,r1,...通过堆栈的方式去传递的参数,比如r0=1, r1=2;那么在被调用的c函数中argv0就是r0, argv1就是r1...,那么我们如果通过汇编给C函数传递字符串呢?

我们可以通过这样声明und_string为一个字符串:

und_string:
    .string "undefined instruction exception"

然后用ldr r1, =und_string,这样r1中就保存了und_string的地址。
这样调用我们的c函数就可以把und_string传入进去。

3._und异常程序示例

我们现在定义一条未定义指令伪代码:

.text
.global _start

_start:
    b reset  /* vector 0 : reset */
    b do_und /* vector 4 : und (看中断向量表)*/

reset:
    /*看门狗
    时钟
    sdram
    设置SP
    重定位*/
    ...
    bl print1

und_code:
    .word 0xdeadc0de; /*定义一条未定义指令*/
    /*故意以一个数据的方式引入一条未定义指令,当cpu执行到这里,读取0xdeadc0de指令码的时候,
    发现无法识别这条指令,就发生未定义指令异常,就跳转到0x4的中断向量去执行*/

    bl print2
    ...

我们现在为了方便调试理解:我们在未定义指令异常前后加上打印print1, print2,如果出现未定义指令异常后,就会跳到0x4的地方去读取指令,print2也就没法执行。

当跳转到0x4的中断向量后,发现此处是一条跳转指令"bl do_und", 我们再到未定义指令异常的服务程序do_und中打印出und_string这个字符串的内容。

现在开始写指令异常的服务程序do_und,实现如下:

do_und:
    /* sp_und未设置, 先设置它 (由于之前一直处于管理模式,现在处在und状态)*/
    ldr sp, =0x34000000

    /* 保存现场 */
    /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
    /* lr是异常处理完后的返回地址, 也要保存 */
    stmdb sp!, {r0-r12, lr}  /*先减后存*/ /* 把栈中的值备份到r0-r12*/

    /* 处理und异常 */
    mrs r0, cpsr
    ldr r1, =und_string /*保存und_string地址*/
    bl printException

    /* 恢复现场 */
    ldmia sp!, {r0-r12, pc}^  /*(ldmia先读后加),把备份的值恢复到栈中,让pc=lr就可以恢复到异常前的指令地址。^会把spsr的值恢复到cpsr里 */

下面来分析一下这个未定义指令异常服务程序:(其实代码的注释已经讲的很详细了)

1.进入未定义指令异常服务do_und之前硬件自动完成的事情如下:

     1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
     2. SPSR_und保存有被中断模式的CPSR
     3. CPSR中的M4-M0被设置为11011, 进入到und模式
     4. 跳到0x4的地方执行程序 (bl do_und)

2.进入指令异常服务程序do_und后,我们需要保存现场,处理und异常,恢复现场,注意:由于发生了cpu模式切换,如果要用到栈,那么先要设置对应模式的栈。由于栈的地址是向下生长的,这里我就用sdram的末位地址作为栈指针,把sp_und=0x34000000。

3.在und异常服务程序中有可能会用到栈, 所以先保存现场,通过stmdb sp!, {r0-r12, lr}语句把栈中的值备份到r0-r12和lr,然后恢复现场的时候通过ldmia sp!, {r0-r12, pc}^,详见上面的注释。

4.我们看到保存现场后,我们把cpsr的值放到r0, 把und_string放到r1, 然后用bl printException调用c函数,这样我们的c函数printException就能收到汇编传过来的参数,一个是cpsr模式(r0),一个是und_string汇编传过来的字符串(r1)。我们用C函数实现printException:

void printException(unsigned int cpsr, char *str)
{
    puts("Exception! cpsr = ");
    printHex(cpsr);
    puts(" ");
    puts(str);
    puts("\n\r");
}

完整的代码如下:

    .text
.global _start

_start:
    b reset  /* vector 0 : reset */ 

    b do_und /* vector 4 : und (看中断向量表)*/

do_und:
    /* 执行到这里之前:
     * 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
     * 2. SPSR_und保存有被中断模式的CPSR
     * 3. CPSR中的M4-M0被设置为11011, 进入到und模式
     * 4. 跳到0x4的地方执行程序 (bl do_und)
     */

    /* sp_und未设置, 先设置它 (由于之前一直处于管理模式,现在处在und状态)*/
    ldr sp, =0x34000000

    /* 保存现场 */
    /* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
    /* lr是异常处理完后的返回地址, 也要保存 */
    stmdb sp!, {r0-r12, lr}  /*先减后存*/ /* 把栈中的值备份到r0-r12*/

    /* 处理und异常 */
    mrs r0, cpsr
    ldr r1, =und_string /*保存und_string地址*/
    bl printException

    /* 恢复现场 */
    ldmia sp!, {r0-r12, pc}^  /*(ldmia先读后加),把备份的值恢复到栈中,让pc=lr就可以恢复到异常前的指令地址。^会把spsr的值恢复到cpsr里 */

und_string:
    .string "undefined instruction exception"

reset:
    /* 关闭看门狗 */
    /* 时钟 */
    /* sdram */
    bl copy2sdram
    bl clean_bss

    bl uart0_init

    bl print1
    /* 故意加入一条未定义指令 */
und_code:
    .word 0xdeadc0de  /* 未定义指令 */
    bl print2

    //bl main  /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
    ldr pc, =main  /* 绝对跳转, 跳到SDRAM */

halt:
    b halt

测试结果如下:

打印出print1中的字符串‘abc’后,紧接着打印printException函数中的结果,cpsr=0x600000db,那么对应的M[4:0]=11011, 对应下图为und模式。然后从und异常返回,恢复原来的模式继续执行。

上述代码改进

我们将上面的代码的und_string字符串修改一下:

...
und_string:
    .string "undef instruction"

reset:
    /* 关闭看门狗 */
    /* 时钟 */
...

编译烧录再次运行,发现没有任何打印输出,这是为什么呢?我明明只是把und_string字符串改了一下呀。

查看反汇编:

我们发现reset的地址是30000032,竟然不是4字节对齐的,我们知道arm指令集是以4字节为单位的,那么这里没有对齐,肯定无法解析指令。那么我们手工改进代码如下:

...
und_string:
    .string "undef instruction"

.align 4

reset:
    /* 关闭看门狗 */
    /* 时钟 */
...

我们再来看看反汇编,发现reset的地址是30000040,是以4字节对齐的,再次烧录运行,发现能够正常输出print1, 能够进入未定义指令异常。

原文地址:https://www.cnblogs.com/fuzidage/p/12114215.html

时间: 2024-10-07 20:17:09

s3c2440裸机-异常中断(二. und未定义指令异常)的相关文章

S3C2440的七种模式之——未定义模式

现在做第一个实验,模拟未定义模式. 未定义模式,是cpu遇到自己不能识别的指令时候做出的异常处理. arm指令的机器码一定是按照某种规范要求的,不然你随意写一条指令,cpu不是都可以执行吗?在cpu没有定义该条指令含义的情况下,我们执行了这样未定义的指令,就会进入未定义异常. 现在我们要模拟一个未定义异常,所以我们只要写出一个cpu无法识别的指令即可. 在这之前,要明白一个道理,在内存中执行的机器码,只有0,1两个值,不同的指令被分解为不同的0,1信号的机器码. 所以,我们在运行内存中存放一个3

ARMv7用户层发生指令异常的处理流程?是否每个进程都有一个APSR的副本?

1.用户层发生指令异常的处理流程? 用户层程序正在执行时,遇到未定义的指令(ARM不是别的指令)或者SWI软件中断指令(产生系统调用),就会产生异常,这里以未定义指令异常为例进行说明: 一旦出现未定义指令异常,CPU会自动做如下操作: (1)未定义模式(ARM其中运行模式的一种)下对应的lr(即R14)寄存器保存当前发生异常的指令下一条指令的地址.例如,在用户态有A B C 三条指令,指令A发生未定义指令异常,则指令B的地址就会由CPU保存到未定义模式下的lr寄存器中,用于异常返回. (2)CP

ARM异常中断返回的几种情况

ARM异常中断返回的几种情况重要基础知识:R15(PC)总是指向“正在取指”的指令,而不是指向“正在执行”的指令或正在“译码”的指令.一般来说,人们习惯性约定将“正在执行的指令作为参考点”,称之为当前第一条指令,因此 PC总是指向第三条指令.当 ARM 状态时,每条指令为 4 字节长,所以 PC 始终指向该指令地址加 8 字节的地址,即:PC 值=当前程序执行位置+8: 而 ADS 中的 pc,是为了调试看着方便而修改过的,它指向的是正在执行的指令,即“真正 pc-8”! 1.SWI 和和未定义

s3c2440裸机编程-时钟编程(二、配置时钟寄存器)

s3c2440裸机编程-时钟编程(二.配置时钟寄存器) 1.2440时钟时序 下图是2440时钟配置时序: 1.上电后,nRESET复位信号拉低,此时cpu还无法取指令工作. 2.nRESET复位信号结束后变为高电平,此时cpu开始工作.此时cpu主频FCLK=osc. 3.此时可以配置PLL,经过lock time后,FCLK倍频成新的时钟. 2.如何配置时钟 在参考手册的特性里介绍了S3C2440的工作频率,Fclk最高400MHz,Hclk最高136MHz,Pclk最高68MHz.那么 我

JSON未定义解决办法

ie6.ie7下JSON.parse JSON未定义的解决方法 解决方法一: var jsons = req.responseText; var s; if (typeof(JSON) == 'undefined'){ s = eval("("+jsons+")"); }else{ s = JSON.parse(jsons); } 解决方法二: 调用的页面里引用json2.js即可解决问题(推荐方法二). 即: <script type="text/

跟王老师学异常(二)try-catch

try-catch 主讲人:王少华  QQ群号:483773664 学习目标 学会使用try-catch处理异常 一.什么是异常 异常是指在程序的运行过程中所发生的不正常事件,它会中断正在运行的程序. 二.什么是异常处理 异常处理机制就像我们时可能会遇到的意外情况,预先想好的一些处理办法. 在程序执行代码的时候,万一发生的异常,程序会按照预定的处理办法对异常进行处理,异常处理完毕之后,程序继续运行. Java的异常处理是通过5个关键字来实现的:try.catch.finally.throw和th

【转】关于C的未定义行为

关于C的未定义行为 转自:http://www.guokr.com/blog/471312/ 对于C的初学者来说,被要求做下面的这种题目真的是脑残的不能再脑残的行为.但是很多C初级教程——居然都有这样的题.最典型的例子就是 a+=a++; 这种情况下,a最后到底等于多少了? 编译器应该如何理解a+=a++呢?首先是展开,a=a+a++:然后分别计算a和a++的值,把它们相加,然后把结果赋给a.但是这里有一个问题,就是执行完a++之后,a++的值等于a本身的值,但是a的值却变成了a+1.所以关键是

【转载】s3c2440裸机开发调试环境(MDK4.6,Jlink v8,mini2440)

用于arm裸机程序开发的IDE基本有 以下3个:MDK,IAR,还有ADS.具体它们的具体情况在这里我就不多说了,百度一下就明白了.由于之前开发c51,stm32时候都使用了MDK开发环境,而且MDK的界面确实看起来舒服多了,所以我选择了MDK作为我的s3c2440裸机开发的IDE.以下主要介绍一下如何使用MDK配合J-link来调试基于s3c2440的开发板. 首先,我们需要下载的有以下2样: MDK J-link 驱动 我的开发环境:windows 7 64位,J-linkv8,mini24

预定义的类型“System.Object”未定义或未导入

生成网站或项目时,VS 2010 出现如下错误: 预定义的类型“System.Object”未定义或未导入 无效的引用选项 无法引用目录 解决方法: 关掉 VS 2010 重新打开即可.