三、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表,记录着可能用到的所有符号,并且这些符号在链接时都是可以找到的。运行时链接则需要在运行时定位这些符号。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot懒加载

什么是懒加载 懒加载是一种优化技术&#xff0c;它延迟加载或按需加载资源&#xff0c;以减少初始加载时间或提高性能。在不同的上下文中&#xff0c;懒加载有不同的应用和含义。在web开发中&#xff0c;懒加载通常指的是延迟加载图片数据。在长网页或应用中&#xff0c;如果图…

ruoyi后台管理系统部署-4-安装nginx

yum 安装 ngix 1.24 yum 官方源安装&#xff1a; # 1. 需要预先安装 yum-utils sudo yum install yum-utils # 2. 配置yum repo touch /etc/yum.repos.d/nginx.repongix.repo: [nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/$releasever/$…

Mac安装nvm以及使用nvm安装node

1. 安装nvm命令 git clone https://gitee.com/mirrors/nvm.git ~/.nvm && cd ~/.nvm && git checkout git describe --abbrev0 --tags2. 配置环境变量 vi ~/.bash_profileexport NVM_DIR"$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] &&…

【书生·浦语】大模型实战营——第五课作业

教程文档&#xff1a;https://github.com/InternLM/tutorial/blob/vansin-patch-4/lmdeploy/lmdeploy.md#tritonserver-%E6%9C%8D%E5%8A%A1%E4%BD%9C%E4%B8%BA%E5%90%8E%E7%AB%AF 视频链接&#xff1a; 作业&#xff1a; 基础作业 使用如下命令创建conda环境 conda create…

SpringBoot+thymeleaf实战遇到的问题

目录 一、控制台&#xff1a; 二、数据库查询异常&#xff1a; 三、前后端错误校验 四、在serviceImp中需要添加一个eq条件&#xff0c;表示和数据库中的哪个字段进行比较&#xff0c;否则会查出所有数据&#xff0c;导致500 五、使用流转换数据更简洁 六、重复报错&…

AI软件开发:探索原理、挑战与未来趋势

AI软件开发已经成为当前最热门和具有前景的技术领域之一。随着人工智能技术的快速发展&#xff0c;AI软件的应用范围也在不断扩大。本文将主要探讨AI软件开发的原理、挑战以及未来的趋势。 首先&#xff0c;AI软件开发的原理是基于机器学习和深度学习算法。机器学习是一种通过…

ssm基于web的电影购票系统+vue论文

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统电影购票信息管理难度大&#xff0c;容错率低&#xff0c…

C#无标题栏窗体拖动代码

文章目录 一、概念二、案例三、常见问题四、链接 一、概念 C#&#xff08;C Sharp&#xff09;是由微软公司开发的一种面向对象的编程语言。它是从C和C语言演化而来的&#xff0c;并结合了Java和其他编程语言的特性。C#是微软.NET平台的一部分&#xff0c;允许开发人员创建各种…

GAMES101:作业7记录

1. 总览 在之前的练习中,我们实现了 Whitted-Style Ray Tracing 算法,并且用 BVH等加速结构对于求交过程进行了加速。在本次实验中,我们将在上一次实验的基础上实现完整的 Path Tracing 算法。至此,我们已经来到了光线追踪版块的最后一节内容。 请认真阅读本文档,按照本文档指…

使用swift创建第一个ios程序

一、安装xcode 先到app store中下载一个Xcode app 二、创建项目 1、项目设定 创建ios app 2、工程结构 三、修改代码实现按键联动 四、运行测试

AI大模型引领未来智慧科研暨ChatGPT在地学、GIS、气象、农业、生态、环境等领域中的高级应用

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

YOLOv8 Ultralytics:使用Ultralytics框架进行FastSAM图像分割

YOLOv8 Ultralytics&#xff1a;使用Ultralytics框架进行FastSAM图像分割 前言相关介绍前提条件实验环境安装环境项目地址LinuxWindows 使用Ultralytics框架进行FastSAM图像分割参考文献 前言 由于本人水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。更多精彩内容…

SpringCloud:Ribbon

文章目录 Ribbon快速入门Ribbon负载均衡算法常见的负载均衡算法更改算法规则修改配置 饥饿加载 Ribbon ribbon是一个客户端负载均衡器&#xff0c;会从注册中心拉取可用服务&#xff0c;当客户端需要获取服务请求时&#xff0c;ribbon能够解析服务地址并实现负载均衡 快速入门 …

伴鱼实时数仓建设案例

伴鱼实时数仓建设案例 文章目录 伴鱼实时数仓建设案例伴鱼实时作业应用场景伴鱼实时数仓的建设体系DWD 层复杂场景数据处理方案1. 数据的去重2. join场景两条实时数据流相关联对于关联历史数据 3. 从数据形态观查join DWS 数据层数据处理方案未来与展望 随着伴鱼业务的快速发展…

mysql进阶 - 存储过程

目录 1. 用途&#xff1a; 2. 相关语法 2.1 创建 2.1.1 语法 2.1.2 示例 2.2 查看存储过程 2.3 调用 2.4 修改存储过程 2.5 删除存储过程 1. 用途&#xff1a; 存储过程广泛存在于一些遗留系统&#xff0c;可以减少代码的编写。而近些年&#xff0c;存储过程很少再用…

【Maven】001-Maven 概述

【Maven】001-Maven 概述 文章目录 【Maven】001-Maven 概述一、Maven 概述1、为什么学习 MavenMaven 作为依赖管理工具Maven 作为构建工具其它 2、Maven 介绍3、Maven 软件工作模型图 一、Maven 概述 1、为什么学习 Maven Maven 作为依赖管理工具 依赖管理&#xff1a; Mave…

【Docker】Docker安装入门教程及基本使用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Docker实战》。&#x1f3af;&#x1f3af; &…

WEB前端人机导论实验-实训3超链接与多媒体文件应用

1.项目1 设计简易灯箱画廊 A.题目要求&#xff1a; 编程实现简易灯箱画廊&#xff0c;鼠标单击任一个图像超链接&#xff0c;在底部浮动框架中显示大图像&#xff0c;效果如下的页面。 B.思路: &#xff08;1&#xff09;CSS样式&#xff1a; a.在样式中对body元素进行居中…

【复现】大华 DSS 数字监控系统 SQL 注入漏洞_18

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 大华DSS是大华的大型监控管理应用平台&#xff0c;支持几乎所有涉及监控等方面的操作&#xff0c;支持多级跨平台联网等操作。 可…

GPT-4与DALL·E 3:跨界融合,开启绘画与文本的新纪元

在人工智能的发展浪潮中&#xff0c;MidTool&#xff08;https://www.aimidtool.com/&#xff09;的GPT-4与DALLE 3的集成代表了一个跨越式的进步。这一集成不仅仅是技术的结合&#xff0c;更是艺术与文字的完美融合&#xff0c;它为创意产业带来了革命性的变革。本文将探讨GPT…