重定位和链接脚本

Posted 进心进利

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重定位和链接脚本相关的知识,希望对你有一定的参考价值。

大部分指令是“位置有关编码”

  • 位置无关编码:汇编源文件编码成二进制可执行程序时,编码方式与位置无关。

在我们写程序时,必须给编译器链接器指定地址。将来的程序被执行时必须放在当时编译链接时给定的地址才能运行。

  • 位置有关编码:汇编源码编码成二进制可执行程序后和内存地址是有关的。

但是也有一种特别的指令他可以跟指定的链接地址没有关系,这些代码不管放在哪里都可以正常运行。

分析:

  之前的裸机程序中,makefile中用-Ttext 0x0来指定链接地址是0x0;这意味着我们认为将来这个程序会被放在0x0这个地址去运行。但是实际上我们在DNW中,把程序下载到0xd0020010. 在s5pv210中,由于在内部做了映射,所以0xd002_0010和0x0000_0010是一样的。那么为什么放在0x0而不是0x0000_0010依旧可以呢?原因就这个是位置无关编码。

  • 链接地址:链接时指定的地址(指定方式:makefile中用T-text,或者链接脚本)
  • 运行地址:程序实际运行的地址(指定方式:由实际运行时被加载到内存那个位置说了算)

在linux中的应用程序:gcc hello.c -o hello

  • 这时候默认的链接地址就是0x0,所以链接在0地址,因为应用程序运行在操作系统的一个进程中,这个进程独享了4G的内存空间,所以应用程序可以链接到0地址,因为每个进程都是从0地址开始的。
  • 210中的裸机程序运行地址由我们下载时确定,下载时下载到0xd0020010,所以就从这里开始运行。(这个下载地址是iROM中的BL0加载BL1时实现指定好的地址,这个是由CPU设计时候决定的)。所以理论上我们编译链接时应该指定到0xd0020010,但是实际上我们之前的裸机程序都是使用位置无关码PIC,所以链接地址可以是0

再分析s5pv210的启动过程

三星推荐的启动方式:

  bootloader必须大于16KB小于96KB,假定为80KB。启动过程如下:开机上电后BL0运行,BL0加载外部启动设备中的bootloader的前16KB(BL1)到SRAM中运行,BL1运行后会加载BL2到(80-16=64KB)到SRAM中运行,BL2运行时会初始化DDR并且将OS搬到DDR中执行,启动完成。

uboot实际使用方式:

  uboot大小随意,假定为200KB,启动过程:先开机上电后BL0运行,BL0会加载外部启动设备中的uboot的前16KB到SRAM运行,BL1运行时会初始化DDR,然后将整个uboot搬运到DDR中,然后用一句长跳转指令从SRAM直接跳转到DDR中继续执行uboot直到uboot完全启动。uboot启动后在命令行中启动OS

为什么需要重定位?

  • 原因:链接地址和运行地址有时候必须不相同,而且不能全部用位置无关码,这种就只能重定位
  • 扩展:分散加载:把uboot分为2部分(BL1和整个uboot)两个部分分别制定不同的链接地址。启动时将两部分加载到不同的地址(BL1加载到SRAM,整个uboot加载到DDR),这时候不用重定位也能启动
  • 评价:分散加载其实相当于手工重定位,重定位是用代码来重定位,分散加载是手工操作重定位

从源码到可执行程序的步骤:

  1. 预编译:预编译器执行。譬如C中的宏定义就是由预编译处理器处理,注释等也是由预编译器处理的
  2. 编译:编译器来执行。把源码.c  .S编译成机器码
  3. 链接:连接器来执行。把.o文件中的各个程序按照一定的规则(链接脚本指定)累积在一起

strip:strip是把可执行程序中的符号给拿掉,以节省空间(debug版本和release版本)

objcopy:由可执行程序生成可烧录的镜像bin文件

程序段的概念:代码段、数据段、bss段(ZI段)、自定义段

段是程序的一部分,我们把整个程序的所有东西分成了一个个段,给每个段起一个名字,然后再链接时可以用和这个名字来指示这些段

段名分为2种:一种是编译器连接器内部定好的,一种是程序员自己指定的,自定义的段名

先天性段名:

  •   代码段:(.text),又叫文本段,代码段其实就是函数编译后生成的东西
  •   数据段:(.data),数据段就是C语言中显示初始化为非0的全局变量
  •   bss段:(.bss),又叫ZI(zero initial)段,就是零初始化段,对应C语言中初始化为0的全局变量

后天性段名:

   段名由程序员自己定义,段的属性和特征也由程序员自己定义。

分析一些问题,跟这里结合,然后试图明白一些本质

  • C语言中全局变量如果未显示初始化,值为0。本质上是因为C语言中我们将这类全局变量放在了bss段,从而保证了为0
  • C运行时环境如何保证显示初始化为非0的变量在main函数之前就被赋值了,就是因为把这类变量放在了.data段了,而.data段会在main函数之前被处理。

链接脚本就是一个规则文件,他是程序员用来指挥链接器用来工作的。链接器会参考链接脚本并且使用其中规定的规则来处理.o文件中的那些段,将其链接成可执行程序。

链接脚本的关键内容有两个部分:段名+地址(作为链接地址的内存地址)

链接脚本的理解:

SECTIONS{}这个是整体链接脚本

. 点号在链接脚本中代表当前位置

= 等号代表赋值

SECTIONS
{
    . = 0xd0024000
    
    .text : {
            start.o
            *(.text)
    }
    
    .data : {
        *(.data)
    }

    bss_start = .;
    .bss : {
        *(.bss)
    }
    bss_end = .;
}

 

以上是关于重定位和链接脚本的主要内容,如果未能解决你的问题,请参考以下文章

代码重定位

重定位和链接脚本

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

链接器中——链接脚本

重定位与链接脚本

重定位与链接脚本