ELF文件格式

本文详细介绍了ELF(Executable and Linking Format)文件格式,包括可执行文件、静态链接库和动态链接库。内容涉及到nm、objdump、readelf等工具的用途,以及链接器如何处理不同类型的文件。静态链接库包括Text、Data、Bss和Symtab等部分,而动态链接库在程序运行时加载,提供共享资源。最后,概述了可执行目标文件加载到内存的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

elf指executable and linking format,不仅包含可执行文件,也包含库文件,包括静态库和动态库。

准备的说,也就是三种(这不废话吗):

可执行文件

静态链接库

动态链接库

要观察ELF的具体信息,可以用以下几个工具

nm: Lists symbols from object files.
objdump: Displays detailed information from object files.
readelf: Displays information about ELF object files.


静态链接库


Text:二进制码,代表逻辑本身

Data:初始化过了的全局以及静态的C变量,

Bss:未初始化的全局C变量,这一部分只算是占位,却不占用任何空间。目标文件格式为了节省空间特意把初始化过的变量和未初始化的变量区别对待,这样做的方式就是:未初始化的变量不占任何空间。

Symtab:符号表。符号表含有本编译模块定义和引用的函数和全局变量的符号信息。

Rel data:重定位信息


Bss

在C语言中,没有显式初始化的静态变量会被初始化为0或者null指针。C对于0值得实现通常使用一个bit模板,因此,BSS段包括所有文件域内未初始化的对象(变量和常量),未初始化的静态局部变量(用static修饰符修饰的局部变量)。而静态局部常量必须在声明的时候被初始化。

符号表

符号和符号表

符号表包含被编译模块定义和引用的代码。在链接器看来,分三种:

1.      被本模块定义并且能被其他模块引用的全局符号。全局符号包含非静态C函数,非静态全局变量;

2.      被其他模块定义但却被本模块引用的全局符号。这些符号被称为externals并且关联于定义于其他模块的C函数和变量;

3.      本地符号只能被本模块定义和引用。一些本地变量指的是静态的C函数和静态的全局变量,这些变量在本模块是可见的,但对于外部模块不可见。目标文件中的段和

注意本地符号不同于本地程序变量,symtab中的符号表不包含局部变量。这些被在运行时放置在栈上并且不为链接器所知。

重定位

这一步链接器会把同样类型的区域融合成一个区域。比如所有input目标文件的。Data部分会融合成输出的可执行文件的data部分。链接器分配实时内存给新的整合区域,给每个输入模块的这个区域,给每个输入模块定义的符号。当这一步完成后,程序中每一个指令和全局变量都有了唯一一个运行时内存地址。

重定向模块中引用的符号。这一步中链接器修改代码中所有符号引用从而他们能指向运行时地址。为了执行者一步,链接器依赖于目标模块中的一种数据结构,叫重定向入口。

那么,什么事重定位入口呢?当一个汇编器产生一个目标模块时,它不知道这些代码和数据最终会被存在内存的什么地方。它也不知道这个目标模块所引用的外部符号(包括函数名和全局变量)的内存位置。所以当汇编器不知道一个符号的确切地址的时候,它会产生一个重定向入口来告诉链接器如何去修改这个入口并融合这个目标文件到可执行文件中。代码中的重定位入口放在.relo .text,初始化过的重定位入口在relo.data

一个典型的重定位入口是这样的:

Typedef struct{

       Intoffset;//引用需要重定位的偏移量

       Intsymbol:24,//引用需要指向的符号

              Type:8;//重定位类型

} Elf32_Rel;


动态链接库

动态链接库,也就是一个so库,它可能是在程序link的时候使用到,然后在load的时候一起load进来;还有一种可能是程序自己取dlopen一个库,再dlclose一个库。

动态链接库在程序运行的时候也需要,静态链接库只在链接的时候需要。

静态链接库只是一堆目标文件的压缩集,比如一个可执行文件exe链接的时候,找到对应的目标文件,抽取出来打包到exe中去。

而且动态链接库是可以共享的,但是为什么可以共享呢?

http://jzhihui.iteye.com/blog/1447570



可执行目标文件


可执行目标文件拥有了所有信息,可以load进代码来运行了。

ELF可执行文件可以很容易的加载入内存,它有持续的可执行的文件块,可以映射到连续的内存区域。这个映射被描述为segment header table。

一个可执行程序执行的过程:

Unix执行execve程序,execve触发loader,loader从磁盘上拷贝代码和数据到内存,然后跳转到入口点开始执行。这个拷贝程序到内存并且运行它的过程叫加载。每个Unix程序都有一个运行内存映像,linux下,代码段起始于0x08048000,数据段在随后的以4K为边界的区域,堆在读写区域的下一个4K边界区域并且通过调用malloc向上生长。0x40000000为共享库所保留,用户堆栈起始于0xBfffffff并向下生长。0xC00000000以上的区域属于内核。但64位机上,不是这样了,什么样有待研究。


一个可执行文件执行后的内存映像




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值