重定位引入和链接脚本

1、一个事实:大部分指令是位置有关编码
位置无关编码(PIC,position independent code):汇编源文件被编码成二进制可执行程序时编码方式与位置(内存地址)无关。
位置有关编码:汇编源码编码成二进制可执行程序后和内存地址是有关的。

我们在设计一个程序时,会给这个程序指定一个运行地址(链接地址)。就是说我们在编译程序时其实心里是知道我们程序将来被运行时的地址(运行地址)的,而且必须给编译器链接器指定这个地址(链接地址)才行。最后得到的二进制程序理论上是和你指定的运行地址有关的,将来这个程序被执行时必须放在当时编译链接时给定的那个地址(链接地址)下才行,否则不能运行(就叫位置有关代码)。但是有个别特别的指令他可以跟指定的地址(链接地址)没有关系,也就是说这些代码实际运行时不管放在哪里都能正常运行。

对比:位置无关代码要好一些,适应性强,放在哪里都能正常运行;位置有关代码就必须运行在链接时指定的地址上,适应性差。位置无关码有一些限制,不能完成所有功能,有时候不得不使用位置有关代码。

2、链接地址和运行地址:可能相同也可能不同
对于位置有关代码来说:最终执行时的运行地址和编译链接时给定的链接地址必须相同,否则一定出错。
我们之前的裸机程序中,Makefile中用 -Ttext 0x0 来指定链接地址是0x0。这意味着我们认为这个程序将来会放在0x0这个内存地址去运行。
但是实际上我们运行时的地址是0xd0020010(我们用dnw下载时指定的下载地址)。这两个地址看似不同,但是实际相同。这是因为S5PV210内部做了映射,把SRAM映射到了0x0地址去。

清楚这两个概念:
链接地址:链接时指定的地址(指定方式为:Makefile中用-Ttext,或者链接脚本)
运行地址:程序实际运行时地址(指定方式:由实际运行时被加载到内存的哪个位置说了算)

3、再解S5PV210的启动过程:三星推荐和uboot的实现是不同的
三星推荐的启动方式中:bootloader必须小于96KB并大于16KB,假定bootloader为80KB,启动过程是这样子:先开机上电后BL0运行,BL0会加载外部启动设备中的bootloader的前16KB(BL1)到SRAM中去运行,BL1运行时会加载BL2(bootloader中80-16=64KB)到SRAM中(从SRAM的16KB处开始用)去运行;BL2运行时会初始化DDR并且将OS搬运到DDR去执行OS,启动完成。
uboot实际使用的方式:uboot大小随意,假定为200KB。启动过程是这样子:先开机上电后BL0运行,BL0会加载外部启动设备中的uboot的前16KB(BL1)到SRAM中去运行,BL1运行时会初始化DDR,然后将整个uboot搬运到DDR中,然后用一句长跳转(从SRAM跳转到DDR)指令从SRAM中直接跳转到DDR中继续执行uboot直到uboot完全启动。uboot启动后在uboot命令行中去启动OS。

4、为什么要重定位?
原因:
          链接地址和运行地址有时候必须不相同,而且还不能全部用位置无关码,这时候只能重定位。
扩展:

分散加载:把uboot分成2部分(BL1和整个uboot),两部分分别指定不同的链接地址。启动时将两部分加载到不同的地址(BL1加载到SRAM,整个uboot加载到DDR),这时候不用重定位也能启动。

评价:分散加载其实相当于手工重定位。重定位是用代码来进行重定位,分散加载是手工操作重定位的。

5、运行时地址由什么决定?链接地址由什么决定?
   运行时的地址是由运行时决定的(编译链接时是无法绝对确定运行时地址的)
   链接地址是由程序员在编译链接的过程中,通过Makefile中-Ttext

举例:

1、linux中的应用程序。gcc hello.c -o hello,这时使用默认的链接地址就是0x0,所以应用程序都是链接在0地址的。因为应用程序运行在操作系统的一个              进程中,在这个进程中这个应用程序独享4G的虚拟地址空间。所以应用程序都可以链接到0地址,因为每个进程都是从0地址开始的。(编译时可以不给定链            接地址而都使用0)
       2、210中的裸机程序。运行地址由我们下载时确定,下载时下载到0xd0020010,所以就从这里开始运行。(这个下载地址也不是我们随意定的,是iROM中             的BL0加载BL1时事先指定好的地址,这是由CPU的设计决定的)。所以理论上我们编译链接时应该将地址指定到0xd0020010,但是实际上我们在之前               裸机程序中都是使用位置无关码PIC,所以链接地址可以是0。

6、从源码到可执行程序的步骤:预编译、编译、链接、strip
   预编译: 预编译器执行。譬如C中的宏定义就是由预编译器处理,注释等也是由预编译器处理的。
   编译:    编译器来执行。把源码.c .S编程机器码.o文件。
   链接:    链接器来执行。把.o文件中的各函数(段)按照一定规则(链接脚本来指定)累积在一起,形成可执行文件。
   strip:   strip是把可执行程序中的符号信息给拿掉,以节省空间。(Debug版本和Release版本)
   objcopy:由可执行程序生成可烧录的镜像bin文件。

7、程序段的概念:代码段、数据段、bss段(ZI段)、自定义段
     段就是程序的一部分,我们把整个程序的所有东西分成了一个一个的段,给每个段起个名字,然后在链接时就可以用这个名字来指示这些段。也就是说给段命名        就是为了在链接脚本中用段名来让段站在核实的位置。

段名分为2种:一种是编译器链接器内部定好的,先天性的名字;一种是程序员自己指定的、自定义的段名。
先天性段名:
                代码段:  (.text),又叫文本段,代码段其实就是函数编译后生成的东西
                数据段: (.data),数据段就是C语言中有显式初始化为非0的全局变量
                bss段:  (.bss),又叫ZI(zero initial)段,就是零初始化段,对应C语言中初始化为0的全局变量。
后天性段名:段名由程序员自己定义,段的属性和特征也由程序员自己定义。

分析一些问题,跟这里结合,然后试图明白一些本质:
1、C语言中全局变量如果未显式初始化,值是0。本质就是C语言把这类全局变量放在了bss段,从而保证了为0
2、C运行时环境如何保证显式初始化为非0的全局变量的值在main之前就被赋值了?就是因为它把这类变量放在了.data段中,而.data段会在main执行之前被处理(初始化)。

8、链接脚本究竟要做什么?
      链接脚本其实是个规则文件,他是程序员用来指挥链接器工作的。链接器会参考链接脚本,并且使用其中规定的规则来处理.o文件中那些段,将其链接成一个可       执行程序。
      链接脚本的关键内容有2部分:段名 + 地址(作为链接地址的内存地址)
      链接脚本的理解:
                            SECTIONS {} 这个是整个链接脚本
                            . 点号在链接脚本中代表当前位置。
                            = 等号代表赋值

9、adr与ldr伪指令的区别
     ldr和adr都是伪指令,区别是ldr是长加载、adr是短加载。
重点:adr指令加载符号地址,加载的是运行时地址;ldr指令在加载符号地址时,加载的是链接地址。

深入分析:只要知道adr和ldr分别用于加载运行地址和链接地址,从而可以判断是否需要重定位即可;根本不需知道为什么adr和ldr是这样子,但是我们还是给大家                扩展讲下为什么adr和ldr可以加载不同的地址。

10、重定位(代码拷贝)
     重定位就是汇编代码中的copy_loop函数,代码的作用是使用循环结构来逐句复制代码到链接地址。
     复制的源地址是SRAM的0xd0020010,复制目标地址是SRAM的0xd0024000,复制长度是bss_start减去_start
     所以复制的长度就是整个重定位需要重定位的长度,也就是整个程序中代码段+数据段的长度。
     bss段(bss段中就是0初始化的全局变量)不需要重定位。
11、清bss段
      清除bss段是为了满足C语言的运行时要求(C语言要求显式初始化为0的全局变量,或者未显式初始化的全局变量的值为0,实际上C语言编译器就是通过清bss       段来实现C语言的这个特性的)。一般情况下我们的程序是不需要负责清零bss段的(C语言编译器和链接器会帮我们的程序自动添加一段头程序,这段程序会在       我们的main函数之前运行,这段代码就负责清除bss)。但是在我们代码重定位了之后,因为编译器帮我们附加的代码只是帮我们清除了运行地址那一份代码中       的bss,而未清除重定位地址处开头的那一份代码的bss,所以重定位之后需要自己去清除bss。
12、长跳转
     清理完bss段后重定位就结束了。然后当前的状况是:
  1、当前运行地址还在0xd0020010开头的(重定位前的)那一份代码中运行着。
  2、此时SRAM中已经有了2份代码,1份在d0020010开头,另一份在d0024000开头的位置。
     然后就要长跳转了。

时间: 2024-12-16 22:42:40

重定位引入和链接脚本的相关文章

链接脚本与重定位

目录 链接脚本与重定位 总结 链接脚本格式 COMM段BSS段 elf和bin文件 获得链接脚本的值 重定位 1-直接指定数据段位置(代码黑洞) 2-分散加载(数据段) 3-全局重定位(一体式) BL跳转指令 bss段处理 汇编处理 C处理 位置无关码 title: 链接脚本与重定位 tags: ARM date: 2018-10-12 19:25:53 --- 链接脚本与重定位 学习视频 韦东山 总结 尽量使用一体式的链接脚本,方便简单,灵活 学会使用链接脚本的值 bss段和comm段是需要我

嵌入式Linux裸机开发(四)——重定位relocate

嵌入式Linux裸机开发(四)--重定位relocate 一.位置有关编码 汇编源文件被编译成二进制可执行程序时编码方式可能与内存地址有关,也可能与内存地址无关.与内存地址有关的为位置有关编码,与内存地址无关的为位置无关编码. 程序在设计时需要规划一个程序运行时的地址(链接地址),编译连接器在链接时必须指定这个链接地址,得到的二进制程序的程序理论规划的运行时地址和编译连接器指定的链接地址才相同,程序才能正常运行.位置无关编码程序则无需设计程序时规划运行时地址,编译链接器链接时同样无需指定链接地址

ELF格式的重定位原理分析

前面有篇文章分析了ELF格式,也只是让我们对目标文件有了一个大概的了解,并没有说明一个十分重要的问题:重定位,今天重新看了下重定位的资料,终于弄懂了重定位的过程,下面来做一个分析. 我们将使用下面两个源代码中的文件a.c和b.c展开分析: //a.c extern int shared; int main() { int a=100; swap(&a,&shared); } //b.c int shared=1; void swap(int *a,int *b) { *a^=*b^=*a^

PE文件结构详解(六)重定位

前面两篇 PE文件结构详解(四)PE导入表 和 PE文件结构详解(五)延迟导入表 介绍了PE文件中比较常用的两种导入方式,不知道大家有没有注意到,在调用导入函数时系统生成的代码是像下面这样的: 在这里,IE的iexplorer.exe导入了Kernel32.dll的GetCommandLineA函数,可以看到这是个间接call,00401004这个地址的内存里保存了目的地址, 根据图中显示的符号信息可知,00401004这个地址是存在于iexplorer.exe模块中的,实际上也就是一项IAT的

重定位和链接脚本

大部分指令是"位置有关编码" 位置无关编码:汇编源文件编码成二进制可执行程序时,编码方式与位置无关. 在我们写程序时,必须给编译器链接器指定地址.将来的程序被执行时必须放在当时编译链接时给定的地址才能运行. 位置有关编码:汇编源码编码成二进制可执行程序后和内存地址是有关的. 但是也有一种特别的指令他可以跟指定的链接地址没有关系,这些代码不管放在哪里都可以正常运行. 分析: 之前的裸机程序中,makefile中用-Ttext 0x0来指定链接地址是0x0;这意味着我们认为将来这个程序会被

第二部分:S5PV210_重定位和链接脚本_2

重定位和链接脚本 (1)重定位:分为静态重定位和动态重定位 静态重定位:静态重定位是在程序执行之前进行重定位,它根据装配模块将要装入的内存起始位置,直接修改装配模块中的有关使用地址的指令 我们下面要分析就是静态重定位的情况. 动态重定位:动态重定位是指,不是在程序执行之前而是在程序执行过程中进行地址重定位.更确切地说,是在每次访问内存单元前才进行地址变换.动态重定位可使装配模块不加任何修改而装入内存,但是它需要硬件——定位寄存器的支持. 图片的参考来源:http://c.biancheng.ne

重定位与链接脚本

1.为什么需要重定位 位置无关编码(PIC,position independent code):汇编源文件被编码成二进制可执行程序时编码方式与位置(内存地址)无关. 位置有关编码:汇编源码编码成二进制可执行程序后和内存地址是有关的. 我们在设计一个程序时,会给这个程序指定一个运行地址(链接地址).就是说我们在编译程序时其实心里是知道我们程序将来被运行时的地址(运行地址)的,而且必须给编译器链接器指定这个地址(链接地址)才行.最后得到的二进制程序理论上是和你指定的运行地址有关的,将来这个程序被执

S5PV210裸机程序之重定位与链接脚本

s5pv210开发板要求代码在0xD0020010处开始执行(BL1),但是有时我们的程序需要重定位在另外一个地址执行(如uboot),这个时候需要我们在汇编代码处加入重定位操作的代码,使得程序能够长跳转到另外一个地址继续执行代码而不会因为执行了与地址有关编码而导致执行错误. 链接脚本如下: SECTIONS {     . = 0xD0024000;     .text : {         start.o         * (.text)         }     .data : {

韦东山yy公开课笔记(2)--汇编,段,栈,重定位/链接地址,位置无关吗

1. 要不要学习汇编 可以只懂一点,工作中基本不用,一旦用就是出了大问题 ldr : load 读内存 ldr r0, [r1]  : r1里存放的是地址值, 去这个地址读取4字节的内容,存入r0 str : stroe 写内存 str r0, [r1]  : r1里存放的是地址值, 把r0里的4字节数据存入这个地址 所有的汇编.C程序也好,终极目标就是:读写某个地址 2. 程序为何要分为代码段.数据段.BSS段 程序的指令等是只读的,可以把它们归为一类,以便运行时可以放在ROM等设备上, 当然