对链接地址的理解

Posted on 2015-07-13 23:24  Bumble_Bee  阅读(474)  评论(0编辑  收藏  举报

  当我们写链接器脚本的时候,我们会设置代码段的起始链接地址为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