Linux 第七章学习笔记
1:链接概述
链接(linking)是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或被拷贝)到存储并执行。
编译系统提供的调用预处理器、编译器、汇编器和链接器来构造目标文件的。
2. 静态链接
为了构造可执行文件,链接器的两个主要任务
符号解析:目标文件定义和引用符号。符号解析的目的是将每个符号引用刚好和一个符号定义联系起来。
重定位:编译器和汇编器生成从地址0开始的代码和数据节。链接器通过把每个符号定义与一个存储器位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器位置,从而重定位这些节。
符号解析:
和引用定义在相同模块的本地符号;编译器只允许每个模块中每个本地符号有一个定义对于全局符号的解析。
当编译器遇到一个不是在当前模块中定义的符号时,它会假设该符号是在某个模块中定义的,生成一个链接器符号表条目然后交给链接器处理;
如果在链接器的任何模块中都找不到这个被引用的符号,它就输出一条错误信息然后终止
对于多重定义的全局符号;
函数和已经初始化的全局变量是强符号,未初始化的全局变量是弱符号 规则:
不允许有多个强符号
如果有一个强符号和多个弱符号,那么选择强符号
如果有多个弱符号,那么从其中任选一个
重定位
重定位节和符号定义:链接器将所有相同类型的节合并为同一类型的新的聚合节
重定位节中的符号引用:链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时的地址
重定位条目。
3. 目标文件
三种形式
可重定位目标文件:包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标件。
可执行目标文件:包含二进制代码和数据,其形式可以被直接拷贝到存储器并执行。
共享目标文件:一种特殊类型的可重定位目标文件,可以在加载或者运行地被动态地加载到存储器并链接。
4. 可重定位目标文件
一个典型的ELF可重定位目标文件的格式:
ELF文件已经是适应到某一种CPU体系结构的二进制兼容文件了。
默认的ELF头加载地址是0x8048000,头部大概要到0x48100处或者0x483000处,也就是可执行文件加载到内存之后
5. 符号和符号表
每个可重定位目标模块m都有一个符号表,它包含m所定义和引用的符号的信息。
链接器上下文中的三种不同符号
由m定义并能被其他模块引用的全局符号。
由其他模块定义并被模块m引用的全局符号。
只被模块m定义和引用的本地符号。
符号表
typedef struct{
int name; //字符串表中的字节偏移,指向符号的以NULL结尾的名字
int value; //符号的地址,对于可重定位的模块是距定义目标的节起始位置的偏移。
int size; //目标大小(单位:字节)
char type:4, //数据或函数
binding:4; //本地LOCAL/全局GLOBAL
char reserved;
char section; //到节头部表的索引
}Elf_Symbol;
每个符号都和目标的某个节相关联,由section字段表示。
- ABS:不该被重定位的符号。
- UNDEF:未定义的符号,在本目标模块中引用,但在其他地方定义。
- COMMON:未被分配位置的未初始化数据目标。
6. 符号解析
与静态库链接
所有的编译系统都提供一种机制,将所有相关的目标模块打包成为一个单独的文件,称为静态库(Linux下是存档文件,Windows下是lib),只拷贝静态库里被应用程序引用的目标模块。
链接时加上-static参数告诉编译器驱动程序,链接器应该构建一个完全链接的可执行目标文件,它可以加载到存储器并执行,在加载时无需更进一步的链接。
如何处理多重定义的符号?
不允许有多个强符号
如果有一个强符号和多个弱符号,那么选择强符号
如果有多个弱符号,那么从这些弱符号中任意选择一个
7. 重定位
(1)重定位两步
重定位节和符号定义:链接器将所有相同类型的节合并为同一类型的新的聚合节
重定位节中的符号引用:
链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时的地址重定位条目。
链接器依赖于称为重定位条目的可重定位目标模块中的数据结构。
(2)重定位条目
ELF定义了11种不同的重定位类型。两种最基本的重定位类型:
*R_386_PC32 重定位一个使用32位PC相对地址的引用。
*R_386_32 重定位一个使用32位绝对地址的引用。
(3)重定位符号引用
链接器修改代码节和数据节中对每个符号的引用,使得他们指向正确的运行时地址。
重定位PC相对引用
重定位绝对引用
8. 可执行目标文件及加载
(1)可执行目标文件
C程序开始时是一组ASCII文本文件,已经被转化为一个二进制文件,且这个二进制文件包含加载程序到存储器并运行它所需的所有信息。
段头部表:可执行文件的连续片被映射到连续的存储器段,段头部表描述了这种关系。
- 在32位Linux系统中,代码段总是从地址0x08048000处开始。
(2)加载可执行目标文件
加载器将可执行目标文件中的执行代码和数据从磁盘拷贝到存储器中,然后通过跳转到程序的第一条指令或入口点来运行该程序。这个将程序拷贝到存储器并运行的过程叫做加载。
要运行可执行目标文件p,可以在Unix外壳的命令行中输入它的名字:unix> ./p
将程序拷贝到存储器并运行的过程叫做加载。
用户栈总是最大的合法用户地址开始,向下增长的(向低存储器地址方向增长)。从栈的上部开始的段是为操作系统驻留存储器
的部分(内核)的代码和数据保留的。
9. 动态连接共享库
动态链接器通过执行下面的重定位完成链接任务:
- 重定位libc.so的文本和数据到某个存储器段
- 重定位libvector.so的文本和数据到另一个存储器段
- 重定位p2中所有对libc.so和libvector.so定义的符号的引用
- 最后动态链接器将控制传递给应用程序,此时共享库的位置已固定,并且在程序执行的过程中不会改变
(1)静态库的缺点
- 首先,静态库在更新时,使用该库的程序需要与更新的库进行重新链接。
- 其次,由于使用静态库的程序在链接时都会拷贝静态库里被应用程序引用的目标模块,像printf和scanf这样的函数的代码在运行时都会被复制到每个运行进程的文本段中,这造成了冗余,浪费了稀缺的存储器资源。
(2)共享库
共享库是一个目标模块,在运行时,可以加载到任意的存储器地址,并和一个在存储器中的程序链接起来。
这个过程称为动态链接,是由一个叫做动态链接器的程序来执行的。
共享库也称为共享目标,在Unix系统中通常用.so后缀来表示。微软的操作系统大量地利用了共享库,它们称为DLL(动态链接库)。
共享库是以两种不同的方式来“共享”的(在Windows中分别称为“隐式链接”和“显示链接”)。
1:在任何给定的文件系统中,对于一个库只有一个.so文件。所有引用该库的可执行目标文件共享这个.so文件中的代码和数据,
而不是像静态库的内容那样被拷贝和嵌入引用它们的可执行的文件中。
2:在存储器中,一个共享库的.text节 一个副本可以被不同的正在运行的进程共享。
10.从应用程序中加载和链接共享库
void *dlopen( const char *file, int mode );//将共享目标文件打开并且映射到内存中,并且返回句柄
void *dlsym( void *restrict handle, const char *restrict name );//回一个指向被请求入口点的指针
char *dlerror();//返回 NULL 或者一个指向描述最近错误的 ASCII 字符串的指针
char *dlclose( void *handle );//关闭句柄并且取消共享目标文件的映射
11. 处理目标文件的工具
AR:创建静态库,插入、删除、列出和提取成员。
STRINGS:列出一个目标文件中所有可打印的字符串。
STRIP:从目标文件中删除符号的信息。
NM:列出一个目标文件的符号表中定义的符号。
SIZE:目标文件中节的名字和大小。
READELF:显示一个目标文件的完整结构,包括ELF头中的编码的所有信息。包含SIZE和NM的功能。
OBJDUMP:所有二进制工具之母,能够显示一个目标文件中所有的信息。它最大的作用是反汇编.text节中的二进制指令。
LDD:列出一个可执行文件在运行时所需要的共享库。
Linux 第七章学习笔记的更多相关文章
- Linux 第五章 学习笔记
---恢复内容开始--- 第五章 系统调用 一.与内核通信 1.系统调用在用户控件进程和硬件设备之间添加了一个中间层. 为用户空间提供了一种硬件的抽象接口 系统调用保证了系统的稳定和安全 每个进程都运 ...
- linux第七章读书笔记
Vim编辑器 Vim 仅仅通过键盘来在插入和执行命令等多种模式之间切换.这使得Vim可以不用进行菜单或者鼠标操作,并且最小化组合键的操作,对文字录入员或者程序员可以大大增强速度和效率. CHAPTER ...
- linux第三章学习笔记
第三章 进程管理 进程是Unix操作系统抽象概念中最基本的一种. 进程管理是所有操作系统的心脏所在. 一.进程 1. 进程是处于执行期的程序.除了可执行程序代码,还包括打开的文件.挂起的信号.内核内部 ...
- 20135320赵瀚青LINUX第七周学习笔记
赵瀚青原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 概述 本周学习的内容主要是讨 ...
- Mudo C++网络库第七章学习笔记
muduo编程示例 muduo库是设计来开发内网的网络程序, 它没有做任何安全方面的加强措施, 如果在公网上可能会受到攻击; muduo库把主动关闭连接这件事分成两步来做: 如果主动关闭连接, 会先关 ...
- 《Metasploit魔鬼训练营》第七章学习笔记
P314 木马程序的制作 msfpayload和msfencoder已被msfvenom取代.使用msfvenom -h查看具体用法.以下网址也有相关教程: https://github.com/ra ...
- 《Linux内核设计与实现》第四章学习笔记
<Linux内核设计与实现>第四章学习笔记 ——进程调度 姓名:王玮怡 学号:20135116 一.多任务 1.多任务操作系统的含义 多任务操作系统就是能同时并发地交 ...
- 《Linux内核设计与实现》第四章学习笔记——进程调度
<Linux内核设计与实现>第四章学习笔记——进程调 ...
- 《Linux内核设计与实现》课本第五章学习笔记——20135203齐岳
<Linux内核设计与实现>课本第五章学习笔记 By20135203齐岳 与内核通信 用户空间进程和硬件设备之间通过系统调用来交互,其主要作用有三个. 为用户空间提供了硬件的抽象接口. 保 ...
随机推荐
- linux网关设置
1.linux中eth0为外网ip.外网网关.外网DNS设置,eth1为内网ip”172.22.0.0/16“不设置网关.DNS. 2.启动linux内核中的IP转发功能 执行vim命令编辑sysct ...
- scanf函数(初学者)
scanf函数称为格式输入函数,即按用户指定的格式从键盘上把数据输入到指定的变量之中. 1.scanf函数的一般形式:scanf函数是一个标准的库函数,它的函数原型在头文件“stdio.h”中,与pr ...
- PDO 对 mysql的基本操作
PDO扩展操作 <?php $dsn = 'mysql:dbname=yii2;host=localhost'; $user = 'root'; $password = '123456'; tr ...
- File类_常见的方法(获取目录中指定规则的内容)_listFiles
import java.io.File; public class ListDemo { public static void main(String[] args) { ListDemo(); } ...
- IDEA中使用vim删除复制代码的行号
从别的地方复制来的源代码有时候会把前面的行号也一起拷过来,如果只是十几行代码的话手工去除还是方便的,但是如果代码有很多行的话,一行一行地删就不明智了. 例如我要复制以下代码 1 public clas ...
- nginx的反向代理proxy_pass指令
1. 首先什么是代理服务器?客户机发送请求时,不会直接发送到目的主机,而是先被代理服务器收到,代理服务器收到客服机的请求后,再向目的机发出,目的机就会返回数据给客户机,在返回给客户机之前,会被代理服务 ...
- Android学习之基础知识十一 —运用手机多媒体
一.使用通知(Notification) 通知(Notification)是Android系统中比较有特色的一个功能,当某个应用程序希望向用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助 ...
- element vuex 语音播报
data () { return { showDetail: false, height: 1, // 1 不可用 0 正常 2运维中 result: [], tableData: [], // 应用 ...
- BZOJ4860 BJOI2017 树的难题 点分治、线段树合并
传送门 只会线段树……关于单调队列的解法可以去看“重建计划”一题. 看到路径长度$\in [L,R]$考虑点分治.可以知道,在当前分治中心向其他点的路径中,始边(也就是分治中心到对应子树的根的那一条边 ...
- Java获取指定包名下的所有类的全类名的解决方案
最近有个需求需要获取一个指定包下的所有类的全类名,因此特意写了个获取指定包下所有类的全类名的工具类.在此记录一下,方便后续查阅 一.思路 通过ClassLoader来查找指定包 ...