关于ARM指令中位置无关和位置相关代码的认识【转】

本文转载自:https://blog.csdn.net/talent_CYJ/article/details/50533153

今天在一个问题上折腾了又是半天。就是在学JZ2440串口通信的时候,在sdram初始化函数中有这么一句话
  • 1
  • 2
/*
 * 设置存储控制器以使用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 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

对于前面实验,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]; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

对于这个串口程序,它的链接文件如下:

SECTIONS {
    . = 0x30000000;
    .text          :   { *(.text) }
    .rodata ALIGN(4) : {*(.rodata)} .data ALIGN(4) : { *(.data) } .bss ALIGN(4) : { *(.bss) *(COMMON) } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

表示该程序“应该”放在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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

程序运行时,对于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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

前面表示了程序链接的地址。但是程序在片内内存中还是会继续执行,当执行到

bl  disable_watch_dog 
  • 1

这个语句时,程序会跳转到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]; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

mem_cfg_val的链接地址会大于0x30000000,这个时候SDRAM还没被初始化,当执行下列语句时:

        p[i] = mem_cfg_val[i];
  • 1

即程序会去 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 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

虽然这段代码中p指针变量也是位于0x30000000之后,但是p[0],p[1],这些值会被编译器编译成与MEM_CTL_BASE相关的值,也就是与SDRAM初始化有关的寄存器的地址。所以此时不管p指针位于哪也与它的地址无关。

新手一枚还在进步中,有说错的地方希望各位大神能够为我指出,也欢迎各位和我一起交流讨论。

猜你喜欢

转载自www.cnblogs.com/zzb-Dream-90Time/p/9432234.html