首页 > 其他分享 >可执行文件的生成

可执行文件的生成

时间:2024-03-21 19:35:56浏览次数:18  
标签:可执行文件 文件 符号 生成 地址 目标 定位 链接

1. 可执行文件的生成

源代码到可执行文件的生成可分为预处理(Prepressing)编译(Compilation)汇编(Assembly)链接(Linking),四个步骤。

1.1 预处理

以 C 语言为例,预处理主要是处理源代码中以“#”开头的那些预处理指令,规则如下:

  • 将所有 “#define” 删除并展开宏定义

  • 处理所有条件预编译指令比如 “#if”、“#ifdef”、“#elif”、“#else”、“#endif”;

  • 处理 “#include” 预编译指令,将被包含的文件插入到预编译位置。递归进行,被包含文件可能还包含其他文件;

  • 删除所有注释;

  • 添加行号与文件名标识,以便编译器产生调试信息;

  • 保留 #pragma 编译器指令,因为编译器必须使用它们;

1.2 编译

编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后产生相应的汇编文件。

命令:gcc -S hello.c -o hello.s

1.2.1 词法分析

首先源代码进入扫描器(Scanner),扫描器利用有限状态机(Finite State Machine)算法将源代码的字符序列分割成一系列符号(Token)

词法分析产生的符号分为以下几类:关键字、标识符、字面量(包含数字、字符串等)和特殊符号(如加号、等号)。

与此同时,扫描器也完成了将标识符放入符号表,将数字、字符串常量放入文字表等工作,以备后续步骤使用。

1.2.2 语法分析

语法分析器(Grammar Parser)对扫描器生成的符号进行语法分析,整个过程采取上下文无关语法(Context-free Grammar)分析法,生成语法树,就是以表达式(Expression)为结点的树。

1.2.3 语义分析

接下来就是语义分析器(Semantic Analyzer)对表达式进行语义层面的分析。

编译器所能分析的语义叫做静态语义(Static Semantic),也就是在编译期可以确定的语义,与之对应的叫动态语义(Dynamic Semantic),要到运行期才能确定的语义。

静态语义包括声明和类型的匹配,类型的转换。比如浮点型表达式赋值给整型表达式,这里就隐含了类型转换工作。

动态语义一般是运行期出现的相关问题,比如将 0 作为除数就是一个运行期语义错误。

1.2.4 目标代码生成与优化

代码生成器(Code Generator)将中间代码转化为目标机器代码。 然后目标代码优化器(Target Code Generator)对目标代码进行优化,比如选择合适的寻址方式。

1.3 汇编

汇编是汇编器将汇编代码转变成机器可执行的指令的过程。汇编指令与机器指令几乎一一对应,所以汇编器直接翻译就可以了。

命令:gcc -c hello.s -o hello.o

1.4 链接的过程

为什么汇编器不直接输出可执行文件而是输出一个目标文件?

重定位:程序写好并不是一成不变的。这种重新计算各个目标的地址过程叫做重定位。

在一个程序被分割成多个模块以后,模块之间的组合问题可以归结为模块之间如何通信。比如C++模块间的函数调用,模块间的变量访问。函数访问必须知道目标函数的地址,这两者都可归结为模块符号间的引用。

而链接就是把各个模块之间的相互引用处理好,使得各个模块之间能正确衔接。

链接过程到底包含了什么内容?

链接的主要过程包括地址与空间分配(Address and Storage Allocation)符号决议(Symbol Resolution)重定位(Relocation)等步骤。

最基本的静态链接过程,每个模块的源代码文件经过编译器的编译,形成目标文件(Object File,.o 或 .obj),目标文件和库(Library)一起链接形成最终的可执行文件

比如我们在程序模块main.c中使用另一模块func.c中的函数foo(),我们在main.c模块每一处调用foo的时候都必须确切知道foo这个函数的地址,但是由于每个模块都是独立编译的,在编译main.c的时候并不知道foo函数的地址,所以暂时把这些调用foo的指令的目标地址搁置,等待最后链接的时候由连接器去将这些指令的目标地址修正。

对其他定义在目标文件的变量来说,也存在同样的问题。比如目标文件 A 中一个变量 Foo在链接了目标文件 B 之后才能确定地址。确定后,链接器就要对这个地址进行修正,这个修正的过程就叫重定位(Relocation),每个要被修正的地方叫一个重定位入口(Relocation Entry)

什么是目标文件?

编译器编译源代码后生成的文件叫做目标文件,目标文件从结构上讲,是已经编译后的可执行文件格式,只是还没经过链接的过程。

目标文件的格式:

现在PC平台流行的可执行文件格式主要是windows下的PE、和linux下的ELF,ELF文件标准把系统中采用ELF格式的文件归为4类:

  • 可重定位文件:Linux下的.o

  • 可执行文件

  • 共享目标文件

  • 核心转储文件

Linux下可以使用命令file来查看相应文件的格式;

  • 文件头 ELF Header

    • ELF文件是大端还是小端;

    • ELF文件版本号;

    • 魔数

  • 段表

    • 段表描述了ELF文件各个段的信息。(比如每个段的段名、长度,在文件中的偏移、读写权限及段的其他属性)

  • 重定位表

    • 链接器在处理可重定位文件时,需要对其中某些部位进行重定位,即代码段和数据段中那些对绝对位置引用的地方。

    • 这些重定位信息都记录在重定位表中。(如.rel.text 是针对 .text段的重定位表)。

  • 符号表

    • 在链接中,可重定向文件之间的相互拼合实际上是对地址的引用,即对函数和变量的地址的引用。我们将函数和变量统称为符号(Symbol),函数名和变量名就是符号名。

2. 弱符号与强符号

C++语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号

针对强弱符号,编译器有以下处理规则:

  • 不允许强符号被多次定义;

  • 如果一个符号在某个目标文件中是强符号,在其他文件都是弱符号,那么选择强符号

  • 如果一个符号在所有目标文件都是弱符号,那么选择其中占用空间最大的一个;

3. 静态链接过程

整个链接过程分两步:

  • 第一步 空间与地址 这一步扫描所有输入文件,获取各个段的长度、属性和位置,并将所有符号表中的定义与引用统一放到全局符号表。

  • 第二步 符号解析与重定位 使用第一步收集到的信息进行符号解析与重定位、调整代码中的地址。这一步是链接过程的核心,特别是重定位。

    • 符号解析与重定位。

      链接器根据符号地址对每个需要重定位的指令进行地址修正。

      当源代码a.c在被编译成目标文件时,编译器并不知道"shared""swap"的地址,因为它们定义在其他目标文件中。所以编译器就暂时把地址0看作是"shared"的地址。把真正的地址计算工作留给了链接 器,我们通过前面的空间与地址分配可以得知,链接器在完成地址与空间分配之后就可以确定所有符号的虚拟地址了。那么链接器就可以根据符号的虚拟地址对每个需要重定位的指令修正。

那么链接器如何知道哪些指令是要调整的呢?

  • 重定位表

   重定位表(Relocation Table)是专门保存有关重定位信息的结构。重定位表也叫重定位段,比如代码段.text(数据段.data)里有要被重定位的地方,那么就会有一个相对应叫.rel.text(.rel.data)的段保存了代码段/数据段的重定位表。

   每个要被重定位的地方叫一个重定位入口,重定位入口的偏移表示该入口在要被重定位的段中的位置。

  • 符号解析

      重定位的过程中伴随着符号解析,每个目标文件都可能定义一些符号,也可能引用到定义在其他目标文件的符号。当链接器扫描完所有输入目标文件后,所有未定义的符号都应该能够在全局符号表中找到,否则就会报符号未定义错误。

4. 程序装载

  • 静态装入

程序执行时所需要的指令和数据必须全在内存中才能正常运行,最简单的办法就是将程序原型所需要的指令和数据全都装入内存中,这就是最简单的静态装入的办法。

  • 动态装入

程序运行时是有局部性原理的,所以我们可以将程序最常用的部分驻留在内存中,而将一些不太常用的数据存放在磁盘里面,这就是动态装入的基本原理。

5. 延迟绑定(PLT)

ELF 采取一种延迟绑定(Lazy Binding)的做法,基本思想就是当函数第一次被用到时候再进行绑定(符号查找,重定位等),没有用到则不进行绑定。

标签:可执行文件,文件,符号,生成,地址,目标,定位,链接
From: https://www.cnblogs.com/love-9/p/18088095

相关文章

  • 802.1d STP(生成树协议)
    802.1dSTP(生成树协议,Spanning-TreeProtocol)采用生成树技术,能够在网络中存在二层环路时,通过逻辑阻塞(Block)特定端口,从而打破环路,并且在网络出现拓补变更时及时收敛,保障网络冗余性。 STP生成树协议:802.1d(慢,拓补收敛需要30-50s)RSTP快速生成树协议:802.1w(快,6s内完成收敛)MS......
  • vue3使用qrcodejs2-fix生成背景透明的二维码
    qrcodejs官方仓库:GitHub-davidshimjs/qrcodejs:Cross-browserQRCodegeneratorforjavascriptqrcodejs2-fix 是一个用于生成QR码的JavaScript库,使用的时候先安装,然后通过设置前景色和背景色可以控制显示的二维码效果。想生成透明背景的二维码也可以,我通过下面配置前景......
  • AI新工具(20240321) 又一个开源的Sora实现;高质量动漫风格图像的文本到图像模型;字节跳
    ✨1:Mora利用多智能体合作生成视频任务的多智能体框架Mora是一种多智能体框架,专为通用视频生成任务设计。它通过多个视觉智能体的协作,实现了在多种视频生成任务中的高质量输出,旨在复制并扩展OpenAISora的能力。以下是通俗语言总结的Mora功能以及可能的使用情景......
  • 重新生成新的 VMCA 根证书并替换所有证书
    前提条件在使用此选项运行vSphereCertificateManager时,您必须了解以下信息。[email protected]的密码。要为其生成新的VMCA签名证书的计算机的FQDN。所有其他属性默认设置为预定义的值,但可以更改。过程登录到 vCenterServer,然后启动vSphereCe......
  • C# 窗体 隐藏到托盘 开机自动启动 粘贴板 生成的EXE图标和名称
    1、隐藏到托盘privatevoidMainForm_FormClosing(objectsender,FormClosingEventArgse){if(e.CloseReason==CloseReason.UserClosing){//取消关闭操作,并隐藏窗体e.Cancel=true;......
  • 首发!谷歌倾斜摄影数据转换生成OSGB格式
    一、3DTiles转换工具开发背景距离上一篇文章更新已有46天,这期间(包括过年)一直在忙着一件事情,就是将谷歌倾斜摄影转换成OSGB数据。随着Cesium的广泛使用,对3DTiles数据的使用需求越来越强烈,虽然Cesiumion有免费使用的在线3dtiles数据,但是国内Cesium的使用场景大部分都是在......
  • 更智能的广告素材生成!看A/B测试如何驱动AIGC素材调优
    更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群前言:AIGC大爆发,引发广告营销行业变革ChatGPT等AI产品引发的AIGC大爆发引起了各行业的震动,其中以图片生成甚至视频生成技术的效果和速度最为令人震撼。也正因如此,AIGC的爆发对一直以创意为核......
  • 深度解读UUID:结构、原理以及生成机制
    What是UUIDUUID(UniversallyUniqueIDentifier)通用唯一识别码,也称为GUID(GloballyUniqueIDentifier)全球唯一标识符。UUID是一个长度为128位的标志符,能够在时间和空间上确保其唯一性。UUID最初应用于Apollo网络计算系统,随后在OpenSoftwareFoundation(OSF)的分布式......
  • xls生成对比
    #-*-coding:utf-8-*-importopenpyxl,psycopg2fromopenpyxl.stylesimportFont,PatternFill,Border,Sideclassxls:defget_data1(self):conn=psycopg2.connect(host="192.168.137.129",port="5432",database="postgres&qu......
  • Open Sora 发布!开源的高效复现类 Sora 视频生成方案
         不久前OpenAISora的发布可以说是震惊了世界,但是奈何目前OpenAI还未将Sora开放公测,但在昨天,我们却等来了OpenSora1.0的发布,这是Colossal-AI团队的一个完全开源的视频生成项目,致力于高效制作高质量视频,并使所有人都能使用其模型、工具和内容的计划。通......