Linux相关概念和易错知识点(22)(ELF格式、可执行程序的加载、共享库)

使用 readelf 分析 so 文件:ELF 结构解析全攻略 readelf 是 GNU Binutils 提供的一个命令行工具,专门用于读取 ELF(Executable and Linkable Format)文件的结构信息。 阅读详情

目录

1.ELF格式

(1)ELF格式是什么

(2)ELF文件的划分

(3)静态链接的本质

①静态链接

②动态链接

2.可执行程序的加载

(1)指令编址

(2)逻辑地址存在的意义

(3)CPU调用指令

(4)页表的初始化

3.共享库

(1)struct vm_area_struct

(2).GOT


1.ELF格式

格式是进一步了解链接过程的刚需,所以我们要先认识ELF格式,再理解动静态库,这会清晰很多

(1)ELF格式是什么

我们写的文件都有自己的存储结构,代码和数据要怎么分配空间?哪一部分空间存哪些数据?我们的可执行程序同样是在某种格式下进行数据存储的,这个格式称为ELF格式。不仅仅是可执行程序,.o、.so也是ELF格式。

(2)ELF文件的划分

下面是section各个部分的主要功能的示意图。

我们因此知道,在可执行程序中就已经包含了进程地址空间各个段的大小划分,且这些地址都是绝对的。除此之外,如磁盘上的文件的物理地址,各个section的位置都是以偏移量的形式来保存的。我们能够推导出,进程地址空间的初始化和可执行程序的生成有着直接关系。

(3)静态链接的本质

①静态链接

.o和.so都是ELF格式的文件,静态链接就是将相关.o或.so文件中相同的属性的section进行合并(堆栈段、代码段合并),一个个小的ELF格式文件合并成一个大的ELF格式文件,形成可执行文件。我们因此能够解释为什么同一个程序,在不同文件中不能定义同名变量和函数;也能够解释为什么静态链接形成的可执行文件能够独立运行而不借助任何库。

静态库.a文件是ar格式(归档文件),不是ELF格式,但我们可以将.a看作.o文件的集合。当链接时,编译器会根据使用的函数去.a里面找被使用过的.o,这些.o的section都会被合并入可执行程序中,没有使用过的.o就不会合并。.o是最小合并单位,就算使用了一个函数,整个.o都会被合并,因为ELF文件是分节存储数据的,我们没有办法进一步分割。

②动态链接

对于动态链接的可执行程序来说,当.o和.so链接时,.o并没有直接将.so里面的section合并进来,函数方法的完整代码还在.so中保存着的,只有程序运行起来之后我们才能够获取函数的方法。

2.可执行程序的加载

接下来就是在ELF文件的基础上逐渐加深对可执行程序的认识了。

(1)指令编址

当可执行程序生成时,程序的指令存到了代码段对应的section中,这部分数据长什么样呢?

我们可以直接使用objdump -S查看可执行程序、.o、.so、.a的反汇编,程序编译完成后里面的每一行指令都有自己的地址

上面显示的是每一个数据节section的每一行指令的编址,这个地址叫逻辑地址。编址方式采用平坦模式,即逻辑地址 = 起始地址 + 偏移量,起始地址为0,所以逻辑地址 = 偏移量,从0x00000000->0xFFFFFFFF表示每一行指令的偏移量。事实上这个偏移量就等于虚拟地址。在ELF格式讲解中,我们查找Section Header Table中各个section的偏移量信息就是根据这里的逻辑地址来的,在Program Header Table里面查找的各个section划分的虚拟地址及其节的大小也来源于此。在读法上,一般在内存侧叫做虚拟地址,在磁盘侧叫做逻辑地址,两者之间还是有一定区别的,毕竟逻辑地址只是在起始地址为0时和虚拟地址数值上相等。

需要注意的是,我们通过反汇编查看到的正是section里面的内容,而并非section的属性。这意味着实际的每个section里面的存储结构都是一句指令配上一个逻辑地址,形成一种一一对应的存储结构。

(2)逻辑地址存在的意义

目前我们可以看出,当编译结束后,逻辑地址就确定了,每一行指令都要分配自己的地址,因为后续CPU调用都要根据地址来定位指令。所以编译时编译器就会遇到一个大难题,如何编址?如果没有逻辑地址的概念,那么编译器会很难办,因为内存的使用都是动态变化的,没有哪一块空间是每时每刻都空闲着的,如果给这个指令编址后,当运行程序时,这个地址被占用怎么办?因此,编译器采用平坦模式编址,直接全0到全F编址,编译器不仅能给每句指令编址而不发生冲突,还能和操作系统解耦。

(3)CPU调用指令

当程序运行时,CPU将调度指令执行。其中我们着重关注虚拟地址和指令在CPU中的调用流程。

寄存器EIP(也可以叫PC)负责接收虚拟地址,IR负责存储指令,CR3负责保存页表表头的物理地址,MMU是内存管理单元,一般和CR3协作进行虚拟地址和物理地址之间的转换。当PC读到虚拟地址后,会将这个地址交给MMU,MMU和CR3协助进行虚拟地址 -> 物理地址的转换(页表)。之后系统拿着转换后的物理地址将其对应的指令加载到IR中,以供CPU执行。执行完成后,CPU继续按顺序往下读取指令,由指令来控制跳转过程。

每一句指令都需要经历虚拟地址 -> 物理地址 -> 加载指令 -> 执行这个步骤的,调用函数的本质就是call函数的地址,也是通过虚拟地址来跳转的。

这个时候,我们还面临另一个问题:CPU执行的第一条语句的虚拟地址从何而来?答案就是ELF Header

通过上述流程,我们能够体会到虚拟地址其实是操作系统、编译器、CPU三者合作的产物。

(4)页表的初始化

当程序运行起来的时候,各个section会被加载到物理内存中。我前面已经说过,section里面的存储结构是一句指令配上一个逻辑地址,加载到内存中后也是如此。因此每一条被加载到物理内存的指令 + 逻辑地址的数据刚好就形成了物理地址和逻辑地址一一对应,页表也因此被初始化。

由于section偏移量及其大小属性的存在,程序加载时不一定就要加载数据,可以先根据section的偏移量初始化页表的虚拟地址,当执行时懒加载,当然也可以分批加载。有页表不一定有数据,但只要指令进入了内存,页表一定会更新该指令的映射关系

3.共享库

共享库(动态库)有个特点,就是多个存在于堆栈段的地址之间,起始地址互不相同,同时一个库能被多个进程使用(-fPIC的作用就是生成位置无关的代码,确保库中的代码可以在内存不同的位置加载)。这样一来,如何找到、调用这些动态库就成了一个难题。不过,有了前面的知识积累,我们也能尝试了解一下共享库的加载和调用。

(1)struct vm_area_struct

在mm_struct中保存有进程地址空间,但其实我们更需要维护、管理进程地址空间的结构。struct vm_area_struct就是其中之一,它存在于mm_struct中,以链表形式将进程地址空间每个段的start和end(起止位置的虚拟地址)保存了起来。也就是说,当系统想要初始化或者调整进程地址空间的划分结构时,会实时更新struct vm_area_struct里面的信息。我们也能拿着struct vm_area_struct对进程地址空间进行增删查改。

懒加载、分批加载需要struct vm_area_struct的帮助,它会帮我们进行段的位置的记录,让指令就算没有加载进来,每个段的地址范围也能确定,保证程序能够正常启动。

(2).GOT

动态库.so、可执行程序都是ELF格式的,里面每一句指令都有自己的逻辑地址,且整个ELF共用一套编址,从全0到全F,同一个文件里每个指令的编址都独一无二,这叫统一编址。有了统一编址,每个section的偏移量、大小也都唯一确定。

内存中的库可以有很多,因此当库加载到内存中后,会被操作系统用链表管理。除此之外,这个库会被映射到堆栈段之间,同时更新vm_area_struct,为它划分一块共享区。当调用这个库里面的函数时,会采用类似call libc.so:printf这样的调用方式。其中printf的逻辑地址(偏移量)早已确定,系统只需要修改libc.so为该库的起始地址,就能定位函数了。但注意call指令是在代码段,代码段是只读的。这就意味着我们需要专门用于函数调用的协助的section,也就是.GOT(拥有读写)。

调用库函数的基本逻辑是修改库所在位置的起始地址,加上库方法本来就有的逻辑地址定位。当CPU执行指令时,实际上得到的是call .GOT的地址:下标,.GOT内存的有一张表,其结构为libc.so(库的起始位置):printf(库方法的偏移量)的一一映射。当执行call命令时,由于统一编址,.GOT的地址可以通过偏移量直接得到(本来也是固定的)。之后便去.GOT里面对应下标找到函数的偏移量,再修改库所在位置的起始地址,这样libc.so:printf -> 0x1111:0x2222,函数能够正常被调用了。

需要声明的是,section中有一些值是本来就存在的,上面只是为了直观替换成了函数名。如代码段中的call .GOT的地址:下标,.GOT的地址,下标都已经在编译完成后确定,保存在硬盘里。.GOT中的libc.so:printf中的printf偏移量也已经确定,只有libc.so需要替换为实际的库的起始地址。.GOT存在的意义就是解决代码段只读的问题,利用自己可读可写的权限让系统能够修改库的起始地址,正常调用函数。整个过程称为地址重定位。

综上所述:GOT + 函数偏移量协作实现了与库的调用与其在进程地址空间的位置无关,每个ELF都有.GOT。这使得无论库在哪,操作系统都能找到它并正常调用里面的函数,当地址重定位时直接到.GOT里面修改起始地址即可。

QQ的TEA填充算法C#实现 By Red_angelX 注:本人只是从LUMA QQ的 Source Code里面把相应Java语言翻译成C#,纯技术研究,并没有对TC产品做任何逆向分析,不承担任何法律责任!转载请保持本文完整性网上有c/c++,vb,delphi,java,perl各种版本的tea填充算法,唯独没有C#的,这让我这种狂热喜爱C#的人如何承受,于是,花3天时间看了各种代码经历无数失败的挫折终于用C#完成了该填充算法,废话不多说,直接 阅读详情

相关推荐

AS7262六通道可见光传感器:从光谱原理到物联网色彩检测实战

光谱检测是嵌入式感知领域的关键技术,其核心原理是通过特定波长的滤光片分离光线并转换为电信号,从而实现对环境光或物体反射光的成分分析。这项技术的价值在于将复杂的光学分析简化为可编程读取的数字信号,极大降低了色彩检测在物联网和智能设备中的应用门槛。在智能农业、工业分拣、环境监测等场景中,精确的光谱数据能够驱动自动化决策,例如评估植物光照条件或识别物料颜色。本文以Adafruit AS7262传感器为例,深入解析其基于硅干涉滤光片的六通道设计,并通过Arduino与Python实战,演示如何快速构建从数据采集到可

weixin_28738173的博客 595

ELF文件格式与共享库

ELF头Object目标文件类型Object文件格式数据表示ELF Header结构体e_idente_type类型e_machineELF节section头表结构体section介绍符号表字符串表符号值重定位重定位类型Linux命令行 ELF头 Object目标文件类型 一个可重定位(relocation)文件保存着代码和适当的数据,用来和其他的object文件一起创建一个可执行文件或一个共...

qq_24601427的博客 2430

深入了解 CPU 的型号、代际架构与微架构!

深入了解 CPU 的型号、代际架构与微架构!

欢迎朋友,我是一名十多年开发经验的新手,希望多多指教。 3195

静态库,共享库,动态库 elf格式的

       3.2 使用    3.3 环境变量   3.4 创建共享库   3.5 安装与使用   3.6 兼容性 4.动态加载   4.1 dlopen()   4.2 dlerror()   4.3 dlsym()   4.4 dlclose()   4.5 示例 5.辅助知识   5.1 nm命令   5.2 库的构建与析构函数   5.3 脚本   5.4 版本   5.5 GNU l

xjtuguochen的专栏 2326

STM32+OOK通讯(OOK发送+OOK接收)+cubemx配置

关于OOK通信,我的理解就是将要发送的信息转换成高低电平(即0和1),然后接收方通过对01进行解码获得信息。于是我用了非常朴素的字符串转二进制+AM调制来完成通信链。话不多说我们直接开始!!!

qq_73856732的博客 2338

大模型在心律失常预测及治疗方案制定中的应用研究报告

目录一、引言1.1 研究背景与意义1.2 研究目的与方法1.3 研究创新点二、大模型技术概述2.1 大模型基本原理2.2 常见大模型类型及特点2.3 大模型在医疗领域的应用现状三、心律失常的术前预测与准备3.1 术前心律失常预测的重要性3.2 大模型在术前预测中的应用案例3.3 基于预测结果的术前准备方案四、术中监测与实时调整4.1 术中监测心律失常的方法与技术4.2 大模型辅助术中决策的机制4.3 实时调整手术方案的策略与实践五、术后恢复与并发症风险预测5.1 术后心律失常的监测与评估5.2 大模型预测术

嵌入式毕设/课设资料清单(速查手册·文档模板·题库,持续更新)→ 见最新文章 1596

ELF文件介绍

https://blog.youkuaiyun.com/GrayOnDream/article/details/124564129

二进制模----细微行动改变影响世界 5817

局放起始电压(PDIV)与击穿电压(BDV):绝缘系统设计的核心参数 [特殊字符]

在电力设备绝缘系统设计中,**局放起始电压(Partial Discharge Inception Voltage, PDIV)和击穿电压(Breakdown Voltage, BDV)**是两个至关重要的参数。它们直接反映了绝缘材料的性能和绝缘系统的可靠性,对变压器的设计、制造和运行具有重要指导意义。本文将深入探讨这两项参数的定义、测试方法及其与绝缘设计制造的关系,帮助读者全面理解其在电力设备中的核心作用。

mubiao05的博客 2629

vdso——虚拟 elf 动态共享库

$ nm /home/longyu/linux-git/arch/x86/entry/vdso/vdso64.so.dbg 0000000000000730 W clock_gettime 00000000000005f0 t do_hres 0000000000000348 d _DYNAMIC 0000000000000820 W getcpu 00000000000007a0 W gettimeofday 00000000000004c8 r __GNU_EH_FRAME_HDR fffffffff

龙瑜的博客 1900

【OpenSearch】从零到一:手把手搭建OpenSearch与Dashboard可视化平台

本文提供了一份详细的OpenSearch与Dashboard可视化平台从零搭建指南。内容涵盖环境准备、系统调优、核心服务安装配置、Dashboard部署与汉化,以及生产环境安全加固建议,帮助用户快速构建一个功能完整的搜索与分析平台。

devops8pract的博客 617

学Simulink——基于高渗透率可再生能源微电网的逆变器系统场景实例:含高比例逆变型电源的微电网暂态稳定性分析

本文详细介绍了基于MATLAB/Simulink的高渗透率可再生能源微电网建模方法,重点分析含高比例逆变型电源的微电网暂态稳定性问题。通过构建包含光伏、风电、逆变器和电网等模块的系统模型,展示了从项目创建、模块搭建到控制策略实现的完整过程。文章特别提供了逆变器PQ控制算法和故障处理逻辑的代码示例,并介绍了仿真参数设置与结果分析方法。该模型能有效评估微电网在故障条件下的动态响应特性,为优化逆变器控制策略提供依据,对现代智能电网研究具有重要意义。

xiaoheshang_123的博客 140

linux elf 加载位置,将ELF共享库和自定义binfmt可执行文件加载到相同的Linux地址空间中...

我正在开发一个项目来加载和运行Linux平台上的自定义二进制格式可执行文件(在我的例子中是PE).到目前为止,我已经成功地完成了这项工作,首先加载可执行文件,然后加载一个调用可执行文件起始地址然后安全退出的小型ELF共享库.不过,出于某些原因,我真的不想自己装ELF.首先,我使用的共享库是用汇编语言编写的(我不能使用任何其他东西,因为我没有链接到libc等),这将是非常特定于平台的,我想摆脱它并使...

weixin_32085599的博客 247

实现画布拖拽算子排布,通过flink实现算子编排计算,如何实现?

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值 。

**My Coding Family** 1070

Linux系统篇22:——文件(六):目标文件与ELF深度解析:从编译到加载的全景揭秘

ELF(Executable and Linkable Format,可执行与可链接格式)是 Linux 下可执行文件、目标文件、共享库、核心转储的统一文件格式标准。要理解编译和链接的细节,我们必须了解 ELF。实际上,以下四种文件文件类型扩展名/名称说明可重定位文件.o包含适合与其他目标文件链接来创建可执行文件或共享目标文件的代码和数据可执行文件无扩展名 /a.out即可执行程序,经过完整链接,可以直接加载运行共享目标文件.so动态链接库,包含可在运行时被动态链接器加载和链接的代码和数据内核转储。

2502_94387000的博客 1480

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Linux ELF格式分析

http://www.cnblogs.com/hzl6255/p/3312262.html ELF, Executable and Linking Format, 是一种用于可执行文件、目标文件、共享库和核心转储的标准文件格式。ELF格式是是UNIX系统实验室作为ABI(Application Binary Interface)而开发和发布的。 这里简单介绍一下相关历史:- ...

diaoju3333的博客 216

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Linux中《动/静态库原理》

本文深入解析了ELF(Executable and Linkable Format)文件格式在编译链接过程中的关键作用。从目标文件(.o)生成、多文件合并到可执行文件加载,详细阐述了ELF文件的四种类型(可重定位、可执行、共享目标、核心转储)及其四部分结构(ELF头、程序头表、节头表、节)。重点介绍了readelf工具的使用方法,并通过实例展示了可执行程序加载时Section合并为Segment的过程,揭示了程序从编译到运行的底层实现机制。

zxybf_的博客 880

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Linux内核分析——第七周学习笔记20135308

第七周 可执行程序的装载 一、预处理、编译、链接和目标文件的格式 1.可执行程序是怎么来的 C代码—>预处理—>汇编代码—>目标代码—>可执行文件 .asm汇编代码 .o目标码 a.out可执行文件 (1)预处理:负责把include的文件包含进来及宏替换工作,即文字替换,.c变成.i  gcc -E -o main.i main.c (2)编...

aochandao4900的博客 245

ELF文件结构和实战分析

ELF文件 实战分析

薛瑄的博客 2008

Linux 进程热升级 共享库的动态替换

背景 用户总是希望服务进程能保持稳定。如果可以 7*24 小时的工作,那就永远不要重启它。但是,软件产品的功能总是在不断的丰富。当用户发现一些新的功能正是他所需要的,他也许会主动要求进行一次升级。而当严重的安全问题出现时,用户就不得不接受强制的升级了。 不停机升级,也被称为热升级。通常实现热升级,需要用户部署两套业务系统。至少,被升级的关键模块是两块以上的。这一般是通过硬件方式支持的。由此而产生的成本压力,不是每个用户都可以接受的。 对于小型业务系统,频繁的升级总是不可避免的。如果升级过程中,业务进程

~~ LINUX ~~ 1228

linux内核溢出研究系列,Linux内核研究系列之可执行文件格式

Linux内核研究系列之可执行文件格式发布时间:2006-02-22 00:52:50来源:红联作者:大长今我们知道linux环境下不是所有的二进制文件都有相同的格式,linux系统使用二进制文件的处理程序来实现对不同二进制格式文件的分别处理。二进制处理程序通过内嵌在文件开头的“特征序列”(一个特殊的字节序列)来识别文件,有时也会通过文件名的一些特征,例如ELF文件以'E''L''F'字符开头,j...

weixin_35663151的博客 186

Linux内核如何装载和启动一个可执行程序

郑斌+ 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验要求: 理解编译链接的过程和ELF可执行文件格式,详细内容参考本周第一节;​ 编程使用exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接,编程练习动态链接库的这两种使用方式,...

weixin_30949361的博客 105

《Linux内核分析》课程第七周学习总结

姓名:何伟钦 学号:20135223 (*原创作品转载请注明出处*) ( 学习课程:《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-100002900 学习内容:Linux内核如何装载和启动一个可执行程序 理解编译链接的过程和ELF可执行文件格式; 编程使用exec*库函数加载一个可执...

weixin_30639719的博客 219

Linux下动态库(共享库)的制作与使用

11Linux下动态库(共享库)的制作与使用 https://blog.youkuaiyun.com/weixin_51483516/article/details/120621135

abc 259

Linux命令-ldconfig(共享库配置工具)

是 Linux 系统中用于管理和配置动态链接库的重要工具,主要作用是更新动态链接库的缓存,确保系统能够正确找到新安装的库文件。环境变量来临时指定库的路径。但这通常被视为一种临时的解决方案。:在编译或运行程序时,可以检查某个必需的库是否已被系统识别。需要注意以下几点,这能避免很多常见的库链接问题。掌握以下几个典型场景的用法,就能应对大部分情况。:安装新的软件或库之后,最常用的命令。:在调试或想确认新库是否被正确识别时,使用。选项可以看到详细的扫描过程和信息。文件中列出的目录,并更新缓存。

RisunJan的博客 570

linux pwn 基础知识

环境搭建 虚拟机安装 镜像下载网站 为了避免环境问题建议 22.04 ,20.04,18.04,16.04 等常见版本 ubuntu 虚拟机环境各准备一份。注意定期更新快照以防意外。 虚拟机建议硬盘 256 G 以上,内存也尽量大一些。硬盘大小只是上界,256 G 不是真就占了 256 G,而后期如果硬盘空间不足会很麻烦。 基础工具 vim sudo apt install vim gedit 不习惯 vim 的可以使用 gedit 文本编辑器。 sudo apt install gedit git

sky123博客 4622

小白言承之PWN学习路线(持续更新)

PWN(溢出):PWN在黑客俚语中代表着攻破,取得权限,在CTF比赛中它代表着溢出类的题目,其中常见类型溢出漏洞有栈溢出、堆溢出。在CTF比赛中,线上比赛会有,但是比例不会太重,进入线下比赛,逆向和溢出则是战队实力的关键。主要考察参数选手漏洞挖掘和利用能力。 CTF PWN特点:入门难、进阶难、精通难 pwn学习内容: (1)了解Linux ELF文件 (2)分析掌握栈溢出原理理解函数参...

像初雪一样自由洒落 8187
上一篇: Linux相关概念和易错知识点(21)(软硬链接、动静态库)
下一篇: Linux相关概念和易错知识点(23)(进程间通信、匿名管道、进程池)
-SGlow-
博客等级 码龄3年 1513粉丝 118原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值