关于elf结构及其编译连接的理解

今天研究了下elf格式和链接过程,权且自己理解,以便备忘,诸多问题,还值得商榷。

目前自己用过的elf不外乎两种:编译生成的可重定位文件,连接生成的可执行文件。对于elf文件,结构轮廓为

不过就目前看来,似乎用ld连接出来的可执行文件都有节区头部表,应该是为了附带一些信息。对于elf文件可用工具对其结构进行分析,这样比直接分析其二进制码文件方便些。

(上图为可重定位文件结构)

(上图为可执行文件结构)

对于elf文件各个子项都有其含义,较为繁琐,可查看《ELF 文件格式分析》帮助理解。

接下来分析下关于对于目标文件为elf文件的连接过程:

对于连接器而言,其连接的基本单位为各个可重定位文件的节区,也就是说连接器可以为可重定位文件的节区(section)指定其在连接后生成的目标文件(可执行文件)中的位置,包括其在文件内的位置和当其载入到内存后的位置,当确定好各个节区的位置后,连接器便可根据其位置和符号表,重定位表等信息,填入确定的地址信息,变生成可执行文件。这也就是连接器的工作。

对于ld来说,在linux下可用lds文件来指定连接具体过程,例:

ENTRY(_start) 
SECTIONS { 
  firtst    0x10000000 :{ leds.o }
  second    0xB0004000 :  AT(2048) {  init.o head.o}
}

这里指定了文件的入口点为_start,它表示在这几个可重定位文件的源文件内有一个_start的标号,当载入生成的可执行文件后,其入口点便为_start处的地址,这一点体现在elf文件的elf header里,其表项有一个e_entry,该值便指定了入口地址,至于该值的的得出,是由于当指定好了各个区段的位置后,_start处代码载入到内存的位置可以计算得出,当解决好入口地址后,便是具体指定各个节区的位置了。

在SECTIONS里面,指定了两项,便是可执行文件一共有两个程序段(仍然可以有节表和节表头,往往一个程序段便是一个节表项,上图节表项1,2便是first,second,节表0是指elf头,无意义,后面的节表项,实际意义不大),并且也指定了,载入到内存后的地址和文件内部偏移,此处易误解的地方为后面是*.o文件,而不是节区,个人实验来看,当后面是文件,而不是节区时,意味着,把该文件的所有需要在“内存申请空间”的节区(个人理解,一般来说就是.text和.data)放在一起,接下来又放第二个文件的节区,这里需要注意的是,将各个文件的节区任意摆布,只要正确的设置了入口点,那么程序都会正确按照程序逻辑执行,这是因为无论节区如何摆布,连接器总会用根据指定的地址,计算出应该跳转到的地址,也即是说,只要按照程序段头的信息载入该程序,然后设置正确的入口点,那么程序便可以正确执行。

个人认为,若在pc上执行程序,并无必要自己来指定程序应摆布在何处,主要是由于在嵌入式开发时,需要在向ram中移动代码或进行mmu等操作时,才需要指定连接地址,以便容易直接操作。另外需要注意的是,对于ld连接器,没有提供lds文件链接文件时,ld有自己默认的lds,其中代码较为复杂,不作分析。

时间: 2024-11-10 02:20:14

关于elf结构及其编译连接的理解的相关文章

执行的前戏------编译连接全过程理解

一. 前言 高度封装的事物(如各种IDE)在提供便捷操作的同一时候也失去了很多美好的内部细节.往往让让使用者仅仅知道how to use 而不知道how to achieve,因而在出现一些封装内部的错误时就会让使用者手足无措.因此了解其内部的大致执行过程将有助于处理一些集成环境不提示的错误. 二.基本概念 编译:     编译器对源码进行编译.是将以文本形式存在的源码翻译为机器语言形式的目标文件的过程. 编译单元:对于C++来说,每个cpp文件就是一个编译单元.从之前的编译过程的演示能够看出,

运行的前戏------编译连接全过程理解

一. 前言 高度封装的事物(如各种IDE)在提供便捷操作的同时也失去了许多美好的内部细节,往往让让使用者只知道how to use 而不知道how to achieve,因而在出现一些封装内部的错误时就会让使用者手足无措,因此了解其内部的大致运行过程将有助于处理一些集成环境不提示的错误. 二.基本概念 编译:     编译器对源代码进行编译,是将以文本形式存在的源代码翻译为机器语言形式的目标文件的过程. 编译单元:对于C++来说,每一个cpp文件就是一个编译单元.从之前的编译过程的演示可以看出,

VC编译连接选项详解(转)

大家可能一直在用VC开发软件,但是对于这个编译器却未必很了解.原因是多方面的.大多数情况下,我们只停留在“使用”它,而不会想去“了解”它.因为它只是一个工具,我们宁可把更多的精力放在C++语言和软件设计上.我们习惯于这样一种“模式”:建立一个项目,然后写代码,然后编译,反反复复调试.但是,所谓:“公欲善其事,必先利其器”.如果我们精于VC开发环境,我们是不是能够做得更加游刃有余呢? VC的处理流程,大致分为两步:编译和连接.源文件通过编译生成了.obj文件:所有.obj文件和.lib文件通过连接

VC编译连接选项详解

VC编译连接选项详解 大家可能一直在用VC开发软件,但是对于这个编译器却未必很了解.原因是多方面的.大多数情况下,我们只停留在"使用"它,而不会想去"了解"它.因为它只是一个工具,我们宁可把更多的精力放在C++语言和软件设计上.我们习惯于这样一种"模式":建立一个项目,然后写代码,然后编译,反反复复调试.但是,所谓:"公欲善其事,必先利其器".如果我们精于VC开发环境,我们是不是能够做得更加游刃有余呢? VC的处理流程,大致分

编译器编译过程的理解

编译过程大体包括预处理,编译,汇编,链接四大步,其中预处理主要是宏定义替换,文件包含,条件编译.用#ifndef机构可以防止以下情况:即一个文件A包含了一个头文件C,同时另一个文件B也包含了头文件C,而且文件A包含了头文件B,若用#ifndef结构可以避免在编译的时候报错,但是若头文件里有定义(包括函数,数组,变量)那么在链接的时候会因为多个object包含同一对象而报错,此错误是链接时的错误非编译错误,所以必须注意的是,在头文件里面只能声明,不能定义.另外注意的是include后尖括号和双引号

windows环境中利用NMake工具编译连接C++源代码

这篇文章是上一篇文章(http://www.cnblogs.com/LCCRNblog/p/4532643.html)的补充,因此需要先看看上一篇文章. 最近在写代码的时候,需要通过命令的方式来执行生成的c++源代码文件,因此需要学习有关windows环境下如何使用命令来编译连接c++原文件.这一篇文章是自己慢慢摸索实践得出的.作为自己入门的起点吧,后续还要好好深入理解这方面的知识. 1.准备 编写好main.cpp header.h header.cpp这三个源代码文件,并放入一个文件夹tes

vc++ 编译连接错误and解决方法

1. LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol [email protected] Debug/lizi.exe : [分析]: 项目创建错误,因该选择Win32 Console Project,而不是 Win32 Projectvc++ 编译连接错误and解决方法,布布扣,bubuko.com

(DT系列一)DTS结构及其编译方法

DTS结构及其编译方法 一:主要问题 1,需要了解dtsi与dts的关系 2,dts的结构模型 3,dts是如何被编译的,以及编译后会生成一个什么文件. 二:参考文字 1,DTS(device tree source) .dts文件是一种ASCII文本格式的DeviceTree描述.基本上,在ARMLinux内,一个.dts文件对应一个ARM的machine,一般放置在内核的arch/arm/boot/dts/目录.由于一个SoC可能对应多个machine(一个SoC可以对应多个产品和电路板),

Linux程序可以编译连接但是执行时找不到*.so 一般都和LD_LIBRARY_PATH有关

http://bbs.chinaunix.net/thread-983182-1-1.html PATH变量: 此变量代表可执行程序在任意位置直接输入指令执行时,系统会到此变量标记的目录里去找对应的可执行程序. 有些命令的路径没有在PATH环境变量中,可以用echo $PATH命令查询得知,添加路径到PATH环境变量的方法如下: (1)如果只想在本次开机过程中临时性的添加修改,下次开机就无效的话,可以: export PATH=$PATH:path -----path为命令所在路径 (2)如果只