三、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.mzph.cn/news/618969.shtml

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

相关文章

TypeScript学习笔记、鸿蒙开发学习笔记

变量定义方式 # 变量声明 let msg: string douzi console.log(msg) let num: number 20 console.log(num) let end: boolean true console.log("end" end) let a: any 10 a douzi console.log(a) let p {name:"douzi",age:20} console.log(p.name)…

cmake-将源文件编译为可执行文件

文章目录 准备工作创建cmake文件编译源代码需要的文件不在同一个文件夹的情况 准备工作 首先我们准备三个文件 add.h int add(int a,int b);add.cpp int add(int a,int b) {return ab; }main.cpp #include "add.h" #include <iostream> int main() {in…

Multi-Concept Customization of Text-to-Image Diffusion——【代码复现】

本文是发表于CVPR 2023上的一篇论文&#xff1a;[2212.04488] Multi-Concept Customization of Text-to-Image Diffusion (arxiv.org) 一、引言 本文主要做的工作是对stable-diffusion的预训练模型进行微调&#xff0c;需要的显存相对较多&#xff0c;论文中测试时是在两块GP…

ssh远程登录协议

目录 一、ssh协议定义及特点 &#xff08;一&#xff09;ssh协议简介 &#xff08;二&#xff09;ssh协议的主要特点 二、ssh传输原理 三、sshd服务 &#xff08;一&#xff09;配置文件 &#xff08;二&#xff09;基本用法 1.直接连接 2.连接指定用户 3.指定端口号…

设计模式之策略模式【行为型模式】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档> 学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某…

十分钟教你在 k8s 中部署一个前后端应用

好多开发人员&#xff0c;尤其是没接触过 k8s 的人员对如何在k8s中部署一个 前后端应用很模糊&#xff0c;不知如何下手&#xff0c;所以本篇讲一下如何快速在 k8s 部署一个前后端应用&#xff0c;让大家对k8s有个快速认识 前置依赖 k8s集群&#xff0c;如果没有安装&#xff0…

什么是RoPE-旋转位置编码?

RoPE位置编码是大模型中最常见的位置编码之一。像是谷歌的PaLM和meta的LLaMA等开源大模型都是RoPE位置编码&#xff0c;那么RoPE有什么特点呢&#xff1f; 本文将介绍如下内容&#xff1a; RoPE旋转位置编码概要什么是位置编码&#xff1f;RoPE及其特点总结 一、RoPE旋转位置…

stm32学习笔记:DMA

每个DMA通道硬件触发源是不一样的&#xff0c;要使用某个外设的硬件触发源&#xff0c;就必须使用它连接的那个通道 12个独立可配置的通道&#xff1a;DMA1(7个通道)&#xff0c;DMA2(5个通道) 每个通道都支持软件触发和特定的硬件触发 C8T6 DMA资源&#xff1a;DMA1 &#xff…

Python自动化我选DrissionPage,弃用Selenium

DrissionPage 是一个基于 python 的网页自动化工具。 它既能控制浏览器&#xff0c;也能收发数据包&#xff0c;还能把两者合而为一。 可兼顾浏览器自动化的便利性和 requests 的高效率。 它功能强大&#xff0c;内置无数人性化设计和便捷功能。 它的语法简洁而优雅&#x…

数据库和表的操作

文章目录 前言一、库的操作创建数据库字符集和校验规则操纵数据库查看数据库显示创建语句修改数据库删除数据库备份和恢复数据库还原查看连接情况 二、表的操作创建表查看表结构修改表修改表名添加一列修改某一列属性删除某一列 删除表 前言 一、库的操作 创建数据库 语法&am…

【Python机器学习】分类器的不确定估计——决策函数

scikit-learn接口的分类器能够给出预测的不确定度估计&#xff0c;一般来说&#xff0c;分类器会预测一个测试点属于哪个类别&#xff0c;还包括它对这个预测的置信程度。 scikit-learn中有两个函数可以用于获取分类器的不确定度估计&#xff1a;decidion_function和predict_pr…

day06

1.八大基本数据类型整型: byte short int long 浮点: double float字符: char布尔: boolean2.声明变量的语法格式数据类型 变量名字 初始化的值; 3.使用for循环打印等腰三角形for (int i 1; i < 4; i) {for (int j 1; j < 4 - i; j) {System.out.print(" &qu…

计算机毕业设计----SSH电子相册管理系统

项目介绍 本项目分为普通用户与管理员两个角色&#xff1b; 管理员角色包含以下功能&#xff1a; 管理员登陆,用户信息管理,登陆密码修改等功能。 用户角色包含以下功能&#xff1a; 用户登陆,个人信息管理,相册管理,上传照片等功能。 环境需要 1.运行环境&#xff1a;最好…

高效办公:在文件夹名称左边插入关键字,提高文件管理效率

在繁忙的工作环境中&#xff0c;经常要处理大量的文件和文件夹。有效的文件管理是一个挑战&#xff0c;大量的文件和文件夹难以找到所需的资料。下面一起来看云炫文件管理器如何在文件夹名称左边批量插入关键字。 文件夹名称左边添加关键字前后对比图。 文件夹名称左边批量插…

P1379 八数码难题

题目描述 在 33 的棋盘上&#xff0c;摆有八个棋子&#xff0c;每个棋子上标有 1 至 8 的某一数字。棋盘中留有一个空格&#xff0c;空格用 0 来表示。空格周围的棋子可以移到空格中。要求解的问题是&#xff1a;给出一种初始布局&#xff08;初始状态&#xff09;和目标布局&…

哪里能找到好用的PPT模板?12个免费模板网站让你畅快办公!

你是否有过这样的经历&#xff0c;在准备重要会议或者演讲的时候&#xff0c;为找不到合适的PPT模板而困扰&#xff1f;或是在网上漫无目的地搜寻&#xff0c;结果收获的是设计平淡无奇的PPT模板&#xff1f; 如果你有同样的疑问&#xff0c;那么你来对地方了&#xff01;在这…

电脑扩容升级硬盘选1T还是2T

SSD固态有必要升级2TB吗&#xff1f;----------吴中函 某大二学生用的一台笔记本电脑&#xff0c;512GB的硬盘空间已经严重不够用了&#xff0c;想给笔记本扩容升级一下硬盘&#xff1b; 这位学生是学设计专业的、平时也喜欢摄影、电脑里面也装了一些游戏&#xff0c;经常整理、…

Linux技术,winSCP连接服务器超时故障解决方案

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 故障现象 使用 sftp 协议连接主机时, 明显感觉缓慢且卡顿,并且时常出现如下报错: 点击重新连接后,又有概率重新连接上; 总之在"连接上"和&…

【Web】NSSCTF Round#16 Basic个人wp(全)

出题友好&#xff0c;适合手生复健。 目录 ①RCE但是没有完全RCE ②了解过PHP特性吗 ①RCE但是没有完全RCE 上来就是一段md5八股 (string)就是不让用数组了&#xff0c;然后强比较需要md5碰撞 ?md5_1%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc…

josef约瑟 中间继电器 HJDZ-E440额定电压:AC220V 卡轨安装

HJDZ-静态中间继电器 系列型号&#xff1a; HJDZ-A200静态中间继电器&#xff1b;HJDZ-A110静态中间继电器&#xff1b; HJDZ-A002静态中间继电器&#xff1b;HJDZ-A004静态中间继电器&#xff1b; HJDZ-E112静态中间继电器&#xff1b;HJDZ-E112L静态中间继电器&#xff1…