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

news2024/11/29 12:51:27
源文件生成可执行文件的过程?

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

编译器驱动程序,包括预处理器、编译器、汇编器和链接器。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.coloradmin.cn/o/2189110.html

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

相关文章

如何使用EventChannel

文章目录 1 知识回顾2 示例代码3 经验总结我们在上一章回中介绍了MethodChannel的使用方法,本章回中将介绍EventChannel的使用方法.闲话休提,让我们一起Talk Flutter吧。 1 知识回顾 我们在前面章回中介绍了通道的概念和作用,并且提到了通道有不同的类型,本章回将其中一种…

仿RabbitMQ实现消息队列服务端(一)

文章目录 交换机数据管理队列数据管理绑定信息(交换机-队列)管理队列消息管理虚拟机管理交换机路由管理队列消费者/订阅者管理 整体框架:工具模块及项目整体模块框架 交换机数据管理 交换机数据管理就是描述了交换机应该有哪些数据 定义交换机数据类 1、交换机的名…

Linux忘记root用户密码怎么重设密码

直接说步骤: 1.重启客户机 2.在选择内核页面快速按e键,进入编辑模式 进入后应该是这个样子 在这里只能按上下键切换行 找到Linux16这里 3.按右方向键切换到行尾,也就是UTF-8处,在后面添加一个空格,然后加上这段话 …

鸿蒙网络管理模块04——网络连接管理

如果你也对鸿蒙开发感兴趣,加入“Harmony自习室”吧!扫描下方名片,关注公众号,公众号更新更快,同时也有更多学习资料和技术讨论群。 1、概述 网络连接管理提供管理网络一些基础能力,包括WiFi/蜂窝/Etherne…

数据库概述(1)

课程主页:Guoliang Li Tsinghua 数据库在计算机系统中的位置 首先,数据库是在设计有大量数据存储需求的软件时必不可少可的基础。 最常见的是:我们通过app或者是浏览器来实现一些特定需求——比如转账、订车票。即引出背后的CS和BS两种网…

如何用深度神经网络预测潜在消费者

1. 模型架构 本项目采用的是DeepFM模型,其结构结合了FM(因子分解机)与深度神经网络(DNN),实现了低阶与高阶特征交互的有效建模。模型分为以下几层: 1.1 FM部分(因子分解机层&#…

Epoch、Batch与Iteration简答理解

揭秘神经网络训练的三大神秘要素:Epoch、Batch与Iteration 在探索深度学习的奇妙世界时,你是否曾被Epoch、Batch和Iteration这三个术语搞得晕头转向?别担心,今天我们就来揭开它们的神秘面纱,带你深入了解神经网络训练的奥秘! 一、Epoch:时间的轮回,数据的洗礼 Epoch…

Python | Leetcode Python题解之第454题四数相加II

题目: 题解: class Solution:def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:countAB collections.Counter(u v for u in A for v in B)ans 0for u in C:for v in D:if -u - v in countAB:ans countAB…

《深度学习》OpenCV 图像拼接 原理、参数解析、案例实现

目录 一、图像拼接 1、直接看案例 图1与图2展示: 合并完结果: 2、什么是图像拼接 3、图像拼接步骤 1)加载图像 2)特征点检测与描述 3)特征点匹配 4)图像配准 5)图像变换和拼接 6&am…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-03

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-03 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-03目录1. A Scalable Data-Driven Framework for Systematic Analysis of SEC 10-K Filings Using Large Language Models摘要研…

centos72009源码编译R语言

./dev/make-distribution.sh --name custom-spark --pip --r --tgz -Pconnect -Psparkr -Phive -Phive-thriftserver -Pmesos -Pyarn -Dhadoop.version3.4.0 -Pkubernetes spark3.5.3 源码版本 ./dev/make-distribution.sh --name custom-spark --pip --r --tgz -Pconnect -P…

有符号整型和无符号整型比较大小,整型提升{9.28下午}

有符号整型和无符号整型比较大小时,会先把有符号整型先转变成无符号整型 -1的补码是32个1,当成无符号整型来处理时,补码就是源码,所以是一个超级大的数 在C语言中,当有符号整型(如int)和无符号…

MATLAB|电气互联系统有功-无功协同优化模型

目录 1 主要内容 模型示意图 目标函数 程序亮点 2 部分程序 3 程序结果 4 下载链接 1 主要内容 本程序基本复现《“碳中和”目标下电气互联系统有功-无功协同优化模型》,文献模型提供了一个很好的创新思路,把常规电气互联系统的调度和有功无功优化…

Jmeter中有关属性的获取的问题

Jmeter中有3个方法用来获取属性值: props.getProperty(propName), ${__property(propName)} ${__P(propName)} 试验了下,在JSR223 Sampler中使用以上3个方法获取属性值的情况 1. 返回结果如下: 这里看到,在jmeter属性列表里…

QT学习笔记3.2(建立项目、执行_打包、生成执行文件exe)

QT学习笔记3.2(建立项目、执行_打包、生成执行文件exe) 目录 windeployqt打包过程中: enigma virtual box把所有文件打包成.exe文件 资料 mingw还没有编译成功过,这里说明使用msvc的过程。 使用msvc可以编译生成 1.生成执行文件、库文件…

【玩转 JS 函数式编程_006】2.2 小试牛刀:用函数式编程(FP)实现事件只触发一次

文章目录 2.2 该问题的函数式解 A functional solution to our problem1. 高阶函数解 A higher-order solution2. 高阶函数解的手动测试 Testing the solution manually3. 高阶函数解的自动测试 Testing the solution automatically4. 更好的解决方案 Producing an even better…

计算机毕业设计 基于Python的程序员薪资分析系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…

1000题-操作系统概述

特性微内核(Microkernel)宏内核(Monolithic Kernel)设计哲学精简内核,将非核心功能移至用户空间将所有核心功能集成到单一内核空间中功能集成仅包含最基本的操作系统功能(如进程间通信、内存管理基础&#…

[Meachines] [Easy] Sea WonderCMS-XSS-RCE+System Monitor 命令注入

信息收集 IP AddressOpening Ports10.10.11.28TCP:22,80 $ nmap -p- 10.10.11.28 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 e3:54:…

SkyWalking监控SQL参数

前言 SkyWalking可以记录每个请求中执行的所有SQL,但是默认情况下,SkyWalking不记录SQL参数导致使用起来不是很方便,每次都得看日志才能知道具体的参数。不过SkyWalking提供了一个配置参数,开启后,便可记录SQL执行的参…