嵌入式C语言自我修养:编译链接

源文件生成可执行文件的过程?

源文件经过预处理、编译、汇编、链接生成一个可执行的目标文件。

编译器驱动程序,包括预处理器、编译器、汇编器和链接器。Linux用户可以调用GCC驱动程序来完成整个编译流程。

使用GCC驱动程序将示例程序从ASCII码源文件转换成可执行目标文件的步骤。

编译执行:

gcc -Og -o prog main.c sum.c ./prog

  1. 预处理:调用预处理器,对源文件main.c进行预处理,去除注释、展开宏定义等,生成一个中间的ASCII码文件main.i。
  2. 编译:调用C编译器,将预处理后的中间文件main.i转换成ASCII码的汇编语言文件main.s

编译阶段是指编译器读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码。

  1. 汇编:调用汇编器,将汇编语言文件main.s翻译成可重定位目标文件main.o。同样的过程也会应用于sum.c,生成相应的sum.o。
  2. 链接:调用链接器将main.o和sum.o以及其他必要的系统库文件链接在一起,创建出最终的可执行目标文件prog。
  3. 加载:将可执行程序加载到内存并进行执行。当shell执行./prog命令时,操作系统内的加载器(loader)将会把可执行文件prog中的代码和数据加载到内存中,并设置好运行环境,然后将控制权转移给程序的入口点,从而启动程序执行(详细见程序的加载)
什么是链接?

链接是将不同目标模块整合为单一的可执行文件。链接可以在程序的不同阶段进行。静态链接编译期间进行的链接,动态链接在运行期间进行的链接。

通过链接可以把程序分散到不同的小的源代码中,而不是一个巨大的类中。这样可以复用常见的功能。

目标文件(模块)由段组成(见ELF格式),主要有以下三种类型:

(1)可重定位目标文件

由汇编器处理后的机器码和数据(.s->.o),还没有进行重定位和符号解析。多个可重定位目标文件可以合并为一个单一的可执行目标文件。

(2)可执行目标文件

可以直接被操作系统加载到内存中执行。

共享目标文件

指动态链接库,在Linux系统中表现为.so文件,在Windows系统中表现为.dll文件。它们同样是可重定位目标文件的一种,但在程序运行时动态加载到内存,并与应用程序进行链接。这种机制允许多个进程共享同一份代码,节省资源并提高内存利用率。

目标文件有统一的格式ELF,可重定位目标文件常见的段包括:

.text

代码段(指令、机器码)

.rodata

只读数据

.data

已初始化的全局和静态变量。

.bss

未初始化(或初始化为0)的全局变量和静态变量。

symtab

符号表,存放在程序中定义和引用的函数与全局变量的信息(局部变量不存放)

.rel.text

.text节的重定位信息,当链接时,指令的位置就被修改

.rel.data

.data节的重定位信息,当链接时,变量的位置就被修改

.debug

调试符号表

Section Header Table

节头部表,存放每个节的偏移和大小

符号表记录了模块内定义和引用的各种符号信息。符号表存放如下三种符号:全局变量、外部符号,静态全局变量,这些符号对本模块是随处可见的,但不能被其他模块所引用。局部变量会由栈来管理。局部静态变量,会存放到.bss或者.data节中,也不由符号表来管理。

链接的工作:符号解析和重定位。

(1)符号解析

符号解析把所有目标文件中每一个符号引用都能够绑定到对应的符号定义上。按顺序需要遍历所有输入的目标文件,找出并解决符号依赖关系,确保没有未定义的外部引用。

(2)重定位(Relocation)

编译器和汇编器在生成目标文件时,会为代码和数据分配相对的虚拟地址空间。链接器需要确定每个符号的实际内存地址,并据此更新目标文件中所有的地址引用。利用汇编器产生的重定位表,对每个引用该符号的位置进行调整,使它们指向正确的运行时内存位置。

符号解析:链接器会处理三种符号全局符号(全局变量)、外部符号(外部变量)、本地符号(静态符号)。

编译器只允许模块中的本地符号只有一个定义。当编译器遇到一个不是在本模块中定义的符号时,假设在某个其他模块中定义的,并把后续工作交给链接器处理。当编译器遇到不在当前模块中定义的符号引用时,链接器遍历所有输入目标文件,寻找匹配的全局符号定义。若找不到定义,则会报错终止链接过程。

符号冲突:具有相同全局符号名但在不同目标文件中都有定义的情况,链接器需要解决符号冲突。一次定义原则:即同一个全局符号只能有一个有效定义。

强符号函数和初始化的全局变量。弱符号未初始化的全局变量。不允许多个强符号名称重复。若一个强符号和多个弱符号名称重复,选择强符号;若多个弱符号名称重复,从中任选其一。

关于重载函数的链接问题,译器将函数的名称和参数类型信息一起编码成一个独一无二的、对链接器友好的名称。

重定位

      重定位的任务是将输入的各个目标模块合并成一个单一的可执行文件,并为每个符号分配运行时内存地址。链接器将所有输入模块中相同类型的节合并成一个新的聚合节。(所有输入模块的.data节会被合并成输出可执行文件中的一个.data节)。链接器为这个新的聚合节、输入模块定义的每个节以及每个符号赋予运行时内存地址。这样一来,程序中的每条指令和全局变量都将拥有一个唯一的运行时内存地址。

      链接器会对代码节和数据节中的每个符号引用进行修改,指向正确的运行时地址。为此,链接器依赖于可重定位目标模块中预先定义好的重定位条目(relocation entry)。重定位条目是一种数据结构,它记录了目标模块中需要被修改的位置以及如何修改这些位置以适应运行时环境的信息。链接器根据这些重定位条目,逐一修改符号引用,确保在程序执行时能够准确寻址到对应的符号定义。重定位条目:在汇编器生成目标模块时,由于它无法预知模块中的数据和代码在内存中的具体加载位置,也无法确定模块所引用的外部函数或全局变量的具体位置,因此每当遇到这类不确定位置的引用时,汇编器会在目标文件中生成相应的重定位条目。这些重定位条目指示链接器在合并目标文件生成可执行文件的过程中如何正确地修正这些引用。

静态链接

   静态链接在程序运行之前,将目标模块和所需的静态库链接成一个完整的可执行文件。静态库将一组相关的可重定位目标文件(.o文件)打包成单个.a文件。(Linux系统中,静态库以一种存档的特殊文件存放在磁盘中,存档文件由.a标识)。

  模块化:将函数编译为独立的目标模块,便于管理和更新。

(2) 按需链接:链接器在链接过程中仅将应用程序实际引用的库模块加入到最终可执行文件中,避免了资源冗余。

链接器是如何使用静态库解析引用?

      如果静态库之间是相互独立的,库可以任意顺序排列在命令行的末尾。如果库之间存在依赖关系,即某个库的成员引用了另一个库中定义的符号,那么必须按照依赖关系正确排序库的顺序。因为链接是按顺序扫描目标文件和静态库文件,所以可能存在引用依赖问题的,写编译命令的时候,需要按顺序写。在命令行中,如果定义一个符号的库在引用这个符号的目标文件之前,引用就不会被解析(因为加到了未解析符号引用的集合),因此链接会失败。

动态链接共享库

   静态库虽然有效地解决了函数库复用的问题,但存在一些局限性:

(1)如果静态库中有bug 修复时,所有依赖它的应用程序都需要重新编译并链接到更新后的库版本。(改动后需要重写编译)

(2)内存资源浪费:静态链接导致每个使用标准库的应用程序都会包含库函数的副本。当系统运行很多进程时,这些重复的代码片段占用内存资源。

动态链接

     程序运行时第一次调用某个库函数的时候,由动态链接器负责将程序与共享库链接起来。共享库是存放在磁盘上的对象(unix .so,Windows DLL),可以在运行时使用内存映射的方式加载到程序的共享内存映射区域。

     动态链接也可以在程序开始执行的时候完成,在Linux 中使用 dlopen()接口来完成(会使用函数指针),而且共享库也可以在多个进程间共享。

共享库(动态链接)的优点

(1)内存优化:共享库中的代码在物理内存中只有一份拷贝,所有需要它的进程都可以共享同一份代码,显著减少了内存开销。

(2)当共享库有更新时,只需要替换系统上的库文件即可。已安装的应用程序在下次运行时会自动链接到新版本的库,无需重新编译或重新发布整个应用程序。

     在Linux系统中,运行时动态加载和链接共享库的功能通常通过dlopen() 和dlsym() 等函数实现,这些函数属于动态链接器接口的一部分。dlopen() 用于打开并加载指定的共享库,而 dlsym() 则用于从已加载的库中获取函数指针,以便后续调用。 C标准库默认使用动态链接。编译时候加-static选项可以使用静态链接。

动态链接库如何加载到内存中的?

⭐ 关联知识点:mmap内存映射,exec启动,

     当动态库第一次被链接器加载到内存参与动态链接时,动态库映射到了当前进程虚拟空间的mmap区域,动态链接和重定位结束后,程序就开始运行。当程序访问mmap映射区域,去调用动态库的一些函数时,发现此时还没有为这片虚拟空间分配物理内存,就会产生一个请页异常。内核接着会为这片映射内存区域分配物理内存,将动态库文件libtest.so加载到物理内存,并将虚拟地址和物理地址之间的映射关系更新到进程的页表项,此时动态库才真正加载到物理内存,程序才可以正常运行。

对于已经加载到物理内存的文件,Linux内核会通过一个radix tree(基数树)的树结构来管理这些页缓存对象。当进程B运行也需要加载动态库libtest.so时,动态链接器会将库文件libtest.so映射到进程B的一片虚拟内存空间上,链接重定位完成后进程B开始运行。当通过映射内存地址访问libtest.so时也会触发一个请页异常,Linux内核在分配物理内存之前会先从radix tree树中查询libtest.so是否已经加载到物理内存,当内核发现libtest.so库文件已经加载到内存后就不会给进程B分配新的物理内存,而是直接修改进程B的页表项,进程B中的这片映射区域直接映射到libtest.so所在的物理内存上。

基数树阅读材料:Radix Tree用法-CSDN博客

打包静态库:

gcc add.c -c -o add.o

gcc swap.c -c -o swap.o

ar rcs打包静态库

ar rcs libcalc.a swap.o add.o

打包动态库:使用gcc在链接的时候生成共享库文件(.so)

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

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

相关文章

数字图像处理:边缘检测

数字图像处理:边缘检测 笔记来源: 1.Gradient and Laplacian Filter, Difference of Gaussians (DOG) 1.1 图像一阶梯度 水平方向的一阶导数 一阶导数滤波器在实际应用中难实现的原因 图像梯度中,一阶梯度中找局部极大值就是边缘所在处&a…

SOCKS5代理和HTTP代理哪个快?深度解析两者的速度差异

在现代互联网环境中,使用代理IP已经成为了许多人日常生活和工作的必备工具。无论是为了保护隐私,还是为了访问某些特定资源,代理IP都扮演着重要的角色。今天,我们就来聊聊SOCKS5代理和HTTP代理,看看这两者到底哪个更快…

netty编程之实现websocket客户端并发送二进制消息

写在前面 源码。 本文看下netty如何实现websocket客户端并发送二进制消息。 ws的server端参考这篇文章。 1:正文 抽象类AbstractWebsocketClient定义了发送二进制数据的方法: public abstract class AbstractWebsocketClient implements Closeable {…

向量数据库|第1期|从零开始学习

向量数据库|第1期|从零开始学习 1、向量数据库中的基本概念 1.1 什么是余弦 余弦函数是一种三角函数,在直角三角形中,某个锐角的余弦为:临边与斜边的比值,如下图cosAb/c。引申到任意三角形中,即余弦定理:…

大数据-151 Apache Druid 集群模式 配置启动【上篇】 超详细!

点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…

数据结构--二叉树的顺序实现(堆实现)

引言 在计算机科学中,二叉树是一种重要的数据结构,广泛应用于各种算法和程序设计中。本文将探讨二叉树的顺序实现,特别是堆的实现方式。 一、树 1.1树的概念与结构 树是⼀种⾮线性的数据结构,它是由 n(n>0) 个有限结点组成…

【C++打怪之路Lv6】-- 内存管理

🌈 个人主页:白子寰 🔥 分类专栏:C打怪之路,python从入门到精通,数据结构,C语言,C语言题集👈 希望得到您的订阅和支持~ 💡 坚持创作博文(平均质量分82)&#…

15分钟学 Python 第36天 :Python 爬虫入门(二)

Python 爬虫入门:环境准备 在进行Python爬虫的学习和实践之前,首先需要准备好合适的开发环境。本节将详细介绍Python环境的安装、必要库的配置、以及常用工具的使用,为后续的爬虫编写奠定坚实的基础。 1. 环境准备概述 1.1 为什么环境准备…

基于Springboot投稿和稿件处理系统设计与实现

博主介绍:专注于Java vue .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设,从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用 感兴趣的…

数据集-目标检测系列- 货船 检测数据集 freighter>> DataBall

数据集-目标检测系列- 货船 检测数据集 freighter>> DataBall 数据集-目标检测系列- 货船 检测数据集 freighter>> DataBall 数据量:3k 想要进一步了解,请联系。 DataBall 助力快速掌握数据集的信息和使用方式,会员享有 百种…

订阅ROS2中相机的相关话题并保存RGB、深度和点云图

系统:Ubuntu22.04 ROS2版本:ROS2 humble 1.订阅ROS2中相机的相关话题并保存RGB图、深度图和点云图 ros2 topic list/stellar_1/rgb/image_raw /camera/depth/image_raw /stellar_1/points2CMakeLists.txt cmake_minimum_required(VERSION 3.15) projec…

建筑资质的未来发展趋势

🏗️建筑资质是建筑企业进入市场的通行证,它不仅关系到企业的竞争力,也影响着整个建筑行业的健康发展。随着政策的调整和技术的进步,建筑资质管理正面临着新的变革。 1. 资质管理的数字化转型:🌐 随着信息技…

Gaussian-splatting 项目环境配置笔记(Win11)

如果你是配置别的项目的过程中用到了3D GS相关的内容,然后这部分内容环境一直配不好,也可以跟随这个博客配一下环境,配完后起码3D GS部分就搞定了。 文章目录 概述项目链接:VS2019直接下载链接CUDA不同版本下载链接安装Condasetup…

63.5 注意力提示_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录注意力提示生物学中的注意力提示查询、键和值注意力的可视化使用 show_heatmaps 显示注意力权重代码示例 代码解析结果 小结练习 注意力提示 🏷sec_attention-cues 感谢读者对本书的关注,因为读者的注意力是一种稀缺…

【MATLAB2024b】安装离线帮助文档(windows)

文章目录 一、在 MATLAB 设置中安装二、从math works 网站下载ISO:给无法联网的电脑安装三、重要说明 版本:matlab 2024b(或者大于等于2023a) 所需空间:10~15 GB 平台:Windows 需要注册math works账号。 一…

深度学习-19-深入理解并训练自己的Tokenizer分词器

文章目录 1 tokenization是什么2 Tokenization方法简介2.1 单词级的Tokenization2.2 子词Tokenization技术2.3 举例说明2.3.1 字符级别2.3.2 词语级别2.3.3 子词级别3 训练自己的Tokenizer3.1 下载数据集3.2 huggingface的Tokenizer实现3.3 my-tokenizer.json字段说明3.4 验证一…

鸿蒙harmonyos next flutter混合开发之开发package

​​​​​​ 创建 package flutter create --templatepackage mypackage package代码如下: 创建hello_world.dart ///HelloWorld返回hello world 拼接param class HelloWorld {String helloWorld(String param) > "hello world ${param}"…

【视频目标分割-2024CVPR】Putting the Object Back into Video Object Segmentation

Cutie 系列文章目录1 摘要2 引言2.1背景和难点2.2 解决方案2.3 成果 3 相关方法3.1 基于记忆的VOS3.2对象级推理3.3 自动视频分割 4 工作方法4.1 overview4.2 对象变换器4.2.1 overview4.2.2 Foreground-Background Masked Attention4.2.3 Positional Embeddings 4.3 Object Me…

CSS实现服务卡片

CSS实现服务卡片 效果展示 CSS 知识点 回顾整体CSS知识点灵活运用CSS知识点 页面整体布局 <div class"container"><div class"card"><div class"box"><div class"icon"><ion-icon name"color-pal…

Mac 卸载 IDEA 流程

1、现在应用程序中删除Idea 2、进入Library目录 cd /Users/zhengzhaoxiang/Library 3、删除IntelliJIdea2023.3&#xff08;根据自己的版本而定&#xff09;记得进去看下是否删除干净了 rm -rf Logs/JetBrains/IntelliJIdea2023.3 rm -rf Preferences/com.jetbrains.intel…