Day.5 深入理解连结之Object file

目标文件格式 (Object file)


以文件的方式放在磁碟(Disk)中的 Object module 有三种形式

  • 可重定位目标文件 (Relocatable Object File)
    • 这类文件包含了二进制程序码和数据,其形式可以再编一时与其他可重新店为目标文件合并起来,建立一个可执行的目标文件
  • 可执行目标文件 (Executable Object File)
    • 包含二进制程序码和数据,其形式可以直接复制到记忆体并执行
  • 共享的目标文件 (Shared Object File)
    • 一种特殊类型的可重定位目标文件,可在执行时期动态连结并载入到主记忆体,也可在载入到记忆体时期,才动态连结此档案

目标文件格式


一个程序码Source code档案被Compile後,会依照指令和资料部份分开存到Obj File上不同的Section中。

  • .text, .bss, .data 是一般编译器之预设记忆体区段名称, linker script 会安排实际的记忆体位址给各个区段.
  • .text 为唯读区段, 包含 Const string,及Arm 指令程序(程序码),安置於ROM
  • .data 为可读写区段, 放置初值不为 0 的变数 (安置於 RAM中)
    • RAM的内容会在断电、或是系统重置後消失,Reset的程序需从ROM取得data初值(Copy from ROM to RAM)
  • .bss 为可读写区段, 放置初值为 0 的变数. Reset的程序.bss 区段会被清为 0
  • stack 为堆叠区 一般是呼叫函数时的作业区 (返回位址暂存, 传递参数, 区域变数和返回值之储存区), heap 为堆积区 是呼叫 malloc() 时取得记忆区块的来源.
  • symtal 一个符号表,它存放在程序中定义和引用函数和全域变数的讯息
  • .rel.text 当Linker 把目标文件和其他文件组合时,需要修改那些 text 的位置(Relocation),可执行文件中并不需要重新为讯息,因此通常会省略
  • rel.date 被程序块引用或定义的所有全域变数的 Relocation 讯息
  • .debug 一个除错符号表 只有以 -g 选项调用编译器驱动程序时,才会有这个 section
  • .strtab 字串表
  • .dynamic 动态连结讯息

    其他Section(还有很多未被列出)

符号与符号表


连结的过程就是把多个不同的 obj file 相互"黏"在一起,为了使不同的 obj file 能够相互黏合,必须有固定的规则才行,才能避免连结的过程中不同变数和函数之间的混淆,在连结中,我们将函数很变数称为符号(Symbol),连结过程中很关键的一个部份就是符号的管理,每一个 obj file 都会有一个相对应的符号表(Symbol Tabel) ,记录着目标文件中所用到的所有符号,每个定义的符号都有一个对应值,称为Symbol Value,对於variable和functoin来说,Symbol Value指的就是他们的地址。

还存其他几个不常用到的符号 :

  • 全局符号 : 定义在目标文件的全域符号,可被其他 obj file 使用
  • 外部符号 : 你有使用,但是却没有定义在自己的obj file中 如:printf
  • 段名 : 这个符号的值就是该section的start address 如 .text .data
  • 局部符号 : 像是static variable, 这类的符号对linker来说意义不大,linker往往忽略他们。
  • 行号信息 : obj file 指令与原码的对应关系,它是可选的

我们可以使用nm指令来查看符号表


连结器如何解析多重定义的全局符号?

在编译阶段, Compiler 会将每个 symbol 分类为 strong 或 weak
Strong Symbol : 包含 procedures 和被初始化过的全域变数。
Weak Symbol : 未被初始化的全域变数。

Linker 利用以下列规则来决定如何做 Linking :

  1. 同时存在多个同名 strong symbol 是不允许的。
  2. 假设有一个 strong symbol 与多个 weak symbol , Linker 应选择 strong symbol。
  3. 如果只有多个 weak symbol 同名 ,任意选择其中一个。

Example (规则二)

/* foo3.c */
#include <stdio.h>
void f(void);

int x = 15213; // Strong Symbol

int main(){
    f();
    printf("x = %d\n", x);
    return 0;
}
/* bar3.c */
int x; // Weak Symbol

void f(){
    x = 15212
}

$ gcc -o foobar3 foo3.c bar3.c
$ ./foobar3
x = 15212

在运行时,函数 f 将 x 的值由15212改为15212,因为连结器将选择定义中的 strong symbol

参考文献

程序员的自我修养-linker
深入理解计算机系统-linker


<<:  Day12:合并排序(Merge Sort)

>>:  Day12-救世主Promise

Day 27 - 成本估计与 Amazon DynamoDB

Day 27 - 成本估计与 Amazon DynamoDB 观赏鱼辨识成本估计 根据 观赏鱼辨识系...

走骇客的路让骇客无路可走

废宅看到的几个新闻重点 上级机关视察回收厂发现圾垃车满戴、系统资讯与现况不符合,经行政程序报请南调组...

Day 6 - 原型 (5): 帖子页的元件组合

前言 利用刚设计好的帖子页元件, 组合成帖子页。 元件组合 建立一个属於帖子页的frame 先在Pa...

【红黑树十讲】总学习路径分享

Youtube连结:https://bit.ly/33cfaZS 红黑树有着自我平衡的特性,透过「...

[Day29]-使用python处理pdf档案

基础 使用时要先下载pip install PyPDF2 读取Pdf页面内容 检查Pdf是否被加密...