程序的静态链接——链接和目标文件格式


今天,手机坏了,这部分的课后题忘交了,没心情学习怎么办,那就听《Artist》吧,小鬼在加油,鬼姐姐也得加油!(上周用这首歌荣获英语展示小组第一)

这部分内容会利用gdb调试展示内部表示,所有的linux命令行在另一篇博客中展示

(由于我也是新手,写的东西仅仅是笔记,大家还是参考网上更权威的教程进行学习)
首先我们提到了链接这个词,在程序开发中,我们往往会将多个源文件和标准库函数链接成一个可执行文件,这种可执行文件是一个统一的整体,可以被加载到存储器中执行,链接器是将多个可重定位目标文件合成一个可执行文件的工具。

可执行文件生成概述

这部分内容主要讲述如何从.c文件转化为可执行目标文件,以及链接器的主要功能

可执行文件的生成

对于hello.c文件:
在这里插入图片描述
前三种文件都是文本语言,都是用ASCII码书写的文件,机器不能识别。
😈 预处理命令
$gcc -E hello.c-o hello.i
$cpp hello.c >hello.i(cpp是预处理命令)

  • 删除“#define"并展开定义的宏
    简单介绍宏定义命令:类似于c++中const,用标识符代替常量或语句,预处理时将有相应标识符的地方进行替换。
#define N 100;
int t=N;//N会被直接换成100
  • 处理所有条件预编译指令
    “#if” “#ifdef” “#endif”
    相当于if语句
  • 插入头文件到”#include"处,采用递归方式处理
  • 删除所有注释
  • 添加行号和文件名标识,便于编译产生调试的行号信息
  • 保留编译指令#pragma:设定编译器的状态或者是指示编译器完成一些特定的动作
    此时的文件依然是可读的文本文件,但不包含任何宏定义

😈 编译
通过语法分析,词法分析,优化,生成汇编代码,这个过程往往需要多次对文件进行扫描
$gcc -S hello.i-o hello.s
也可以直接用一个步骤
$gcc -S hello.c-o hello.s
cc1命令
😈 汇编
汇编语言源程序->二进制代码(机器语言序列)(生成可重定位目标文件,机器不能识别)
区分机器代码和机器级代码
$gcc -c hello.s-o hello.o
$as hello.s-o hello.o
每个文件都要经历上述过程
😈 链接
多个可重定位合并生成可执行目标文件
$gcc -static -o myproc main.o test.o
$ld -static -o myproc main.o test.o
静态链接static,如果不指定-o选项,可执行文件名为a.out

链接

链接发展史

链接的概念从机器语言一开始就存在
链接的关键是找出各个数据或变量之间的关系
😈 很久很久以前的机器代码时期
跳转时的地址直接用二进制代码表示,如果增加指令,就需要修改很多指令的地址,纸带就得重做,真麻烦!
😈后来啊,出现了汇编语言
用符号表示转移目标,另一个地方引用这个标号,就能建立标号之间的关系
链接也就是找到各个标号的关系,再将对应的值填入具体的位置。
😈 高级!程序语言
符号定义:子程序和变量的起始地址都使用符号表示,确定定义和引用关系,然后将定义填入引入。
模块中的定义可以被另一个模块引用
符号定义和符号引用

链接操作的步骤

静态链接器可以将多个可重定位目标文件合成一个可执行文件

😈 符号解析:将符号定义和符号引用建立关联
这里的符号指的是全局静态变量和函数名,非静态局部变量不需要考虑与其他重定位文件的关系,局部变量不会被函数外被引用,不属于符号
编译器能将所有符号放在可重定位目标文件的符号表中
以下为重定位操作:
😈 合并代码(可执行文件中每种类型的数据都有自己该去的代码段)
😈 确定地址
😈 将定义处的地址填入引用的地方:这是虚拟地址空间,代码区和数据区的地址都是从0开始的

链接的好处

😈 模块化编程
😈 效率高
在可执行文件运行对应的内存中不需要包含整个共享库,提高空间利用率。

深入讨论链接

链接的本质:合并相同的节
每个可重定位目标文件中的数据和代码都有自己的分类,链接就是将相应的部分合成一个新的节,然后分配地址
在这里插入图片描述
程序头表:描述在磁盘上的内容如何映射到内存中,会映射到一个虚拟地址空间(地址实际上是虚拟地址)
虚拟地址中有读写数据段和只读代码段,就是由磁盘中的可执行文件相应的节组成的
在这里插入图片描述

目标文件格式

  • 目标代码是机器语言代码,目标文件是存放目标代码的文件
  • 可执行文件存储映像
    在这里插入图片描述
    开始链接生成文件在磁盘中。
    段头表描述如何映射,如何将磁盘中内容映射到虚拟地址空间中。
    先映射到虚拟地址空间

三类目标文件

😈 可重定位目标文件(链接对象)
代码和数据地址都是从0开始
😈 可执行目标文件
Linux:默认为a.out window默认为.exe
代码和数据被复制到内存中执行
对应地址是虚拟地址空间中的地址
😈 共享目标文件:可以动态转入内存,自动链接到可执行文件

ELF目标文件格式概述

链接视图:(可重定位目标文件)
节是ELF文件中具有相同特征的最小可处理单位
在这里插入图片描述
执行视图(可执行目标文件)
在这里插入图片描述

ELF可重定位目标文件格式

主要结构 ELF头+代码部分+数据部分+节头表
😈 节头表:包含文件中各节的说明信息节的节名,偏移和大小
定义节头表所用的是struct类型,可以根据单个struct类型字节数和定义的节数确定节头表所占字节数
节头表不一定在磁盘最后,根据ELF头中的数据判断节头表的首地址
在这里插入图片描述
跳转顺序:elf头→节头表→各种节
😈 ELF头:

  • 位于目标文件的起始位置,包含文件结构说明信息,分为32位对应结构和64位对应结构。
  • 占52个字节在这里插入图片描述
  • 文件开始的几个字节为魔数,确定文件类型和格式,加载文件时通过魔数判断文件类型是否正确
    在ELF格式下,最开始有个16字节序列,最开始的4字节为魔数

Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00

  • e_type表明了文件的类型(可重定位?还是可执行)
  • 定义了程序头表和节头表的位置和长度

😈 .text
😈 rodata:跳转表
😈 .data:已初始化的全局变量,有具体的值,需要磁盘空间
😈 .bss:未初始化的全局变量和静态局部变量,默认值为0,仅仅是一个占位符,会在内存中分配空间,但是磁盘中不会有空间,节头表中说明需要预留的空间
😈其他节

  • symtab:符号表节:函数名和全局变量信息,不包括非静态局部变量
  • rel.text节:用于修改text的地址值
  • rel-data节:修改data的地址值

ELF可执行目标文件格式

在这里插入图片描述

  • 在ELF头中给出执行程序第一条指令的地址,在可重定位中该值为0
  • 程序头表(段头表):结构数组
    将节分段,描述节和段的关系
    说明段的信息
  • .init:定义init函数,对执行目标文件进行初始化
  • 没有.rel节:无需重定位
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章