库的原理:目标文件,ELF格式,程序加载
个人主页:小则又沐风个人专栏:数据结构竞赛专栏C语言CLinux座右铭悟已往之不谏知来者之可追目录前言目标文件ELF文件ELF Header节头表(Section header table)节Section 程序头表(Program header table)ELF形成可执行文件静态链接ELF加载与进程地址空间动态链接前言上一篇的文章中我们知道了库文件的制作以及怎么使用我们自己制作的库文件,但是上述的内容只是停留在使用的层面上,今天我们来了解一下底层的知识.目标文件我们之前使用的编写代码的工具都是vs2022,在这上面上我们没有直观的看到代码的编译和链接在Linux下可以清楚的看到.//code.c #includestdio.h void run(); int main() { printf(hello,world\n); run(); return 0; } //hello.c #includestdio.h void run() { printf(i am runing...\n); }我们简单的编写这个代码,来看看这个代码的编译和链接的过程gcc -c code.c hello.c可以看到的是现在我们得到了两个.o后缀的文件,这两个文件就是目标文件,如果我们修改了一个目标文件的源文件的话我们只需要重新编译就可以了,不需要进行过多的操作我们打开目标文件就会发现这个文件是一个二进制的文件目标文件的格式是ELF格式ELF文件这张图中我们看到这个信息:relocatable这个代表的是这个文件是一个可重定向的文件(这个具体的含义之后会进行讲解)那么ELF文件的类型都有什么?可重定位⽂件Relocatable File xxx.o可执⾏⽂件Executable File共享⽬标⽂件Shared Object Filexxx.so内核转储(core dumps)如上图就是ELF格式的示意图ELF Header这个是ELF的表头,里面将会描述ELF文件的主要特征.主要的内容是:定位节头表,程序头表可执行程序的入口地址(在目标文件中是0)还有特定的内容来作为自己的标识符下面来简单的查看一下我们目录下的目标文件readelf -h code.o我们可以这样来理解Header这个表中的信息是来标识自己的特点的,并且指出了自己的ELF中节头表和程序表的地址和文件中偏移量,并且需要我们注意的是上面中的信息有一个Entry point address这个就是程序的入口地址(在之后会进行详细的讲解)节头表(Section header table)节头中会存储着我们ELF中主要的信息,可以理解为文件中的数据.程序中的数据根据着不同的特征划分在了不同的节中那么这个节头表的作用就是用来找到不同的节,有助于之后的不同的节的合并节Section 节就是存储着ELF文件的主要数据的部分,根据不同的数据类型会分成为许多不同的节例如:.text 代码节 存储可执行机器指令程序核心逻辑.data 数据节 存储已初始化的全局变量和局部静态变量.bss 未初始化数据节 为未初始化的全局变量 / 静态变量预留空间不占文件空间加载时分配内存.symtab 符号表 存储函数名、变量名与地址的映射关系.reloc 重定位表 记录需要链接时修正的地址如外部函数调用.rodata 只读数据节 存储字符串常量等只读数据如 printf 的字符串程序头表(Program header table)这个表仅存在于可执行文件和动态库中,因为在这些的文件中,才会进行合并的操作,这个就是文件进行合并操作的指南.拥有这个指南之后合并两个文件形同属性的内容,形成段(Segment)段和节的区别我们在上述讲解的过程中产生了两个名词------段和节那么这两个的区别是什么呢?首先节是产生在编译的环节中的,相对于段,节对数据的分类是更加的细致的.例如:代码节,数据节.....然而段是产生在执行的时候的,因为他是将多个ELF文件按照程序头表这个指南合并产生的段段对数据的分类并没有那么的细致了,比如:可读,不可读....那么为什么需要段,不能把节使用到底吗?段的使用会使内存得到更充分的利用假设我们的代码节的内存是4097个字节,数据段是127字节这时候就会导致有的页会有很多的内存空余,但是如果用段合并的话,就会大大的减少上述的情况解决了内存的碎片化ELF形成可执行文件编译形成ELF文件(将C/C源文件编译形成目标文件.o)将目标文件进行合并下面我们先来看看静态链接的过程静态链接首先我们现在手下有很多的.o文件,这些文件共同的特点就是他们都是ELF格式的在编译链接的时候这些.o文件会把相同的节进行合并例如:将多个的代码节合并成一个得到代码节但是他是怎么知道那些节存储的是什么呢??这时候通过查找节头表和表头就可以知道各个节的起始和大小了,进行合并就可以了然后将节合并之后,然后将函数的地址进行填写objdump -d code.o运行这个指令来看一下反汇编可以看到这个函数的回调的地址是一个0,这就是因为我们的code.c文件中没有这个函数的实现,但是当把目标文件进行合并之后就会有了函数的地址了,所以就要进行函数地址的填写可以看到在我们链接之后的可执行文件函数的地址的确是完成了填写的总结一下静态链接首先编译形成一个个的目标文件,这些的目标文件都是ELF格式,然后进行链接,将每个.o文件的相同的节进行合并,将静态库中嵌入到文件中,重定向函数的地址ELF加载与进程地址空间首先ELF在加载到内存的时候他是有地址的.在链接的时候会基于平坦模式对形成的段进行编址(平坦模式就是起始地址是0),因为初始地址是0所以逻辑地址是和虚拟地址相同的,然后在Header中会存储着各个段的起始地址和长度的,并且还存储着程序的入口地址,所以在加载到内存的时候需要做的就是填写进程的mm_struct表.因为在ELF中都存有各个段的起止地址直接复制就可以.拿着程序入口地址执行程序就可以了动态链接动态链接与静态链接不同就是静态链接是在链接的时候就把函数的地址进行了重定向了,然而动态链接的函数的重定向被推迟到了程序加载的时候了.那么动态库链接的详细的过程是什么呢??首先需要确定这个程序有什么动态库吧怎么确定的呢?我们需要知道的是一个程序的入口不是main函数,而是一个_start函数在这个_start函数中为程序准备堆栈环境初始化数据段最重要的就是检查这个程序依赖的动态库有什么,然后加载动态库文件到内存中.补充:加载动态库的实质就是打开这个动态库的文件,打开文件就是我们熟悉的文件操作了也就是通过目录查找文件名和inode的映射,查找目标文件的inode,找到文件的数据段在这里就是将动态库中需要的函数和方法加载到了内存中不过我们访问库函数通过虚拟地址进 ⾏跳转访问的所以需要把动态库映射到进程的地址空间中具体就是我们通过文件操作打开了对应的库文件,加载到物理内存之后,在页表中建立映射,填写虚拟地址表,得到初始虚拟地址那么进行库函数调用程序进行到了动态库函数的部分,这时候就从代码区跳转到共享区,拿到库函数的起始虚拟地址,方法的地址偏移量和起始地址就可以找到对应的方法函数动态库为了实现随时都可以加载,采用了相对编址的方法那么现在就是这样的过程了首先在_start函数中加载需要的库函数,然后在我们的代码中函数的地址进行二次定位但是有一个问题就是代码区的数据时只读的,我们没有权限对他进行修改所以动态链接采⽤的做法是在 .data 可执⾏程序或者库⾃⼰中专⻔预留⼀⽚区域⽤来存放函数 的跳转地址它也被叫做全局偏移表GOT表中每⼀项都是本运⾏模块要引⽤的⼀个全局变量或函数 的地址。所以动态库加载的完全体就是这样的过程:首先在_程序启动的时候加载需要的库函数,然后在数据区中的GOT表中填写动态库的地址我们的代码区的结构是这样的假设我们将要调用printf函数他会给出我们在GOT表中这个方法的位置然后再加载入动态库之后把printf函数的地址填写到这个GOT表中简单的来说GOT表相当于一个目录一样再加载的时候提供方法的地址代码运行到需要库函数的时候直接查表就行了

相关新闻

终极指南:3个技巧让IINA播放器性能翻倍,告别4K卡顿

终极指南:3个技巧让IINA播放器性能翻倍,告别4K卡顿

终极指南:3个技巧让IINA播放器性能翻倍,告别4K卡顿 【免费下载链接】iina The modern video player for macOS. 项目地址: https://gitcode.com/gh_mirrors/iin/iina 还在为macOS播放器卡顿、HDR色彩失真而烦恼吗?IINA作为macOS平台最…

2026/7/19 14:06:39 阅读更多 →
第17章:复制集基础——从单机到高可用

第17章:复制集基础——从单机到高可用

1. 项目背景 业务场景:本地生活电商上线三个月,单台 MongoDB 开始力不从心。某天凌晨 2 点,服务器突然宕机——只是普通的内存故障,但因为只有一台 MongoDB,整个系统直接不可用,直到早上 8 点运维重启服务…

2026/7/19 14:06:39 阅读更多 →
G-Helper终极指南:三步释放华硕笔记本隐藏性能的免费轻量工具

G-Helper终极指南:三步释放华硕笔记本隐藏性能的免费轻量工具

G-Helper终极指南:三步释放华硕笔记本隐藏性能的免费轻量工具 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenb…

2026/7/19 14:06:39 阅读更多 →

最新新闻

Postman通Swagger不通?详解API测试差异与排查指南

Postman通Swagger不通?详解API测试差异与排查指南

1. 问题现象与核心矛盾解析 最近在排查一个线上问题时,遇到了一个非常典型且让不少开发者困惑的场景:一个后端API接口,在Postman里调用得风生水起,响应又快又准,数据格式完全正确。但当我们满怀信心地把接口文档&#…

2026/7/19 20:45:59 阅读更多 →
STM32多传感器融合的人体平衡检测仪设计与实现

STM32多传感器融合的人体平衡检测仪设计与实现

1. 项目概述与核心功能设计这个便携式人体平衡检测仪的核心思路是通过多传感器融合技术,实现对人体平衡能力的量化评估。我在实际开发中发现,传统平衡检测设备往往体积庞大且价格昂贵,而基于STM32的方案能以极低成本实现专业级功能。系统采用…

2026/7/19 20:45:59 阅读更多 →
高效书签管理系统:从收集到知识转化的全流程指南

高效书签管理系统:从收集到知识转化的全流程指南

1. 书签管理:从零开始构建个人知识库作为一名长期与信息打交道的从业者,我收集了超过5000个各类书签,从技术文档到设计资源,从行业报告到实用工具。这些书签就像散落的珍珠,如果不加以整理,它们的价值就会随…

2026/7/19 20:45:59 阅读更多 →
TMS320F2838x CLA寄存器详解:任务管理、中断控制与实战调试

TMS320F2838x CLA寄存器详解:任务管理、中断控制与实战调试

1. CLA寄存器概览与核心设计思想在TMS320F2838x这类高性能实时微控制器中,控制律加速器(CLA)是一个独立于主C28x CPU的浮点协处理器。它的存在,本质上是为了将那些对实时性要求苛刻、计算密集的控制算法(比如电机控制中…

2026/7/19 20:45:59 阅读更多 →
C#邮件发送全攻略:从基础到企业级实践

C#邮件发送全攻略:从基础到企业级实践

1. C#邮件发送基础与核心原理 在.NET生态中,System.Net.Mail命名空间提供了完整的邮件处理能力。SMTP协议作为邮件发送的事实标准,其工作原理类似于传统邮局系统:客户端(你的代码)通过SMTP服务器(邮局&…

2026/7/19 20:45:59 阅读更多 →
U-Boot目录结构与编译过程全解析

U-Boot目录结构与编译过程全解析

1. U-Boot项目概述 作为嵌入式Linux系统开发中最关键的组件之一,U-Boot承担着硬件初始化、引导加载的核心职责。我接触过数十个基于ARM、MIPS、PowerPC等架构的嵌入式项目,每个项目都离不开对U-Boot的深度定制。今天我们就来解剖这个"嵌入式世界的瑞…

2026/7/19 20:44:58 阅读更多 →

日新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/19 0:00:40 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:00:40 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:00:40 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/19 0:00:40 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:00:40 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:00:40 阅读更多 →

月新闻