当我们写链接器脚本的时候,我们会设置代码段的起始链接地址为0x30008000(S3C2440)。如果对链接地址理解错误的话,可能会出现像我这样的问题。
Q:gboot的链接器脚本里写的其实链接地址是内存中的0x30008000,再用交叉工具反汇编得到的start.S前几行代码地址都变成了0x30008000+,不太对啊。ARM启动流程里面讲处理器将NAND中的代码复制了4K到垫脚石中,垫脚石的地址是0x0,也正是因为这样,我们设置的异常向量表才有意义,处理器才能准确的找到处理异常的标号。但是把链接地址改成了0x30008000还怎么找?
A:首先要理解链接地址。我认为,链接地址就是为程序假想了一个起点,之后的代码都是在这个假想的起点基础上展开的。我们只有根据这个链接地址,才能找到后续的一些指令并执行。但是这个链接地址不是实际的指令的地址,我们先对gboot.elf进行反汇编看看
30008000 <_start>: 30008000: ea000014 b 30008058 <reset> 30008004: e59ff014 ldr pc, [pc, #20] ; 30008020 <_undefined_instruction> 30008008: e59ff014 ldr pc, [pc, #20] ; 30008024 <_software_intrrupt> 3000800c: e59ff014 ldr pc, [pc, #20] ; 30008028 <_prefetch_abort> 30008010: e59ff014 ldr pc, [pc, #20] ; 3000802c <_data_abort> 30008014: e59ff014 ldr pc, [pc, #20] ; 30008030 <_not_used> 30008018: e59ff014 ldr pc, [pc, #20] ; 30008034 <_irq> 3000801c: e59ff014 ldr pc, [pc, #20] ; 30008038 <_fiq>
用b reset举例,虽然reset标号的地址已经到了0x30008000,但是我们的代码此时实际上是在垫脚石中运行的,也就是说PC指针还是指在0x0的位置。这样0x0和0x30008000就形成了一个映射的关系,即0x4相当于0x30008004,0x8相当于0x30008008......,都是因为我们将链接起始地址设成了0x30008000,这也就是我们所说的相对跳转。
Q:为什么链接起始地址要写成0x30008000?
A:照理说这个地址是随意的,只要在S3C2440的寻址空间内都可以。但是后期我们要执行C语言程序,而对堆栈等的初始化都是对SDRAM进行的,即在0x30000000之后的64M空间中。所以为了C语言能够成功运行,直接将代码的链接地址设到内存中,这样方面之后的绝对跳转,即
ldr pc, =main