关于ARM指令中位置无关和位置相关代码的认识
2016-01-17 20:14
399 查看
今天在一个问题上折腾了又是半天。就是在学JZ2440串口通信的时候,在sdram初始化函数中有这么一句话
/* * 设置存储控制器以使用SDRAM */ void memsetup(void) { volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; /* 这个函数之所以这样赋值,而不是像前面的实验那样将配置值 * 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到 * SDRAM之前就可以在steppingstone中运行 */ /* 存储控制器13个寄存器的值 */ p[0] = 0x22011110; //BWSCON p[1] = 0x00000700; //BANKCON0 p[2] = 0x00000700; //BANKCON1 p[3] = 0x00000700; //BANKCON2 p[4] = 0x00000700; //BANKCON3 p[5] = 0x00000700; //BANKCON4 p[6] = 0x00000700; //BANKCON5 p[7] = 0x00018005; //BANKCON6 p[8] = 0x00018005; //BANKCON7 p[9] = 0x008C04F4; //REFRESH p[10] = 0x000000B1; //BANKSIZE p[11] = 0x00000030; //MRSRB6 p[12] = 0x00000030; //MRSRB7 }
对于前面实验,sdram初始化函数是这么写的:
void memsetup(void) { /* SDRAM 13个寄存器的值 */ unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON 0x00000700, //BANKCON0 0x00000700, //BANKCON1 0x00000700, //BANKCON2 0x00000700, //BANKCON3 0x00000700, //BANKCON4 0x00000700, //BANKCON5 0x00018005, //BANKCON6 0x00018005, //BANKCON7 0x008C07A3, //REFRESH 0x000000B1, //BANKSIZE 0x00000030, //MRSRB6 0x00000030, //MRSRB7 }; int i = 0; volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; for(; i < 13; i++) p[i] = mem_cfg_val[i]; }
对于这个串口程序,它的链接文件如下:
SECTIONS { . = 0x30000000; .text : { *(.text) } .rodata ALIGN(4) : {*(.rodata)} .data ALIGN(4) : { *(.data) } .bss ALIGN(4) : { *(.bss) *(COMMON) } }
表示该程序“应该”放在0x30000000中执行。
在程序运行时,首先执行head.S中的代码。
head.S代码如下:
.extern main .text .global _start _start: Reset: ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈 bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启 bl clock_init @ 设置MPLL,改变FCLK、HCLK、PCLK bl memsetup @ 设置存储控制器以使用SDRAM bl copy_steppingstone_to_sdram @ 复制代码到SDRAM中 ldr pc, =on_sdram @ 跳到SDRAM中继续执行 on_sdram: ldr sp, =0x34000000 @ 设置栈指针 ldr lr, =halt_loop @ 设置返回地址 ldr pc, =main @ 调用main函数 halt_loop: b halt_loop
程序运行时,对于NAND FLASH启动,首先NAND FLASH前4K代码会被复制到steppingstone片内内存中去执行,这个时候0x30000000(对于JZ2440来说该地址是SDRAM地址)还不能使用,因为SDRAM还没初始化,虽然链接的时候指出了,程序应该位于0x30000000执行,但是程序代码从NAND FLASH拷贝到片内内存之后,CPU默认从0地址开始执行,所以程序会被执行,当SDRAM没被初始化之前,用的都是bl跳转指令,这些指令都是位置无关指令。
对于反汇编文件,先看前面一部分:
30000000 <_start>: 30000000: e3a0da01 mov sp, #4096 ; 0x1000 30000004: eb00000a bl 30000034 <disable_watch_dog> 30000008: eb00000d bl 30000044 <clock_init> 3000000c: eb000026 bl 300000ac <memsetup> 30000010: eb000040 bl 30000118 <copy_steppingstone_to_sdram> 30000014: e59ff00c ldr pc, [pc, #12] ; 30000028 <.text+0x28> 30000018 <on_sdram>: 30000018: e3a0d30d mov sp, #872415232 ; 0x34000000 3000001c: e59fe008 ldr lr, [pc, #8] ; 3000002c <.text+0x2c> 30000020: e59ff008 ldr pc, [pc, #8] ; 30000030 <.text+0x30> 30000024 <halt_loop>: 30000024: eafffffe b 30000024 <halt_loop> 30000028: 30000018 andcc r0, r0, r8, lsl r0 3000002c: 30000024 andcc r0, r0, r4, lsr #32 30000030: 30000200 andcc r0, r0, r0, lsl #4 30000034 <disable_watch_dog>: 30000034: e3a02000 mov r2, #0 ; 0x0 30000038: e3a03453 mov r3, #1392508928 ; 0x53000000 3000003c: e5832000 str r2, [r3] 30000040: e1a0f00e mov pc, lr
前面表示了程序链接的地址。但是程序在片内内存中还是会继续执行,当执行到
bl disable_watch_dog
这个语句时,程序会跳转到disable_watch_dog去执行,由于bl是位置无关,所以跳转的时候只是PC(new) = PC + 偏移。
而对于ldr pc, =on_sdram这些指令来说则是位置有关,即让PC赋值为一个确切值。对于
=on_sdram来说,它则等于 30000018,所以程序会跳转到该位置中去执行,若SDRAM没初始化之前使用了这类位置有关的指令,则会出错。
所以在下列情况下:
void memsetup(void) { /* SDRAM 13个寄存器的值 */ unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON 0x00000700, //BANKCON0 0x00000700, //BANKCON1 0x00000700, //BANKCON2 0x00000700, //BANKCON3 0x00000700, //BANKCON4 0x00000700, //BANKCON5 0x00018005, //BANKCON6 0x00018005, //BANKCON7 0x008C07A3, //REFRESH 0x000000B1, //BANKSIZE 0x00000030, //MRSRB6 0x00000030, //MRSRB7 }; int i = 0; volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; for(; i < 13; i++) p[i] = mem_cfg_val[i]; }
mem_cfg_val的链接地址会大于0x30000000,这个时候SDRAM还没被初始化,当执行下列语句时:
p[i] = mem_cfg_val[i];
即程序会去 mem_cfg_val 所在位置取值,所以会出错。因为此时SDRAM没初始化。
但是对于下列这种情况则不会出错:
/* * 设置存储控制器以使用SDRAM */ void memsetup(void) { volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; /* 这个函数之所以这样赋值,而不是像前面的实验那样将配置值 * 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到 * SDRAM之前就可以在steppingstone中运行 */ /* 存储控制器13个寄存器的值 */ p[0] = 0x22011110; //BWSCON p[1] = 0x00000700; //BANKCON0 p[2] = 0x00000700; //BANKCON1 p[3] = 0x00000700; //BANKCON2 p[4] = 0x00000700; //BANKCON3 p[5] = 0x00000700; //BANKCON4 p[6] = 0x00000700; //BANKCON5 p[7] = 0x00018005; //BANKCON6 p[8] = 0x00018005; //BANKCON7 p[9] = 0x008C04F4; //REFRESH p[10] = 0x000000B1; //BANKSIZE p[11] = 0x00000030; //MRSRB6 p[12] = 0x00000030; //MRSRB7 }
虽然这段代码中p指针变量也是位于0x30000000之后,但是p[0],p[1],这些值会被编译器编译成与MEM_CTL_BASE相关的值,也就是与SDRAM初始化有关的寄存器的地址。所以此时不管p指针位于哪也与它的地址无关。
新手一枚还在进步中,有说错的地方希望各位大神能够为我指出,也欢迎各位和我一起交流讨论。
相关文章推荐
- 内部类、匿名内部类、异常及异常处理
- C++11新特性应用--介绍几个新增的便利算法(不更改容器中元素顺序的算法)
- C++11新特性应用--介绍几个新增的便利算法(不更改容器中元素顺序的算法)
- Python数据类型-序列(元组)
- java.lang.NoClassDefFoundError: org/eclipse/jetty/util/thread/QueuedThreadPool$1
- 6、面向对象
- OpenCV Haar AdaBoost源代码改进(比EMCV快6倍)
- python数据类型(字符串)
- php
- java中不同进制数据表现
- python 利用正则过滤表情符号
- 从gitbook将书籍导入到github中
- Java基础_08_Java泛型
- Thinkphp中的条件分页
- Java基础之集合:概览
- windows 上执行python pywin32.exe
- C/C++——C++中new与malloc的10点区别
- 判断鼠标是否在指定区域代码
- C++ 输入函数getline(cin,str) 与cin.getline(str,int)区别
- 通过RelativeLayout.LayoutParams.addRule()方法在代码中设置RelativeLayout相关属性