Android虚拟机章节——1.3.2 ELF的加载和动态链接过程

本文介绍了静态库和动态链接库的区别,重点解析了动态链接库的工作原理。在动态链接中,ELF头文件的e_entry指定了程序入口,而动态链接器在运行时加载动态链接库,解析外部函数地址。动态链接过程涉及动态链接器的加载、动态符号解析和重定位。在Android系统中,这个过程与Linux类似,但Linker有所不同。通过GOT和PLT机制,程序可以在运行时动态解析和绑定函数,实现了代码的共享和高效利用。
摘要由CSDN通过智能技术生成

静态库和动态链接库有什么区别呢?
Wiki上对静态库的释义如下所示:
“In computer science, a static library or statically-linked library is a set of routines, external functions and variables which are resolved in a caller at compile-time and copied into a target application by a compiler, linker, or binder, producing an object file and a stand-alone executable”
静态链接库的特点是会在程序的编译链接阶段就完成函数和变量的地址解析工作,并使之成为可执行程序中不可分割的一部分。这种处理手段在某种程度上也可以有效的实现代码的重复利用,使得编写程序不需要每次都从零开始,因而在程序开发的早期得到了广泛的应用——甚至是当时技术条件下的唯一选择。但是它的缺点也是比较明显的,即可执行程序的体积会随着静态链接库的增加而不断变大。另外,如果操作系统中有多个可执行程序都用到了同一个静态库A,按照静态链接的做法就需要把A分别打包进所有程序中——这显然是一种资源浪费。
与静态链接库相对应的,便是动态链接库的处理方式,我们再来看下它的定义:
“In computing, a dynamic linker is the part of an operating system that loads and links the shared libraries needed by an executable when it is executed (at “run time”), by copying the content of libraries from persistent storage to RAM, and filling jump tables and relocating pointers.”
动态链接库有如下几个核心特点:
 动态链接库不需要在编译时就被打包到可执行程序中,而是等到后者在运行阶段再实现动态的加载和重定位
 动态链接库在被加载到内存中之后,操作系统需要为它执行动态链接操作。值得一提的是,有一些参考资料会把这里的链接称为“动态链接”,而将前述编译阶段的链接叫做“静态链接”;而且静态链接中也会有Relocation(Link Time Relocation),只是和动态链接中的重定位(Load Time Relocation)所针对的对象和处理过程存在差异。换句话说,只要涉及到多个文件之间的链接,通常都需要重定位。只不过静态链接发生在编译阶段,而动态链接则发生在运行阶段。这些概念都很容易搞混淆,提醒大家注意区分。
实际的动态链接过程是比较复杂的,例如需要链接器的介入(链接器通常也是一个ELF文件,因而存在“蛋生鸡,鸡生蛋”的问题,解决类似问题的办法通常被称为“BootStrap”。这其中还有很多有趣的细节,有兴趣的读者可以自行查阅相关资料),需要重定位,需要有效的机制来管理所有的动态符号等等。

接下来我们仍以之前的求和程序为例,进一步讲解动态链接库的工作原理。

/*main.c*/
#include “stdio.h”

void main()
{
    add(1,2);
    printf(“This is an example”);
}

int add(int value1, int value2)
{
    return value1+value2; 
}

在这个程序中,add函数是定义在main.c中的,因而它的地址是已知的;而printf函数则由C库提供,属于“外部函数”,所以它在编译时并不会被打包到“求和”小程序中。等到“求和”程序在机器上真正运行起来后,操作系统会把它需要的动态链接库从磁盘(或其它存储介质)加载到内存中,然后解析出(如果是启用了延时解析的话,情况会有所不同)所有它引用的外部函数的真实地址,并保证可执行程序可以正确指向这些外部函数。
那么动态链接的这些操作是在什么时候执行的呢?由前一小节的分析大家应该知道,ELF头文件中有一项代表的是程序的入口地址,即e_entry。它在求和程序中对应的具体值是:
这里写图片描述
ELF可执行程序在运行过程中的入口地址一定是e_entry吗?这个问题可以从Linux Kernel如何启动ELF程序中找到答案。当我们需要运行一个ELF程序时,内核在经过一系列操作后会调用do_execve——这个函数最终又会利用load_elf_binary来完成ELF文件的加载和解析(Linux支持多种可执行文件格式,由一个linux_binfmt结构体表示&#

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值