随笔分类 -  PT-Linker Loader & Library

chap-7 7.5 动态链接相关结构
摘要:7.5 动态链接相关结构7.5.1 “.interp”段动态链接器的位置既不是由系统配置指定,也不是由环境参数决定,而是由ELF可执行文件决定。在动态链接的ELF可执行文件中,有一个专门的段叫做".interp"段(interp是interpreter的缩写),我们可以用objdump -s program1来查看该段的内容,如下图所示:***图7.5.1***该段的内容很简单,里面保存的就是一个字符串,这个字符串就是可执行文件所需要的动态链接器的路径。在Linux下,可执行文件所需要的动态链接器路径几乎都是/lib/ld-linux.so.2。但是这个文件是一个软链接,在 阅读全文

posted @ 2014-01-15 23:22 Persistence 阅读(240) 评论(0) 推荐(0)

chap-7 7.4 延迟绑定
摘要:7.3.4 共享模块的全局变量问题当一个模块引用了一个定义在共享对象的全部变量的时候,比如一个共享对象定义了一个全部变量global,而模块module.c中是这么引用的:extern int global;int foo() { global = 1;}当编译器编译module.c时,它无法根据这个上下文判断global是定义在同一个模块的其他目标文件中,还是定义在另外一个共享对象中,即无法判断是否为跨模块间的调用。7.3.5 数据段地址无关性例如有如下代码段:static int a;static int* p = &a;如果共享对象里面有这样一段代码的话,那么指针p的地址就是一个 阅读全文

posted @ 2014-01-15 23:21 Persistence 阅读(374) 评论(0) 推荐(0)

chap-7 7.3 地址无关代码
摘要:7.3 地址无关代码7.3.3 地址无关代码装载时重定位是解决动态模块中有绝对地址引用的办法之一,但是它有一个很大的缺点就是指令部分无法在多个进程之间共享,这就失去了动态链接节省内存的一大优势,我们的目标就是程序模块中共享的指令部分在装载时不需要因为装载地址的改变而改变,因此基本想法就是把指令中需要被修改的地方分离出来,跟数据部分放在一起,这样指令部分就可以保持不变,而数据部分在每个进程中拥有一个副本,这种方案被称为地址无关代码(PIC, Position-Independent Code)。我们先来分析模块中各种类型的地址引用方式。这里我们把共享对象模块中的地址引用按照是否为跨模块分为两类: 阅读全文

posted @ 2014-01-14 00:40 Persistence 阅读(387) 评论(0) 推荐(0)

chap-7 7.2 动态链接的简单实例
摘要:7.2 动态链接的简单实例我们将使用一下几个文件来说明动态链接的过程://program1.c#include "lib.h"int main() { foobar(1); return 0;}//program2.c#include "lib.h"int main() { foobar(2); return 0;}//Lib.c#include #include "lib.h"void foobar(int i) { printf("Printing from Lib.so %d\n", i);}//Lib.h# 阅读全文

posted @ 2014-01-12 01:13 Persistence 阅读(273) 评论(0) 推荐(0)

chap-7 7.1 为什么需要动态链接?
摘要:7.1 为什么要动态链接为什么静态链接会被舍弃?首先,从空间方面而言,因为每个程序在装载进内存运行之前,要将程序和所需要的其他模块进行链接。比如,程序A用到了printf.o模块,同时程序B也用到了printf.o模块,如果磁盘中有成百上千个可执行文件用到了printf.o模块,那么printf.o将被链接到各个最终的可执行文件中。printf.o的不可共享导致了磁盘和内存空间的浪费。其次,静态链接对程序的更新、部署、发布也会带来很多麻烦。比如program1用到了模块Lib.o,并且该模块是第三方厂商提供的,当该厂上更新Lib.o模块后,program1需要和Lib.o重新进行链接形成可执行 阅读全文

posted @ 2014-01-12 00:51 Persistence 阅读(230) 评论(0) 推荐(0)

Chap-6 6.4.2 堆和栈
摘要:6.4.2 堆和栈在操作系统里面,VMA除了被用来映射可执行文件中的各个segment,操作系统还使用VMA来对进程的地址空间进行管理。进程在执行的时候还需要用到堆(heap)和栈(stack)等空间,一个进程中的栈和堆分别都有一个对应的VMA,在Linux下,可以通过/proc来查看进程的虚拟空间分布,下面查看一个死循环的虚拟空间分布:***图6.4.2***第一列是VMA的地址范围;第二列是VMA的权限,“p”表示私有(COPY ON WRITE,COW),“s”表示共享;第三列表示VMA对应的segment在文件中的偏移;第四列表示映像文件所在设备的主设备和次设备号;第五列表示映像文件的 阅读全文

posted @ 2014-01-10 23:20 Persistence 阅读(219) 评论(0) 推荐(0)

Chap-6 6.5 ELF可执行文件的装载
摘要:6.5 Linux装载ELF可执行文件的过程当我们在Linux系统的bash下输入一个命令执行某个ELF文件时,Linux是怎样装载这个ELF文件并且执行它呢?首先在用户层面,bash进程会调用fork()系统调用常见一个新进程,然后新的进程调用execve()系统调用执行指定的ELF文件,原先的bash进程继续返回等待刚才启动的新进程结束。在进入execve()系统调用后,Linux内核就开始进行真正的装载工作。在内核中,execve()系统调用相应的入口是sys_execve(),它被定义在arch/i386/kernel/process.c文件中。sys_execve()进行一些参数的检 阅读全文

posted @ 2014-01-10 23:18 Persistence 阅读(266) 评论(0) 推荐(0)

Chap-6 6.4 进程虚拟地址空间分布
摘要:6.4 进程虚拟空间分布6.4.1 ELF文件的链接视图和执行视图可执行文件被映射时,是以系统页的长度为单位的,每个段在被映射时的长度应该为系统页长度的整数倍。如果不是,那么多余部分也将占用一个页。一个ELF可执行文件往往有十几个段,那么内存空间的浪费是可想而知的,有没有办法来减少这种浪费?一个简单的方案就是:对于有相同权限的段,把它们合到一起当作一个段进行映射。比如有两个段分别叫做“.text”和“.init”,它们包含的分别是程序的可执行代码和初始化代码,权限相同,都是可读并且可执行。假设.text段为4097字节,.init为513字节,这两个段分别映射的话就要占用三个页面,但是,如果将 阅读全文

posted @ 2014-01-10 23:17 Persistence 阅读(461) 评论(0) 推荐(0)

Chap-6 6.1~6.3 程序装载
摘要:6.1 进程的虚拟地址空间程序和进程的区别:程序(或者狭义上讲可执行文件)是一个静态的概念,它就是一些预先编译好的指令和数据集合的一个文件;进程则是一个动态的概念,它是程序运行时的一个过程,很多时候把动态库叫做运行时(Runtime)也有一定的含义。程序运行起来以后,将拥有自己独立的虚拟地址空间,这个虚拟地址空间的大小由计算机的硬件平台决定,具体的说是由CPU的位数决定。硬件决定了地址空间的最大理论上限,即硬件的寻址空间大小,比如32位的硬件平台决定了虚拟地址空间的地址为0到2^32 - 1,即0x00000000 ~ 0xFFFFFFFF,就是4GB的虚拟空间大小。在32位平台下的4GB虚拟 阅读全文

posted @ 2014-01-10 23:15 Persistence 阅读(177) 评论(0) 推荐(0)

Chap-4 Section 4.6 链接控制过程
摘要:4.6 链接过程控制前面我们在使用ld链接器的时候,没有指定链接控制脚本,其实ld在用户没有指定链接脚本的时候会使用默认链接脚本,可以使用ld -verbose来查看ld默认的链接脚本。当然,为了更加精确的控制链接过程,可以自己写一个链接控制脚本,然后指定该脚本为链接控制脚本,可以使用-T参数:ld -T link.script4.6.2 最小的程序为了演示链接的控制过程,我们要写一个程序,该程序的功能是在终端输出"hello world"。但是我们这里的程序与C语言教科书中的例子有所不同。首先,经典的hello world使用了printf函数,该函数是C语言库的一部分, 阅读全文

posted @ 2014-01-07 00:19 Persistence 阅读(401) 评论(0) 推荐(0)

Chap-4 Section 4.5 静态库链接
摘要:4.5 静态库链接一个静态库可以看成是是一组目标文件的集合,即很多目标文件经过压缩打包后形成的一个文件。比如我们在Linux中最长使用的C语言静态库libc位于/usr/lib/i386-linux-gnu目录下,它属于glibc项目的一部分。glibc本身是用C语言开发的,它由成千上百个C语言源代码文件组成,编译完成后有相同数量的目标文件,比如输入输出printf.o,scanf.o;文件操作有fread.o,fwrite.o;时间日期有date.o,time.o;内存管理有malloc.o,把这些零散的目标文件直接提供给库的使用者,很大程度上会造成文件传输、管理和组织方面的不便,于是使用a 阅读全文

posted @ 2014-01-07 00:16 Persistence 阅读(270) 评论(0) 推荐(0)

Chap-4 Section 4.4 C++相关问题
摘要:4.4 C++相关问题C++的一些语言特性使之必须由编译器和链接器共同支持才能完成工作,最主要的又两个方面,一个是C++的重复代码消除,另外一个是全局的构造和析构。另外由于C++语言的各种特性,比如虚函数、函数重载、继承、异常机制等,使得背后的数据结构异常复杂,这些数据结构往往在不同的编译器和链接器之间相互不能通用,使得C++程序的二进制兼容性成了一个大问题。4.4.1 重复代码消除4.4.2 全局构造和析构我们知道一般的C++/C程序是从main函数开始执行的,随着main函数的结束而结束。然而,其实在main函数被调用之前,为了程序能够顺利执行,先要初始化进程的执行环境,比如堆分配初始化、 阅读全文

posted @ 2014-01-07 00:14 Persistence 阅读(227) 评论(0) 推荐(0)

Chap-4 Section 4.3 COMMON块
摘要:4.3 COMMON块如果一个弱符号定义在多个目标文件中,而它们的类型又不同,怎么办?目前的链接器类型并不支持符号的类型,即变量类型对于链接器而言是透明的,它只知道一个名字,并不知道类型是否一致,那么当我们定义的多个符号类型不一致时,链接器该怎么处理呢?主要分三种情况:1.两个或者两个以上的强符号类型不一致2.有一个强符号,其他都是弱符号,出现类型不一致3.两个或者两个以上弱符号类型不一致对于第一种情况而言,定义多个强符号本身就是违法的,链接器会报符号多重定义错误,链接器处理的就是后两种情况。事实上,现在的编译器和链接器都支持一种叫COMMON块的机制,该机制最早来源于Fortan,早期的Fo 阅读全文

posted @ 2014-01-07 00:12 Persistence 阅读(418) 评论(0) 推荐(0)

Chap-4 Section 4.2.4 指令修正方式
摘要:对于X86平台下的ELF文件的重定位入口所修正的指令寻址方式只有两种:绝对近址32寻址和相对近址32寻址。这两种指令修正方式每个被修正的位置的长度都为32位,即4个字节,而且都是近址寻址,不用考虑Intel的段间远址寻址。r_info成员的低8位表示重定位入口的类型。X86基本重定位类型宏定义 值 重定位修正方法R_386_32 1 绝对寻址修正 S + AR_386_PC32 2 相对寻址修正 S + A - PA = 保存在指令中被修正位置的值P = 被修正的位置(相对于段开始的偏移量或者虚拟地址),该值可以通过r_offset... 阅读全文

posted @ 2014-01-05 01:01 Persistence 阅读(859) 评论(0) 推荐(0)

Chap-4 Section 4.2.3 符号解析
摘要:4.2.3 符号解析在平时的编程过程中,之所以要进行链接是因为我们目标文件中用到的符号被定义在其他文件中,所以要将它们链接起来。例如,直接用ld来链接a.o,而不将b.o做为输入,链接器就会发现shared和swap这两个符号没有被定义,会出现下面如图4.2.6的错误:***图4.2.6***上图所示的错误也是我们平时编程过程中遇到的常见问题之一,就是链接符号未定义。导致这个问题的原因很多,最常见的一般都是链接时缺少某个库,或者输入目标文件路径不正确,或者符号的定义和生命不一样。通过上面的介绍,可以深层次的理解为什么缺少符号的定义会导致链接错误,其实重定位的过程也伴随着符号解析的过程,每个目标 阅读全文

posted @ 2014-01-05 00:56 Persistence 阅读(227) 评论(0) 推荐(0)

Chap-4 Section 4.2.2 重定位表
摘要:4.2.2 重定位表链接器怎么知道那些指令是要被调整?而这些指令的中那些部分需要被调整?记得在ELF文件中有一个重定位表,专门保存这些与重定位相关的信息,重定位表往往是一个或者多个重定位段。对于每个要被重定位的段在ELF中都会有一个对应的重定位段。比如代码段“.text”有要被重定位的地方,那么会有一个相对应的“.rel.text”段保存代码段中的重定位表。可以用objdump -r a.o来查看ELF文件的重定位表。如图4.2.4所示:***图4.2.4***上图显示了目标文件a.o中所引用到的外部符号的地址。每个要被重定位的地方叫重定位入口(Relocation Entry),可以看到a. 阅读全文

posted @ 2014-01-05 00:54 Persistence 阅读(478) 评论(0) 推荐(0)

Chap-4 Section 4.2 符号解析和重定位
摘要:在完成空间和地址的分配步骤后,链接器进入了符号解析与重定位的步骤,这也是静态链接的核心内容。首先看一下目标文件a.o中怎么使用两个外部符号shared和swap的。用objdump -d a.o查看反汇编后的结果,如图4.2.1:***图4.2.1***可以很清楚的看到a.o的反汇编结果中,a.o共定义了一个函数main,这个函数占用0x2b个字节,共12条指令,最左边那列是每条指令的偏移量,每行代表一条指令,有的指令很长,例如偏移量为0x09和0x11的两条指令,对于变量shared引用的是偏移为0x11的movl指令,这条指令共计8个字节,作用是将shared的地址赋值给esp寄存器+4的 阅读全文

posted @ 2014-01-02 23:47 Persistence 阅读(550) 评论(0) 推荐(0)

Chap-4 Section 4.1 静态链接介绍
摘要:当我们又两个目标文件时,如何将他们链接起来形成一个可执行文件?这个过程发生了什么?这基本上就是链接的核心:静态链接。在本章中,将会用一下两个源文件a.c和b.c做为例子说明://a.cextern int shared;int main() { int a = 100; swap(&a, &shared); return 0;}//b.cint shared = 1;void swap(int* a, int* b) { *a ^= *b ^= *a ^= *b;}4.1 空间与地址分配对于多个输入目标文件,链接器如何将它们的各个段合并到输出文件?或者说,输出文件中的空间如何分 阅读全文

posted @ 2014-01-02 23:44 Persistence 阅读(254) 评论(0) 推荐(0)

Chap-3 Section 3.6 符号修饰和函数签名
摘要:C++符号修饰C++拥有类、继承、虚机制、重载、名称空间这些特性,使得符号管理更加复杂。例如:针对如下两个函数fun(int)和fun(double),函数名字相同,但是参数不同,这是C++里面函数重载的最简单的一种情况,那么编译器和链接器在链接过程中是怎样区分这两个函数呢?为此,C++引入了符号修饰(Name Decoration)和符号改编(Name Mangling)。比如有如下代码://nameDe.cint func(int);float func(float);class C { int func(int); class C2 { int func(int); };};names. 阅读全文

posted @ 2014-01-02 23:41 Persistence 阅读(228) 评论(0) 推荐(0)

Chap-3 Section 3.5 弱符号与强符号
摘要:链接器解析符号的方法是将每个引用与它输入的可重定位目标文件的符号表中的一个确定的符号定义联系起来。对那些和引用定义在相同模块中的本地符号引用,符号解析很直接。编译器只允许每个模块中每个本地符号只有一个定义。不过对于全局符号的引用就稍微麻烦一些,当编译器遇到一个不是在当前模块中定义的符号(变量或者函数名)时,它会假设该符号是在其他某个模块中定义的,生成一个链接器符号表条目,并把它交给链接器处理。如果链接器在它的任何输入模块 中找不到这个被引用的符号,它就输出一条错误信息并且终止。 在编译时,编译器向汇编器输出每个全局符号,或者是强(strong)或者是弱(weak),而汇编器把这个信息隐含地编码 阅读全文

posted @ 2014-01-02 23:39 Persistence 阅读(276) 评论(0) 推荐(0)

导航