关于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指针位于哪也与它的地址无关。

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

原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/9432234.html