三、GCC编译:链接

代码准备

main.c

extern int shared;
extern void func(int *a, int *b);
int main(){
        int a = 100;
        func(&a, &shared);
        return 0;
}

func.c

int shared = 1;
int tmp = 0;
void func(int *a, int *b){
        tmp = *a;
        *a = *b;
        *b = tmp;
}

静态链接

编译

gcc -static -fno-stack-protector main.c func.c -save-temps --verbose -o func.ELF

请添加图片描述

链接方式

在func.ELF-main.o和func.ELF-func.o这两个目标文件链接为一个可执行文件时,最简单的方法是按序叠加,即拼在一起(左图),但这种方法如果链接的目标文件过多,那么输出的可执行文件会十分零散。同时段的装载地址和空间以页为单位对齐,不足一页的代码节或数据节也要占用一页,造成内存空间的浪费。

现在的链接器采用的是相似节合并的方法,首先对每个节的长度、属性和偏移进行分析,然后将两个文件中的相同节进行合并,然后将符合表合并,引用生成统一的全局符号表,最后读取输入文件的各类信息对符号进行解析、重定位等操作。相似节的合并发生在重定位时。
请添加图片描述

详细过程

为了构造可执行文件,链接器必须完成两个重要工作:

  • 符号解析(symbol resolution):将每个符号(函数、全局变量、静态变量)的引用与其定义进行关联
  • 重定位(relocation):将每个符号的定义与一个内存地址进行关联,然后修改这些符号的引用,使其指向这个内存地址

地址分配

下面比较可执行文件func.ELF和中间产物main.o的区别
请添加图片描述
请添加图片描述
其中,VMA(Virtual Memory Address)是虚拟地址,LMA(Load Memory Address)是加载地址,一般情况下两者是相同的。
可以看到,尚未进行链接的main.o中的VMA都是0,而链接后的func.ELF中,相似节被合并,且完成了虚拟地址的分配。

偏移计算

下面查看反汇编代码
请添加图片描述
main函数的地址从0开始,其中对func()函数的调用在偏移0x25处。此时,0xe8是call指令,后四个字节0x00000000为调用指令的偏移量,此时call所调用的地址是call指令结束的地址+偏移量,即0x25+0x00=0x25,指向func()。
请添加图片描述
可以看到,链接成功后的func.ELF中,MOV指令在0x40163a,偏移量为0x07,0x40163a+0x07=0x401641,指向func()的地址。

重定位表

可重定位文件中最重要的是要包含重定位表,用于告诉链接器如何修改节的内容。每一个重定位表对应一个需要被重定位的节,例如.rel.text用于保存.text的重定位。
请添加图片描述
如图所示,shared的类型R_X86_64_PC32用于相对寻址,(原书中shared的类型为R_X86_64_32为绝对寻址),func的类型R_X86_64_PLT32就是新版本gcc的标记方法,还是相对寻址。
另外,value中的0x04为r_addend域的值,是对偏移的调整。

静态链接库

此外,后缀名为.a的文件是静态链接库文件。一个静态链接库可以视为一组目标文件经过压缩打包后形成的集合。执行各种编译任务时,需要许多不同的目标文件,例如各种.o文件,为了方便管理,便可使用ar工具将他们打包为静态链接库文件。

动态链接

意义

静态链接中,同一链接文件被不同的可执行文件需要时,该文件大量出现,并且都会加载入内存,而实际上只需要一个文件存储和装载即可,因此静态链接带来的磁盘和内存空间浪费问题愈发严重。

如图所示,testLib.o同时被func1.ELF和func2.ELF需要,静态链接时testLib.o被重复装载,导致内存浪费;而动态链接时func1.ELF和func2.ELF不再包含单独的testLib.o,当运行func1.ELF时,系统将func1.o和依赖的testLib.o装载入内存,进行动态链接,这之后func2.ELF想要执行时,由于内存中已经有testLib.o,因此无需再次重复装载。
请添加图片描述

编译

gcc -shared -fpic -o func.so func.c
gcc -fno-stack-protector -o func.ELF2 main.c ./func.so
ldd func.ELF2
objdump -d -M intel --section=.text func.ELF2 | grep -A 11 "<main>"

请添加图片描述

位置无关代码

可以加载而无须重定位的代码称为位置无关代码(Position-Independent Code, PIC),它是共享库必须具有的属性,通过gcc传递-fpic参数可以生成PIC。通过PIC,一个共享库的代码可以被无限多个进程所共享,从而节约内存资源。

由于程序或共享库中的数据段和代码段的相对距离总是不变的美因茨,指令和变量的距离之间的距离是一个运行时常量,与绝对内存地址无关。于是就有了全局偏移量表(Global Offset Table, GOT),它位于数据段的开头,用于保存全局变量和库函数的引用,每个条目占8个字节,在加载时会进行重定位并填入符号的绝对地址。

实际上,为了引入RELRO(ReLocation Read-Only,为了保护某些段将他们设置只读)保护机制,GOT被拆分为.got节和.got.plt节两个部分,不需要延迟绑定的前者用于保存全局变量引用,加载到内存后被标记为只读;需要延迟绑定的后者则用于保存函数引用,具有读写权限。

objdump -h func.so | grep "Idx"
objdump -h func.so | grep ".got"
readelf -r func.so | grep tmp
objdump -d -M intel --section=.text func.so | grep -A 20 "<func>"

请添加图片描述

延迟绑定

由于动态链接是由动态链接器在程序加载时进行的,当需要重定位的符号(库函数)多了之后,势必会影响性能。延迟绑定的基本思想是当函数第一次调用时,动态链接器才进行符号查找、重定位等操作,如果未调用则不绑定,从而减小开销。

ELF文件通过过程链接表(Procedure Linkage Table,PLT)和GOT的配合来实现延迟绑定,每个被调用的库函数都有一组对应的PLT和GOT。

位于代码段.plt节的PLT是一个数组,每个条目占16个字节。其中PLT[0]用于跳转到动态链接器,PLT[1]用于调用系统启动函数__libc_start_main(),main()函数从这里调用,从PLT[2]开始就是被调用的各个函数条目。

PLT作用/内容
0跳转到动态链接器
1调用系统启动函数__libc_start_main()
2及以上被调用的各个函数条目的GOT地址

位于数据段的.got.plt节的GOT也是一个数组,每个条目占8个字节。其中GOT[0]和GOT[1]包含动态链接器在解析函数地址时所需要的两个地址,GOT[2]是动态链接器ld-linux.so的入口点,从GOT[3]开始就是被调用的各个函数条目,这些条目默认指向对应PLT条目的第二条指令,完成绑定后才会被修改为函数的实际地址。

GOT作用/内容
0.dynamic,保存了动态链接器所需要的符号基本信息
1relor
2动态链接器ld-linux.so的入口点
3及以上被调用的各个函数条目的存放地址

以func()为例,执行call后会进入func@plt,第一条jmp指令找到对应的GOT条目,此时该位置保存的还是第二条指令的地址,于是执行第二条指令push,将对应的0x1(func在.rel.plt中的下标)压栈,然后进入PLT[0]。

PLT[0]先将GOT[1]压栈,然后调用GOT[2],也就是动态链接器的_dl_runtime_resolve()函数,完成符号解析和重定位工作,并将func()的真实地址填入func@got.plt,也就是GOT[4],最后把控制权交给func()。

延迟绑定完成后,再次调用func(),就可以通过func@plt的第一条指令直接跳转到func@got.plt,将控制权交给func()。

运行时链接

程序在运行时加载和链接共享库。Linux为此提供了一个简单的接口dlopen。传统的动态链接会生成一个GOT表,记录着可能用到的所有符号,并且这些符号在链接时都是可以找到的。运行时链接则需要在运行时定位这些符号。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/314829.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Leetcode】2696. 删除子串后的字符串最小长度

文章目录 题目思路代码 题目 2696. 删除子串后的字符串最小长度 思路 计算通过删除字符串中的 “AB” 和 “CD” 子串后&#xff0c;可获得的最终字符串的最小长度。 主要思路是使用一个栈来模拟字符串的处理过程&#xff0c;每次遍历字符串时&#xff0c;如果当前字符和栈…

open3d相关操作总结

open3d其实有很多交互式命令&#xff0c;在运行程序打开了open3d渲染的窗口后&#xff0c;鼠标点击窗口&#xff0c;按H就会弹出&#xff0c;交互命令的帮助&#xff0c;如下图所示&#xff1a; 其中比较常用的有&#xff1a; Q &#xff1a;退出当前窗口 H&#xff1a;打印帮…

掌汇云 | 公司库开启入驻模式:FoodTalks靠食品雷达实现一本“万”利

不久前我们介绍了群硕的掌汇云产品功能&#xff0c;深入探讨了公司库的作用及其生态价值。 当FoodTalks遇上公司库&#xff0c;新一代食品类商业信息服务平台隆重登场——“食品雷达”&#xff0c;让食品人轻松找到公司、品牌、供应商、客户…… 为了更好地享用本文&#xff…

【计算机组成原理】程序的转换及机器级表示 常用计算机术语英文缩写汇总

编码 二进制编码的十进制数&#xff08;BCD&#xff09;&#xff1a;Binary Coded Decimal美国信息交换标准代码&#xff08;ASCII&#xff09;&#xff1a;American Standard Code for Information Interchange 数据的排列顺序 最低有效位&#xff08;LSB&#xff09;&…

深入剖析 Linux Cgroups 子系统:资源精细管理

本章主要演示以下 cgroups 下各个 subsystem 的作用。 根据难易程度&#xff0c;依次演示了 pids 、cpu 和 memory 3 个 subsystem 的使用。 注&#xff1a;本文所有操作在 Ubuntu20.04 下进行。 如果你对云原生技术充满好奇&#xff0c;想要深入了解更多相关的文章和资讯&…

Camunda Spin

Spin 常用于在脚本中解析json或者xml使用&#xff0c;S(variable) 表示构造成Spin对象&#xff0c;通过prop(“属性名”)获取属性值&#xff0c;通过stringValue()、numberValue()、boolValue() 等对类型转换。 repositoryService.createDeployment().name("消息事件流程&…

力扣120. 三角形最小路径和(Java 动态规划)

Problem: 120. 三角形最小路径和 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 Problem:64. 最小路径和 本题目可以看作是在上述题目的基础上改编而来&#xff0c;具体的思路&#xff1a; 1.记录一个int类型的大小的 n 乘 n n乘n n乘n的数组&#xff08;其中 n n n为…

SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测

SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测 目录 SCI一区级 | Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多变量多步时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现RIME-CNN-BiLSTM-Mutilhead-Attention多…

YOLOV8

YOLOv8 是 ultralytics &#xff08;超溶体&#xff09;公司在 2023 年 1月 10 号开源的 YOLOv5 的下一个重大更新版本&#xff0c;目前支持图像分类、物体检测和实例分割任务&#xff0c;在还没有开源时就收到了用户的广泛关注。 总结&#xff1a; 1. 是YOLOV5的继承者 2. …

使用CloudCompare对obj网格模型转换为pcd/ply点云模型

1.打开CloudCompare&#xff0c;点击文件夹图标&#xff0c;首先先把文件类型选择为.obj&#xff0c;然后再去找预处理的obj网格模型&#xff0c;点击打开。 2.测试打开的obj网格模型如下图&#xff1a; 3.选中obj文件&#xff0c;点击网格上样本点的图标&#xff0c;输入预生成…

VNC:虚拟网络计算技术及在VMware中开启VNC连接教程

什么是VNC&#xff1f; VNC (Virtual Network Computing) 是一种广泛应用的远程桌面共享系统&#xff0c;它允许用户通过网络实时查看并控制另一台计算机的桌面环境。无论操作系统如何&#xff0c;只要两端设备均安装了兼容的VNC客户端和服务端软件&#xff0c;即可实现跨平台…

RT-Thread入门笔记6-空闲线程及两个常用的钩子函数

空闲线程 空闲线程是一个比较特殊的系统线程&#xff0c;它具备最低的优先级。当系统中无其他就绪线程可运行时&#xff0c;调度器将调度到空闲线程。 空闲线程还负责一些系统资源回收以及将一些处于关闭态的线程从线程调度列表中移除的动作 空闲线程在形式上是一个无线循环结…

Vue3-43-组件- 组件状态保持 KeepAlive 的简单使用

作用说明 一个应用场景 &#xff1a; 当我们在进行路由跳转的时候&#xff0c;会用到 <router-view> 来作为 组件渲染的出口&#xff0c; 此时&#xff0c;组件的状态是不会被保持的。 比如 &#xff1a; 当前在【组件A】中有一个响应式状态 num 的值通过 自加的方式 从初…

JS-DOM树和DOM对象

作用和分类 作用&#xff1a;就是使用JS去操作html和浏览器 分类&#xff1a;DOM&#xff08;文档对象模型&#xff09;、BOM&#xff08;浏览器对象模型&#xff09; 什么是DOM DOM&#xff08;Document Object Model--文档对象模型&#xff09;是用来呈现以及与任意HTML或…

横版动作闯关游戏:幽灵之歌 GHOST SONG 中文版

在洛里安荒凉的卫星上&#xff0c;一件长期休眠的死亡服从沉睡中醒来。踏上发现自我、古老谜团和宇宙骇物的氛围2D冒险之旅。探索蜿蜒的洞穴&#xff0c;获得新的能力来揭开这个外星世界埋藏已久的秘密。 游戏特点 发现地下之物 探索这个广阔而美丽如画&#xff0c;充满密室和诡…

【算法笔记】状态压缩dp(noip)

在acwing学习算法的一点思考和总结 状态压缩dp可以用来解决两种问题&#xff1a;一种是棋盘式的&#xff0c;也就是表示一行有2^N种摆法&#xff0c;另一种是表示一类集合 状压——棋盘式 思路&#xff1a;可以类比一下蒙德里安的梦想的解题过程&#xff0c;每一行的状态都只会…

数据库悲观锁 select for update的详解

一 作用 1.1 结论 在mysql中&#xff0c;select ... for update 仅适用于InnoDB&#xff0c;且必须在事务块中才能生效。Innodb引擎默认是行锁。 Select .... from where .... for update 如果在where的查询条件字段使用了【主键|索引】&#xff0c;则此命令上行锁。否…

“Frontiers”系列多本期刊分区下跌,1本SCI被踢,2本SCI升为Top,还可投吗?

近期&#xff0c;2023年中科院分区正式发布&#xff0c;不少学者都很关心期刊变动情况。此次分区更新中&#xff0c;Frontiers出版社旗下的医学期刊表现让人大跌眼镜。 据汇总来看&#xff0c;32本大类医学SCI期刊中&#xff0c;Frontiers of Hormone Research直接从原来的医学…

C语言操作符详解与进制

目录 一&#xff1a;操作符的分类 二&#xff1a;二进制和进制转换 2.1 2进制转10进制 2.1.1 10进制转2进制数字 2.2 2进制转8进制和16进制 2.2.1 2进制转8进制 2.2.2 2进制转16进制 三&#xff1a; 原码、反码、补码 四&#xff1a;移位操作符 4.1左移操作符 4.2 右…

AOT-GAN-for-Inpainting项目解读|使用AOT-GAN进行图像修复

项目地址&#xff1a; https://github.com/researchmm/AOT-GAN-for-Inpainting 基于pytorch实现 论文地址&#xff1a; https://arxiv.org/abs/2104.01431 开源时间&#xff1a; 2021年 项目简介&#xff1a; AOT-GAN-for-Inpainting是一个开源的图像修复项目&#xff0c;其对 …