编译链接总结

本文是读<程序员的自我修养>的笔记和总结

编译过程

程序的编译可以分解为四个步骤, 预处理->编译->汇编->链接
下面以main.c文件为例分析:

预编译

main.c -> main.i

  • 展开宏定义, 替换#define
  • 处理预编译指令, #if, #endif, #ifdef等
  • 递归处理#include, 将引入的文件插入到对应位置
  • 删除注释
  • 添加行号, 文件名标识
  • 保留#pragma指令

编译

main.i -> main.s
词法分析: 源代码经过扫描器的处理, 运用类似有限状态机的算法将代码的字符分割成不同的记号
语法分析: 根据不同的记号, 构建语法树
语义分析: 分析语法树的静态语义是否正确, 主要是类型匹配和转换
中间语言生成:将语法树转换成中间代码, 优化一些中间结果. 中间代码使得编译器分为前端和后端, 前端产生与机器无关的中间代码, 后端将中间代码转换成目标机器代码

汇编

main.s -> main.o
根据汇编指令和机器指令的翻译一一对应

链接

修正指令对符号地址的引用
主要包括:地址和空间分配, 符号决议, 重定位

目标文件分析

目标文件是指编译后未进行链接的中间文件, 格式和可执行文件几乎一样, 在Windows称为PE-COFF, linux下称为ELF

目标文件按照节(section)或者段(segment)形式存储, 目标文件的开头是一个文件头, 描述了整个文件的属性, 静态链接还是动态链接, 入口地址等, 还包含一个段表, 段表用来描述各个段的属性和地址偏移.
常见的段名有:

  • .text(代码段)
    程序指令编译后放在代码段
  • .data(数据段)
    保存初始化了的全局静态变量和局部静态变量, 有些编译器也会将const变量保存在.rodata段里
  • .bbs(为初始化数据段)
    存放未初始化的全局变量和局部静态变量
  • 其他段
    .plt存放动态链接的跳转表, .got存放全局入口表, .dynamic存放动态链接信息, .comment存放编译器版本信息等
    除此之外, 也可以自定义段名, 但不能使用"."为前缀, 使用__ attribute__(section("customName"))可自定义段名.

注意:
源代码编译后会把程序指令和程序数据分成两个段, 即.text和.data, 这样做的优点是:
1.数据和指令分别映射到两个虚拟内存区域, 读写权限就可以分开, 防止指令被意外改写
2.提高程序的局部性, 有利于CPU缓存命中率的提高
3.对于只读数据和只读指令, 可以进程共享资源, 节省空间

.bbs段记录了未初始化的变量, 只是预留了位置, 没有内容, 因此在文件中不占据空间, 但是在链接器装载后的虚拟地址中是要分配虚拟地址空间的. 其中区别在于, 未初始化的变量不会增加可执行文件的大小, 但是会增加程序运行时的空间.

符号

函数和变量称为符号, 函数名和变量名就是符号名.
链接器的过程就是把多个不同的目标文件拼装在一起, 如果目标文件之间有引用关系, 那么就需要解析外部符号, 链接器主要通过符号表和重定位表实现.

  • 符号表
    记录了目标文件中用到的所有符号, 对应每个符号的地址
  • 重定位表
    在引用外部文件的符号时, 目标文件会先预置为undefined, 之后重定位. 重定位信息都记录在重定位表里.

静态链接

多个源代码文件编译后生成多个.o目标文件, 静态链接就是将多个目标文件链接在一起最终形成一个可执行文件.

相似段合并

将所有输入的目标文件的相同段合并在一起, 比如多个.text段合并为一个大的.text段. 链接器会扫描所有的输入文件, 读取段首信息, 将目标文件中的所有符号的定义和引用收集到一个全局符号表里. 简单来说, 链接器合并了多个目标文件, 并建立了全局符号映射关系

符号解析和重定位

链接之前, 目标文件引用的外部符号都标记为undefined, 链接时, 通过全局符号表替换所有undefined符号. 除了符号的重定位, 还有虚拟地址VMA(Virtual Memory Address)的分配, 即链接后, 各个段分配了虚拟地址. 各个段内的符号根据偏移量计算符号地址

注意:
虚拟地址并不是从0地址开始分配, 不同操作系统有不同的分配规则, 如Linux下默认分配的起始地址是0x08048000

Common块

Common块是编译器用来处理弱符号的
强符号与弱符号
编译器默认函数和初始化了的全局变量是强符号, 未初始化的全局变量为弱符号, 也可以使用__ attribute__(weak)定义一个强符号为弱符号, 编译器对强弱符号有以下规则:

  • 强符号不允许被定义多次
  • 一个符号在某个文件中为强符号, 其他文件只能为弱符号, 编译器最终选择强符号的类型
  • 一个符号在多个文件中只有弱符号, 没有强符号, 编译器会选择占用空间最大的弱符号, 比如double和int, 选择double

注意: 强符号和弱符号的并集并不是全部的符号, 很多符号既不是强符号, 也不是弱符号

直接导致需要Common机制的原因是编译器和链接器允许不同类型的弱符号存在, 但最本质的原因还是链接器不支持符号类型, 即链接器无法判断各个符号的类型是否一致

链接过程控制

链接过程可以使用ld链接脚本控制, 自定义链接过程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352