arm-linux-ld lds脚本文件

 

 

arm-linux-ld

 

1:b和ld的差异


  在开始后续实验之前,我们得了解一下arm-linux-ld连接命令的使用。在
上述实验中,我们一直使用类似如下的命令进行连接:
arm-linux-ld -Ttext 0x00000000 crt0.o led_on_c.o -o led_on_c_tmp.o
我们看看它是什么意思:-o选项设置输出文件的名字为 led_on_c_tmp.o;
“--Ttext 0x00000000”设置代码段的起始地址为0x00000000;这条指令的作用就
是将crt0.o和led_on_c.o连接成led_on_c_mp.o可执行文件,此可执行文件的代
码段起始地址为0x00000000。
我们感兴趣的就是“—Ttext”选项!进入LINK目录,link.s代码如下:
1 .text
2 .global _start
3 _start:  
4   b step1
5 step1:
6   ldr pc, =step2
7 step2:
8  b step2
 
Makefile如下:
1 link:link.s
2   arm-linux-gcc  -c -o link.o link.s
3  arm-linux-ld -Ttext 0x00000000   link.o -o link_tmp.o
4 #  arm-linux-ld -Ttext 0x30000000   link.o -o link_tmp.o
5  arm-linux-objcopy -O binary -S link_tmp.o link
6   arm-linux-objdump -D -b binary -m arm  link >ttt.s
7 #  arm-linux-objdump -D -b binary -m arm  link >ttt2.s
8 clean:
9  rm -f   link
10  rm -f   link.o
11  rm -f   link_tmp.o
 
实验步骤:
1.进入目录LINK,运行make生成arm-linux-ld选项为“-Ttext 0x00000000”
的反汇编码ttt.s
2.make clean
3.修改Makefile:将第4、7行的“#”去掉,在第3、6行前加上“#”
4.运行make生成arm-linux-ld选项为“-Ttext 0x30000000”的反汇编码ttt2.s
 
link.s程序中用到两种跳转方法:b跳转指令、直接向pc寄存器赋值。我们先
把在不同“—Ttext”选项下,生成的可执行文件的反汇编码列出来,再详细分析这
两种不同指令带来的差异。
ttt.s:          ttt2.s
0: eaffffff  b   0x4      0: eaffffff  b   0x4

4:  e59ff000  ldr pc, [pc, #0] ; 0xc    4:  e59ff000  ldr pc, [pc, #0] ; 0xc
8:  eafffffe  b   0x8       8:  eafffffe  b   0x8
c:  00000008  andeq r0, r0, r8     c:  30000008  tsteq r0, #8  ; 0x8
 
先看看 b 跳转指令:它是个相对跳转指令,其机器码格式如下:
 
[31:28]位是条件码;[27:24]位为“1010”时,表示 B 跳转指令,为“1011”时,表示 BL
跳转指令;[23:0]表示偏移地址。使用 B 或BL 跳转时,下一条指令的地址是这样计算的:将指
令中 24 位带符号的补码立即数扩展为 32(扩展其符号位);将此 32 位数左移两位;将得到的值
加到 pc 寄存器中, 即得到跳转的目标地址。 我们看看第一条指令 “b  step1” 的机器码 eaffffff: 
1. 24 位带符号的补码为 0xffffff,将它扩展为 32 得到:0xffffffff
2.将此 32 位数左移两位得到:0xfffffffc,其值就是-4
3.pc 的值是当前指令的下两条指令的地址,加上步骤 2 得到的-4,这恰好是第
二条指令step1 的地址
各位不要被被反汇编代码中的“b  0x4”给迷惑了,它可不是说跳到绝对地址 0x4
处执行,绝对地址得像上述 3 个步骤那样计算。您可以看到 b 跳转指令是依赖于当
前 pc 寄存器的值的, 这个特性使得使用b 指令的程序不依赖于代码存储的位置——
即不管我们连接命令中“--Ttext”为何,都可正确运行。
 
再看看第二条指令ldr pc, =step2:从反汇编码“ldr pc, [pc, #0]”可以看出,
这条指令从内存中某个位置读出数据,并赋给 pc 寄存器。这个位置的地址是当前
pc 寄存器的值加上偏移值 0,其中存放的值依赖于连接命令中的“--Ttext”选项。
执行这条指令后,对于 ttt.s,pc=0x00000008;对于 ttt2.s, pc=0x30000008。于
是执行第三条指令“b  step2”时,它的绝对地址就不同了:对于ttt.s,绝对地址
为 0x00000008;对于 ttt.s,绝对地址为0x30000008。
 
ttt2.s 上电后存放的位置也是 0,但是它连接的地址是 0x30000000。我们以后
会经常用到“存储地址和连接地址不同”(术语上称为加载时域和运行时域)的特性:
大多机器上电时是从地址 0 开始运行的,但是从地址 0 运行程序在性能方面总有很
多限制,所以一般在开始的时候,使用与位置无关的指令将程序本身复制到它的连
接地址处, 然后使用向pc 寄存器赋值的方法跳到连接地址开始的内存上去执行剩下
的代码。在实验 5、6 中,我们将会作进一步介绍。
arm-linux-ld 命令中选项“-Ttext”也可以使用选项“-Tfilexxx”来代替,在
文件 filexxx中, 我们可以写出更复杂的参数来使用 arm-linux-ld 命令——在实验
6 中,我们就是使用这种方法来指定连接参数的。

 

 

 

 

2:lds脚本文件

 

 

1 SECTIONS { 
2   firtst   0x00000000 : { head.o init.o }
3   second  0x30000000 : AT(4096) { main.o }
4 } 
 
 完整的连接脚本文件形式如下:
SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
  { contents } >region :phdr =fill
...
}
并非每个选项都是必须的,仅介绍nand.lds用到的:

1、secname:段名,对于nand.lds,段名为first和second
2、start:本段运行时的地址,如果没有使用AT(xxx),则本段存储的地址
也是start
3、AT( ldadr ):定义本段存储(加载)的地址
4、{ contents }:决定哪些内容放在本段,可以是整个目标文件,也可以
是目标文件中的某段(代码段、数据段等)
 
nand.lds的含义是:head.o放在0x00000000地址开始处,init.o放在hean.o
后面,它们的运行地址是0x00000000;main.o放在地址4096(0x1000)开始处,但
是它的运行地址在0x30000000,在运行前需要从4096处复制到0x30000000处。为
了更形象一点,您可以打开反汇编文件ttt.s,现摘取部分内容如下:
00000000 <.data>:
1      0: e3a0da01  mov sp, #4096 ; 0x1000
2      4: eb00000b  bl 0x38
3      8: eb000011  bl 0x54
4      c: eb000042  bl 0x11c

 ...
5   1000: e1a0c00d  mov ip, sp
6   1004: e92dd800  stmdb sp!, {fp, ip, lr, pc}
7   1008: e24cb004  sub fp, ip, #4 ; 0x4
8  100c: e59f1058  ldr r1, [pc, #88] ; 0x106c
 ...
 
上面的第1-4行与head.s中的前面4行代码对应,第2-4行调用init.c中的函
数disable_watch_dog、memsetup、init_nand;再看看第5行,“1000”的得来
正是由于设置了“AT(4096)”,这行开始的是main.c中的第一个函数Rand()。
如果您想进一步了解连接脚本如何编写,请参考《Using ld The GNU linker》
(在目录“参考资料”下)。
 

你可能感兴趣的:(bios)