- 编译链接
.c/.cpp
预编译(生产.i文件) 编译(生产 .s文件,即汇编文件)
操作指令:gcc –E main.c –o main.i 操作指令:gcc –c main.s –o main.o
删除#define文本替换 词法分析
处理所有条件预编译指令,如#if #endif #elif 语法分析
递归展开#include 语义分析
删除注释 代码优化
添加行号和文件标识 生成汇编指令
保留所有的#pragma编译器指令
进程
汇编(生产.o文件,二进制可重定位文件) 链接.exe(生产可执行程序)
操作指令:gcc –c main.s –o main.o 操作指令:gcc –o main main.s
翻译指令(将汇编代码转变成机器可以执行的指令) 合并段和符号表
符号解析
分配地址和空间
符号重定位
- 所有的数据都会生产符号,指令只有函数名生产符号
- 程序运行在:(32位系统 2^32 4G虚拟地址空间)
4G虚拟地址空间:
- 虚拟地址空间
任何进程都会产生它们的虚拟地址空间,一下是LINUX32位操作系统产生的虚拟地址空间图
- 虚拟地址空间分为两大块,一个是用户空间,一个是内核空间。用户空间占3G的大小,并且它是每个进程独有的,它的开头128M存放的是我们无法访问的地方。
- .text段:它也叫指令段,顾名思义,它存放的是指令代码,在程序中,我们把局部变量定义(局部变量的定义是指令而不是数据)还有一些操作指令都存放在.text中。它的属性是可执行可读不可写。
- .data段:数据段,里面存放的是初始化不为0 的静态局部变量和全局变量。属性是可读可写不可执行。
- .bss段:数据段,里面存放的是未初始化或者初始化为0的全局变量和静态局部变量。属性是可读可写不可执行,在bss段中的数据默认都会被修改为0.
- 堆:堆内存是我们在c语言中用malloc申请或者在c++中用new来申请的一端可能不连续的内存,堆是由低地址向高地址申请的。
- 栈:栈中存放我们的局部变量,特性是先进后出,并且它是由系统自动开辟以及释放。并且内存是连续的。
- 命令行参数:main函数的参数列表。Argc参数个数 argv参数内容 envp环境变量
- 环境变量:即envp的值
- 最下边是内核空间
- ELF文件
1、ELF文件:在计算机科学中,是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。ELF由4部分组成,分别是ELF头(ELF header)、程序表头(Program header table)、节(Section)和节头表(Section header table)。
在elf文件结构中,有一个字符串表.strtab,里面存放的是elf文件中各个段的名字以及变量名等字符串,字符串表中记录了这些字符串以及对应的下标、需要用到这些字符串时,直接用偏移下标去取就行了。段表中存放的段的名字这一项,就是存的.strtab中对应字符串的偏移。
2、符号:所有数据都会生产符号,指令只有函数名会生产符号。
强符号:初始化了的非静态数据
弱符号:没有初始化的非静态数据
- 链接
- 链接有以下几个部分要做:
- 合并段表:将多个二进制可重定位文件中的段信息合放到一个文件中
- 调整段偏移:段表经过合并之后大小发生了变化,所以地址需要适当的偏移
- 合并符号表:将多个二进制可重定位文件中的符号整合到一个文件中
- 完成符号的重定位:连接器把每个符号定义与一个虚拟地址联系起来,然后修改所有对这些符号的引用,使得他们指向这个存储位置,从而重定位这些节。
- 按照属性划分不同的段放在同一页,运行时按照LOAD页信息将段按照页读入到虚拟地址空间。