搞ARM开发时,在连接目标代码会提到运行地址和加载地址。这两者有什么区别呢?其次,网上也有说链接地址和存储地址,那么这四个地址之间有什么区别?
1、运行地址<--->链接地址:他们两个是等价的,只是两种不同的说法。
2、加载地址<--->存储地址:他们两个是等价的,也是两种不同的说法。
运行地址:程序在SRAM、SDRAM中执行时的地址。就是执行这条指令时,PC应该等于这个地址,换句话说,PC等于这个地址时,这条指令应该保存在这个地址内。
加载地址:程序保存在Nand flash中的地址。
位置无关码:B、BL、MOV都是位置位置无关码。
位置有关码:LDR PC,=LABEL等类似的代码都是位置有关码。
下面我们来看看一个Makefile文件
sdram.bin : head.S leds.c
arm-linux-gcc -c -o head.o head.S
arm-linux-gcc -c -o leds.o leds.c
arm-linux-ld -Ttext 0x30000000 head.o leds.o -o sdram_elf
arm-linux-objcopy -O binary -S sdram_elf sdram.bin
arm-linux-objdump -D -m arm sdram_elf > sdram.dis
clean:
rm -f sdram.dis sdram.bin sdram_elf *.o
我们可以看到sdram_elf的代码段是从0x30000000地址开始存放,这个地址我们称之为运行地址。为什么从这个地址开始存放,因为SDRAM的起始地址是0x30000000.
下面来看看一个启动代码
@*************************************************************************
@ File:head.S
@ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
@*************************************************************************
.equ MEM_CTL_BASE, 0x48000000
.equ SDRAM_BASE, 0x30000000
.text
.global _start
_start:
bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
bl memsetup @ 设置存储控制器
bl copy_steppingstone_to_sdram @ 复制代码到SDRAM中
ldr pc, =on_sdram @ 跳到SDRAM中继续执行
on_sdram:
ldr sp, =0x34000000 @ 设置堆栈
bl main
halt_loop:
b halt_loop
disable_watch_dog:
@ 往WATCHDOG寄存器写0即可
mov r1, #0x53000000
mov r2, #0x0
str r2, [r1]
mov pc, lr @ 返回
copy_steppingstone_to_sdram:
@ 将Steppingstone的4K数据全部复制到SDRAM中去
@ Steppingstone起始地址为0x00000000,SDRAM中起始地址为0x30000000
mov r1, #0
ldr r2, =SDRAM_BASE
mov r3, #4*1024
1:
ldr r4, [r1],#4 @ 从Steppingstone读取4字节的数据,并让源地址加4
str r4, [r2],#4 @ 将此4字节的数据复制到SDRAM中,并让目地地址加4
cmp r1, r3 @ 判断是否完成:源地址等于Steppingstone的未地址?
bne 1b @ 若没有复制完,继续
mov pc, lr @ 返回
memsetup:
@ 设置存储控制器以便使用SDRAM等外设
mov r1, #MEM_CTL_BASE @ 存储控制器的13个寄存器的开始地址
adrl r2, mem_cfg_val @ 这13个值的起始存储地址
add r3, r1, #52 @ 13*4 = 54
1:
ldr r4, [r2], #4 @ 读取设置值,并让r2加4
str r4, [r1], #4 @ 将此值写入寄存器,并让r1加4
cmp r1, r3 @ 判断是否设置完所有13个寄存器
bne 1b @ 若没有写成,继续
mov pc, lr @ 返回
.align 4
mem_cfg_val:
@ 存储控制器13个寄存器的设置值
.long 0x22011110 @ BWSCON
.long 0x00000700 @ BANKCON0
.long 0x00000700 @ BANKCON1
.long 0x00000700 @ BANKCON2
.long 0x00000700 @ BANKCON3
.long 0x00000700 @ BANKCON4
.long 0x00000700 @ BANKCON5
.long 0x00018005 @ BANKCON6
.long 0x00018005 @ BANKCON7
.long 0x008C07A3 @ REFRESH
.long 0x000000B1 @ BANKSIZE
.long 0x00000030 @ MRSRB6
.long 0x00000030 @ MRSRB7
下面来看看反汇编代码
sdram_elf: file format elf32-littlearm
Disassembly of section .text:
30000000 <_start>:
30000000: eb000005 bl 3000001c <disable_watch_dog>
30000004: eb000010 bl 3000004c <memsetup>
30000008: eb000007 bl 3000002c <copy_steppingstone_to_sdram>
3000000c: e59ff090 ldr pc, [pc, #144] ; 300000a4 <mem_cfg_val+0x34>
30000010 <on_sdram>:
30000010: e3a0d30d mov sp, #872415232 ; 0x34000000
30000014: eb000033 bl 300000e8 <main>
30000018 <halt_loop>:
30000018: eafffffe b 30000018 <halt_loop>
3000001c <disable_watch_dog>:
3000001c: e3a01453 mov r1, #1392508928 ; 0x53000000
30000020: e3a02000 mov r2, #0
30000024: e5812000 str r2, [r1]
30000028: e1a0f00e mov pc, lr
3000002c <copy_steppingstone_to_sdram>:
3000002c: e3a01000 mov r1, #0
30000030: e3a02203 mov r2, #805306368 ; 0x30000000
30000034: e3a03a01 mov r3, #4096 ; 0x1000
30000038: e4914004 ldr r4, [r1], #4
3000003c: e4824004 str r4, [r2], #4
30000040: e1510003 cmp r1, r3
30000044: 1afffffb bne 30000038 <copy_steppingstone_to_sdram+0xc>
30000048: e1a0f00e mov pc, lr
3000004c <memsetup>:
3000004c: e3a01312 mov r1, #1207959552 ; 0x48000000
30000050: e28f2018 add r2, pc, #24
30000054: e1a00000 nop ; (mov r0, r0)
30000058: e2813034 add r3, r1, #52 ; 0x34
3000005c: e4924004 ldr r4, [r2], #4
30000060: e4814004 str r4, [r1], #4
30000064: e1510003 cmp r1, r3
30000068: 1afffffb bne 3000005c <memsetup+0x10>
3000006c: e1a0f00e mov pc, lr
30000070 <mem_cfg_val>:
30000070: 22011110 andcs r1, r1, #4
30000074: 00000700 andeq r0, r0, r0, lsl #14
30000078: 00000700 andeq r0, r0, r0, lsl #14
3000007c: 00000700 andeq r0, r0, r0, lsl #14
30000080: 00000700 andeq r0, r0, r0, lsl #14
30000084: 00000700 andeq r0, r0, r0, lsl #14
30000088: 00000700 andeq r0, r0, r0, lsl #14
3000008c: 00018005 andeq r8, r1, r5
30000090: 00018005 andeq r8, r1, r5
30000094: 008c07a3 addeq r0, ip, r3, lsr #15
30000098: 000000b1 strheq r0, [r0], -r1
3000009c: 00000030 andeq r0, r0, r0, lsr r0
300000a0: 00000030 andeq r0, r0, r0, lsr r0
300000a4: 30000010 andcc r0, r0, r0, lsl r0
300000a8: e1a00000 nop ; (mov r0, r0)
300000ac: e1a00000 nop ; (mov r0, r0)
300000b0 <wait>:
300000b0: e52db004 push {fp} ; (str fp, [sp, #-4]!)
300000b4: e28db000 add fp, sp, #0
300000b8: e24dd00c sub sp, sp, #12
300000bc: e50b0008 str r0, [fp, #-8]
300000c0: ea000002 b 300000d0 <wait+0x20>
300000c4: e51b3008 ldr r3, [fp, #-8]
300000c8: e2433001 sub r3, r3, #1
300000cc: e50b3008 str r3, [fp, #-8]
300000d0: e51b3008 ldr r3, [fp, #-8]
300000d4: e3530000 cmp r3, #0
300000d8: 1afffff9 bne 300000c4 <wait+0x14>
300000dc: e28bd000 add sp, fp, #0
300000e0: e8bd0800 pop {fp}
300000e4: e12fff1e bx lr
300000e8 <main>:
300000e8: e92d4800 push {fp, lr}
300000ec: e28db004 add fp, sp, #4
300000f0: e24dd008 sub sp, sp, #8
300000f4: e3a03000 mov r3, #0
300000f8: e50b3008 str r3, [fp, #-8]
300000fc: e59f3030 ldr r3, [pc, #48] ; 30000134 <main+0x4c>
30000100: e3a02b55 mov r2, #87040 ; 0x15400
30000104: e5832000 str r2, [r3]
30000108: e59f0028 ldr r0, [pc, #40] ; 30000138 <main+0x50>
3000010c: ebffffe7 bl 300000b0 <wait>
30000110: e59f3024 ldr r3, [pc, #36] ; 3000013c <main+0x54>
30000114: e3a02000 mov r2, #0
30000118: e5832000 str r2, [r3]
3000011c: e59f0014 ldr r0, [pc, #20] ; 30000138 <main+0x50>
30000120: ebffffe2 bl 300000b0 <wait>
30000124: e59f3010 ldr r3, [pc, #16] ; 3000013c <main+0x54>
30000128: e3a02e1e mov r2, #480 ; 0x1e0
3000012c: e5832000 str r2, [r3]
30000130: eafffff4 b 30000108 <main+0x20>
30000134: 56000010 undefined instruction 0x56000010
30000138: 00007530 andeq r7, r0, r0, lsr r5
3000013c: 56000014 undefined instruction 0x56000014
Disassembly of section .ARM.attributes:
00000000 <.ARM.attributes>:
0: 00002541 andeq r2, r0, r1, asr #10
4: 61656100 cmnvs r5, r0, lsl #2
8: 01006962 tsteq r0, r2, ror #18
c: 0000001b andeq r0, r0, fp, lsl r0
10: 00543405 subseq r3, r4, r5, lsl #8
14: 01080206 tsteq r8, r6, lsl #4
18: 04120109 ldreq r0, [r2], #-265 ; 0x109
1c: 01150114 tsteq r5, r4, lsl r1
20: 01180317 tsteq r8, r7, lsl r3
24: Address 0x00000024 is out of bounds.
Disassembly of section .comment:
00000000 <.comment>:
0: 3a434347 bcc 10d0d24 <SDRAM_BASE-0x2ef2f2dc>
4: 74632820 strbtvc r2, [r3], #-2080 ; 0x820
8: 312d676e teqcc sp, lr, ror #14
c: 312e362e teqcc lr, lr, lsr #12
10: 2e342029 cdpcs 0, 3, cr2, cr4, cr9, {1}
14: 00332e34 eorseq r2, r3, r4, lsr lr
当我们从Nand flash启动时,硬件会自动将Nand flash前4kB代码拷贝到片内SRAM中,然后CPU从SRAM的0x00000000地址处开始执行程序。在这里我想纠正一个错误的观点,网上很多人都说是CPU自动把Nand flash前4kB代码拷贝到片内SRAM中,其实不然,是Nand flash控制器完成的,这个过程中CPU根本就没有参与 。
通过上面的Makefile文件,我们可以知道 bl disable_watch_dog 这条指令的运行地址是0x30000000,但是它现在保存在SRAM的0x00000000的地址处,那么这条指令能够正确执行吗?of course,why?
因为这条指令 bl disable_watch_dog 是位置无关码,虽然它的运行地址是在SDRAM中的0x30000000,但是它可以在Steppingstone中执行。这条指令的功能是跳转到标号disable_watch_dog 处执行,它是一个相对跳转,PC=当前PC的值+偏移量OFFSET。其中当前PC的值等于下两条指令的地址,通过反汇编可以看到下两条指令的地址为0x0000 0008,而不是0x3000 0008.因为现在指令是保存在SRAM中。
这条指令的机器码为eb00 0005,将机器码低24位按符号位扩展成32位得到0x0000 00005.然后将0x0000 0005左移2位得到0x0000 0014。这个值就是偏移量OFFSET=0X0000 0014。所以PC=0X0000 0008+0X0000 0014=0X0000 001c.那么CPU就会到SRAM中的0x0000 001c地址处执行程序。
可以发现bl指令依赖当前PC的值,这个特性使得bl指令不依赖指令的运行地址。所以接下来的bl mensetup ,bl cope_steppingstone_to_sdram都能够执行。
接下来的ldr pc,=on_sdram是一条位置有关码,经过反汇编可以看到,它是当前pc的值+偏移量,得到一个地址Addr,然后从内存中的这个地址去取数据Data赋给pc。现在我们计算一下Addr这个地址。
Addr=当前PC的值+144。当前PC的值等于下两条指令的地址0x0000 0014,而不是0x3000 0014,因为现在程序是保存在SRAM中。所以Addr=0x0000 0014+144=0x0000 0014+0x0000 0090=0x0000 00a4.那么这个地址0x0000 00a4中保存了什么数据。通过反汇编可以看到SRAM中的0x0000 00a4这个地址保存的机器码为30000010,所以cpu会跳转到0x30000010这个地址处执行程序。这个地址0x30000010
是在SDRAM中,这样程序就跳到SDRAM中去了。因为我们前面的指令 bl cope_stepping_to_sdram 已经把SRAM中4kB的程序拷贝到了SDRAM中,所以现在SDRAM中有程序了。
但是如果在程序的开头放置一条这样的指令:ldr pc,=disable_watch_dog ,那么整个程序就不能够正确执行了。why?
我们先来看看启动代码
@*************************************************************************
@ File:head.S
@ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
@*************************************************************************
.equ MEM_CTL_BASE, 0x48000000
.equ SDRAM_BASE, 0x30000000
.text
.global _start
_start:
@bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
ldr pc, =disable_watch_dog
bl memsetup @ 设置存储控制器
bl copy_steppingstone_to_sdram @ 复制代码到SDRAM中
ldr pc, =on_sdram @ 跳到SDRAM中继续执行
on_sdram:
ldr sp, =0x34000000 @ 设置堆栈
bl main
halt_loop:
b halt_loop
disable_watch_dog:
@ 往WATCHDOG寄存器写0即可
mov r1, #0x53000000
mov r2, #0x0
str r2, [r1]
mov pc, lr @ 返回
copy_steppingstone_to_sdram:
@ 将Steppingstone的4K数据全部复制到SDRAM中去
@ Steppingstone起始地址为0x00000000,SDRAM中起始地址为0x30000000
mov r1, #0
ldr r2, =SDRAM_BASE
mov r3, #4*1024
1:
ldr r4, [r1],#4 @ 从Steppingstone读取4字节的数据,并让源地址加4
str r4, [r2],#4 @ 将此4字节的数据复制到SDRAM中,并让目地地址加4
cmp r1, r3 @ 判断是否完成:源地址等于Steppingstone的未地址?
bne 1b @ 若没有复制完,继续
mov pc, lr @ 返回
memsetup:
@ 设置存储控制器以便使用SDRAM等外设
mov r1, #MEM_CTL_BASE @ 存储控制器的13个寄存器的开始地址
adrl r2, mem_cfg_val @ 这13个值的起始存储地址
add r3, r1, #52 @ 13*4 = 54
1:
ldr r4, [r2], #4 @ 读取设置值,并让r2加4
str r4, [r1], #4 @ 将此值写入寄存器,并让r1加4
cmp r1, r3 @ 判断是否设置完所有13个寄存器
bne 1b @ 若没有写成,继续
mov pc, lr @ 返回
.align 4
mem_cfg_val:
@ 存储控制器13个寄存器的设置值
.long 0x22011110 @ BWSCON
.long 0x00000700 @ BANKCON0
.long 0x00000700 @ BANKCON1
.long 0x00000700 @ BANKCON2
.long 0x00000700 @ BANKCON3
.long 0x00000700 @ BANKCON4
.long 0x00000700 @ BANKCON5
.long 0x00018005 @ BANKCON6
.long 0x00018005 @ BANKCON7
.long 0x008C07A3 @ REFRESH
.long 0x000000B1 @ BANKSIZE
.long 0x00000030 @ MRSRB6
.long 0x00000030 @ MRSRB7
对应的反汇编代码
sdram_elf: file format elf32-littlearm
Disassembly of section .text:
30000000 <_start>:
30000000: e59ff09c ldr pc, [pc, #156] ; 300000a4 <mem_cfg_val+0x34>
30000004: eb000010 bl 3000004c <memsetup>
30000008: eb000007 bl 3000002c <copy_steppingstone_to_sdram>
3000000c: e59ff094 ldr pc, [pc, #148] ; 300000a8 <mem_cfg_val+0x38>
30000010 <on_sdram>:
30000010: e3a0d30d mov sp, #872415232 ; 0x34000000
30000014: eb000033 bl 300000e8 <main>
30000018 <halt_loop>:
30000018: eafffffe b 30000018 <halt_loop>
3000001c <disable_watch_dog>:
3000001c: e3a01453 mov r1, #1392508928 ; 0x53000000
30000020: e3a02000 mov r2, #0
30000024: e5812000 str r2, [r1]
30000028: e1a0f00e mov pc, lr
3000002c <copy_steppingstone_to_sdram>:
3000002c: e3a01000 mov r1, #0
30000030: e3a02203 mov r2, #805306368 ; 0x30000000
30000034: e3a03a01 mov r3, #4096 ; 0x1000
30000038: e4914004 ldr r4, [r1], #4
3000003c: e4824004 str r4, [r2], #4
30000040: e1510003 cmp r1, r3
30000044: 1afffffb bne 30000038 <copy_steppingstone_to_sdram+0xc>
30000048: e1a0f00e mov pc, lr
3000004c <memsetup>:
3000004c: e3a01312 mov r1, #1207959552 ; 0x48000000
30000050: e28f2018 add r2, pc, #24
30000054: e1a00000 nop ; (mov r0, r0)
30000058: e2813034 add r3, r1, #52 ; 0x34
3000005c: e4924004 ldr r4, [r2], #4
30000060: e4814004 str r4, [r1], #4
30000064: e1510003 cmp r1, r3
30000068: 1afffffb bne 3000005c <memsetup+0x10>
3000006c: e1a0f00e mov pc, lr
30000070 <mem_cfg_val>:
30000070: 22011110 andcs r1, r1, #4
30000074: 00000700 andeq r0, r0, r0, lsl #14
30000078: 00000700 andeq r0, r0, r0, lsl #14
3000007c: 00000700 andeq r0, r0, r0, lsl #14
30000080: 00000700 andeq r0, r0, r0, lsl #14
30000084: 00000700 andeq r0, r0, r0, lsl #14
30000088: 00000700 andeq r0, r0, r0, lsl #14
3000008c: 00018005 andeq r8, r1, r5
30000090: 00018005 andeq r8, r1, r5
30000094: 008c07a3 addeq r0, ip, r3, lsr #15
30000098: 000000b1 strheq r0, [r0], -r1
3000009c: 00000030 andeq r0, r0, r0, lsr r0
300000a0: 00000030 andeq r0, r0, r0, lsr r0
300000a4: 3000001c andcc r0, r0, ip, lsl r0
300000a8: 30000010 andcc r0, r0, r0, lsl r0
300000ac: e1a00000 nop ; (mov r0, r0)
300000b0 <wait>:
300000b0: e52db004 push {fp} ; (str fp, [sp, #-4]!)
300000b4: e28db000 add fp, sp, #0
300000b8: e24dd00c sub sp, sp, #12
300000bc: e50b0008 str r0, [fp, #-8]
300000c0: ea000002 b 300000d0 <wait+0x20>
300000c4: e51b3008 ldr r3, [fp, #-8]
300000c8: e2433001 sub r3, r3, #1
300000cc: e50b3008 str r3, [fp, #-8]
300000d0: e51b3008 ldr r3, [fp, #-8]
300000d4: e3530000 cmp r3, #0
300000d8: 1afffff9 bne 300000c4 <wait+0x14>
300000dc: e28bd000 add sp, fp, #0
300000e0: e8bd0800 pop {fp}
300000e4: e12fff1e bx lr
300000e8 <main>:
300000e8: e92d4800 push {fp, lr}
300000ec: e28db004 add fp, sp, #4
300000f0: e24dd008 sub sp, sp, #8
300000f4: e3a03000 mov r3, #0
300000f8: e50b3008 str r3, [fp, #-8]
300000fc: e59f3030 ldr r3, [pc, #48] ; 30000134 <main+0x4c>
30000100: e3a02b55 mov r2, #87040 ; 0x15400
30000104: e5832000 str r2, [r3]
30000108: e59f0028 ldr r0, [pc, #40] ; 30000138 <main+0x50>
3000010c: ebffffe7 bl 300000b0 <wait>
30000110: e59f3024 ldr r3, [pc, #36] ; 3000013c <main+0x54>
30000114: e3a02000 mov r2, #0
30000118: e5832000 str r2, [r3]
3000011c: e59f0014 ldr r0, [pc, #20] ; 30000138 <main+0x50>
30000120: ebffffe2 bl 300000b0 <wait>
30000124: e59f3010 ldr r3, [pc, #16] ; 3000013c <main+0x54>
30000128: e3a02e1e mov r2, #480 ; 0x1e0
3000012c: e5832000 str r2, [r3]
30000130: eafffff4 b 30000108 <main+0x20>
30000134: 56000010 undefined instruction 0x56000010
30000138: 00007530 andeq r7, r0, r0, lsr r5
3000013c: 56000014 undefined instruction 0x56000014
Disassembly of section .ARM.attributes:
00000000 <.ARM.attributes>:
0: 00002541 andeq r2, r0, r1, asr #10
4: 61656100 cmnvs r5, r0, lsl #2
8: 01006962 tsteq r0, r2, ror #18
c: 0000001b andeq r0, r0, fp, lsl r0
10: 00543405 subseq r3, r4, r5, lsl #8
14: 01080206 tsteq r8, r6, lsl #4
18: 04120109 ldreq r0, [r2], #-265 ; 0x109
1c: 01150114 tsteq r5, r4, lsl r1
20: 01180317 tsteq r8, r7, lsl r3
24: Address 0x00000024 is out of bounds.
Disassembly of section .comment:
00000000 <.comment>:
0: 3a434347 bcc 10d0d24 <SDRAM_BASE-0x2ef2f2dc>
4: 74632820 strbtvc r2, [r3], #-2080 ; 0x820
8: 312d676e teqcc sp, lr, ror #14
c: 312e362e teqcc lr, lr, lsr #12
10: 2e342029 cdpcs 0, 3, cr2, cr4, cr9, {1}
14: 00332e34 eorseq r2, r3, r4, lsr lr
通过反汇编代码我们可以看到:第一条指令
ldr pc,=disable_watch_dog 对应的反汇编代码为30000000: e59ff09c ldr pc, [pc, #156] ; 300000a4 <mem_cfg_val+0x34>
其中pc=下两条指令的地址=0x0000 0008,立即数156对应的16进制为为0x0000 009c,0x0000 0008+0x0000 009c=0x0000 00a4.而SRAM中的0x0000 00a4这个地址中保存的机器码为3000001c,所以执行完这条指令后pc=0x3000 001c,0x3000 001c这个地址是SDRAM中的,而现在SDRAM中什么都没有,所以程序不能正确执行。
在嵌入式编程中,我们经常讲程序保存在 nand flash中。但是我们知道,nand flash的接口设计和 RAM 的接口设计是不一样的。
他的 数据线通常都是复用的,所以通常存取都是以块为单位(nor flash带有RAM接口,有足够的地址线来寻址,
所以可以访问内存中每一个字节) 这导致了,nand flash不可以片内执行程序(nor flash可以,因为他能存取内存每一个字节)
对于 s3c2440 来说,当使用 nand flash 启动时,为了解决 nand flash 不能片内执行程序的问题(片内不能执行,那么程序烧进去不是不能运行嘛)
于是 s3c2440 在内部有一个 叫 Stepping Stone(垫脚石?)的东西,其实他就是一块 4KB 的RAM,当我们以 nand flash启动想运行烧写在上面
的程序是,s3c2440会自动将 nand flash 前面 4KB 的内容拷贝到 这个叫Stepping Stone的片内内存中。
然后 pc指针为0 从这个片内内存0地址开始运行。
但是这个 片内内存只有 4Kb 大小,如果我们烧到 nand falsh中的程序大于 4KB 那么只有一部分被考到了片内内存中去执行。剩下的就不能执行了。
但是我们不是可以接外设吗, s3c2440的BANK6 和 BANK7都可以接最大 128M的SDRAM。SDRAM是一个RAM(内存)
那么当程序大于 4k 的时候,当我们以 nand flash启动后,前面的4Kb 被拷贝到 片内RAM中去执行(自动完成)。
我们在这前4K的程序中初始化SDRAM(SDRAM 使用前需要初始化) ,然后将剩下的程序拷贝到 SDRAM中(不是只有4kb 被拷贝到片内RAM中执行了嘛)
然后跳转到 SDRAM中去执行剩下的程序。
那么也就是说 通常当程序大于 4kb的 时候,我们就需要把程序拷贝到SDRAM中去运行。(程序小于4KB 那么也就可以不用拷贝了,以nand flash方式 启动后,程序全被拷贝到 片内4kb的 RAM中去运行。)。
那么,既然程序大于4kb的时候需要从nand flash中拷贝到 SDRAM中去运行。自然可以想到 烧到nand flash中的程序前面一部分代码应该
是初始化SDRAM(程序最终需要拷贝到SDRAM中去运行)和 将NAND flash中的剩余的程序拷贝到SDRAM中去(全考过去也行,方便点),然后跳转到SDRAM中执行。
下面我们就要详细说明,前面这一段跳转到 SDRAM去执行前在片内内存中运行的初始化代码的要点和细节。也就是 关于程序运行地址和加载地址以及位置无关指令的 一些注意点和细节
先来看下程序运行地址和加载地址
看个 随便写的简单的示例,这是一个连接脚本中的一段 first 0x30000000 : AT(0){main.o}
我们只注意 0x30000000 和 AT(4096) 这两处。
如果程序是烧到nand flash中。这句话里面的意思就是 a.o烧到nand flash 中从0地址(当然也可以是其他数)开始的地方,但他的运行地址是在
从0x30000000地址开始的地方。(为什么是0x30000000,应为 s3c2440的 bank6 和bank7 可以接sdram,bank6从地址0x30000000开始,我们的程序最终是要
在SDRAM中运行的)
烧到 nand flash中从地址0 开始的地方应该比较好理解,就是说我程序是存储在nand flash中最开始的地方。那么运行地址呢怎么理解呢
看一下断汇编
1 .text
2 .global _start
3 _start:
4 mov r0, #2
5 loop:
6 ldr pc,=loop
上面这段汇编,我们将 他的运行地址分别设为 0x0 和0x30000000来看看反汇编后的情况
先将运行地址设为0x0
all: test.c head.s
2 arm-linux-gcc -c -Wall -o head.o head.s
3 arm-linux-ld -Ttext 0x0 -o main_elf head.o
4 arm-linux-objdump -D -m arm main_elf > main.dis
5
6 clean:
7 rm -rf *.o main.dis main_elf
反汇编 main.dis如下:
6 00000000 <_start>:
7 0: e3a00002 mov r0, #2 ; 0x2
8
9 00000004 <loop>:
10 4: e51ff004 ldr pc, [pc, #-4] ; 8 <.text+0x8>
11 8: 00000004 andeq r0, r0, r4
将 arm-linux-ld -Ttext 0x0 -o main_elf head.o
改为 arm-linux-ld -Ttext 0x30000000 -o main_elf head.o
也就是将运行地址设定为0x30000000 再看看它的反汇编
6 30000000 <_start>:
7 30000000: e3a00002 mov r0, #2 ; 0x2
8
9 30000004 <loop>:
10 30000004: e51ff004 ldr pc, [pc, #-4] ; 30000008 <.text+0x8>
11 30000008: 30000004 andcc r0, r0, r4
注意最左边的数字,这就代表程序的运行时地址。也就是说程序的代码的地址是以运行地址为基址来标示的。什么意思呢,
看 head.s中的 ldr pc,=loop 这段,这是想让 程序调回到 loop处(死循环)
当运行地址设定为 0x0 时 从汇编代码我们看到 loop标号代表的地址为0000004
也就是说 ldr pc,=loop 反汇编为 ldr pc, [pc, #-4] 即pc值为pc-4地址里面放的值(4)。就是跳转到 00000004 地址去
那么把运行地址设定为0x30000000时, 从反汇编代码中我们看到 这时 loop表号代表的地址是0x30000000
那么 ldr pc,=loop 就是跳转到 地址 0x30000000
简单的理解就是 程序运行地址 就是 计算机认为程序运行时应该处于的地址。
所以 运行地址设置为0 时,程序中的所有代码的中的标号都是以0x0为基址的。计算机认为他运行的时候的地址是从 0x0开始的。
运行地址设置为0x30000000 时,程序中的所有代码的中的标号都是以0x30000000为基址的。计算机认为他运行的时候的地址是从0x30000000开始的
明白了 运行地址 的概念后,我们来看看程序的启动过程
假设现在程序的 加载地址(烧到nand flash中的位置)为0 运行地址为0x30000000
前面说过程序是烧写在 nand flash中从 0地址开始的地方,那么以nand flash方式启动后,该程序被拷贝到 片内ram中。这时候pc为0。并开始运行程序,也就是说,计算机现在从 片内内存地址0开始运行程序进行一些初始化操作并将sdram初始化后再跳转到sdram中去运行。
但是我们上面不是说,程序运行地址被设置成了 0x30000000(SDRAM的起始地址)吗。
但是程序在跳转到sdram之前却是在0x0地址开始运行。这就造成了前面这段还未跳转到sdram(从0x30000000开始)的程序的实际程序运行地址和设定的运行地址不符合。
如果程序中没有用到 地址有关代码,和全局变量静态变量之类地址有关变量,那么这段 在初始化SDRAM之前 的程序 其实还是可以运行下去的
并在初始化SDRAM后,跳转到SDRAM中去运行,一切正常
但是如果程序中用了这些代码。就不会运行成功了。
原因很简单,应为现在我们设定的 运行地址为 0x30000000,那么当我们执行地址有关代码 如 ldr pc,=A (A是个标号或函数名)
就是使用了 绝对地址,那么 pc = 0x3000000+x (x为标号A相对于起始也就是前面设定的运行地址的偏移) 。
那么这条指令执行之后,pc 指针将跳转到 0x30000000后的某处(SDRAM中)。但是 sdram 现在 还未初始化!
这就 造成了 错误。
同样 如果有全局变量 或静态变量也是。
所以我们需要使用 b bl类的 位置无关指令。 如 b A
b bl跳转是基于 pc的 跳转。即相对跳转 ,比如执行 b A 这条指令时,假设 现在pc =5. A标号相对当前的位置为2(在之后)
那么 b A 后 pc =pc +2 即计算机不管当前pc指针是多少,他执行的是相对于当前位置的跳转。也就避免了 向上面的那样跳转到了 0x30000000之后的未初始化的地址中去
1. 假设PC=0x1000, 现在 PC <--- 0x1200 ,这个0x1200就是绝对地址;如果是 PC + 0x20 这个020就是相对地址。
2. 绝对和相对路径是PC中文件目录结构中用的.绝对地址和相对地址在汇编中经常出现.关键是理解什么是绝对,什么是相对.
举个简单的例子:
在一个队伍中,从头开始你排在15位,这就是绝对位置,指从头开始.
如果前9个是女生,第10位开始为男生.那么你就是从男生开始的第5位,这个5就是相对位置.
在汇编中有绝对跳转和相对跳转指令.绝对跳转指令中给出的是绝对地址,也就是从0000H开始算起的地址.而相对跳转的指令中是相对地址,是说相对跳转指令本身所在地址加上一个(偏移)量.
如果转移指令在10的位置,要转移执行15位置的指令:可以使用
绝对转移:JMP 15 =======>这里的15是绝对地址,从0开始算.
相对转移:JNE 5 =======>这里的5时相对地址,相对JNE的位置,加5.而JNE在第10个,加上5为15,同上一句相同都是跳转到15的位置执行了.
3. 也就是说绝对地址是相对于地址0x0000的偏移量.而相对地址上相对于当前执行指令地址量来说的.
只要将被编译到0xC0000000地址的代码放到0x00000000地址开始执行,如果它们只
使用顺序执行或者相对跳转执行方式就可以正常运行,但如果使用了绝对寻址,那么程序就跑飞了。
因此我们在看linux内核启动这部分的代码时只能看到顺序执行指令和相对跳转指令,不会存在绝对跳转指令,就是这个原因。如果需要使用全局变量或者函数指针时,则需要将 这个地址减去0xC0000000的偏移量才可以获取到此时运行的地址,因为全局变量和函数指针在编译时都是按照0xC0000000基址编译出的绝对地址,运行时既然将程序段和数据段都偏移了0xC0000000的距离,那么使用时只要减去这个值就可以找到正确的位置了.
链接时指定链接地址为0X30000000的目的是为了让SRAM程序和SDRAM程序无缝连接,通过一条绝对跳转指令ldr,让程序跳转到SDRAM中"继续"运行。让CPU错误的以为程序刚开始就是运行在SDRAM的0x30000000上的。