当我们写好.c/.cpp文件时 此时文件还不能运行 因为他要经过以下的四步才可以运行
.c/.cpp(生成.i) 编译(生成.s) 汇编(生成.o) 链接(生成.exe)
1.#define宏替换 1.词法分析 指令翻译成二进制 1.合并段和符号表
2.#include 递归展开 2.语法分析 2.符号解析
3.处理#if #endif等 3.语义分析 3.分配地址空间
4.添加行号和文件表示 4.代码优化 4.符号重定位
5.删除注释
6.保留#pragma
此时的文件已经成.exe的ELF格式了 然而现在文件还是不可以运行的 因为此时文件还在磁盘上 文件要加载到内存上才可执行 于是还有这以下三步文件才可以运行
运行.exe(./main)
1.建立虚拟地址空间到物理内存的映射(创建内核映射结构体)
2.加载指令和数据到内存中
3.程序的入口地址写入pc(保存下一行指令的地址)寄存器中
虚拟地址空间布局映射到真是物理内存如下图:
此时就算虚拟空地址(本身并不存在)被恶意修改 真实物理内存的大小是固定的 也不会影响到其他进程了
注意:数据一般都放在堆和栈 堆和栈都有随机的偏移 是因为防止被恶意修改 堆栈只有在运行的过程才有
DMA是不过寄存器的 直接通过数据总线得到数据
int * p = NULL;
*P = 20;
此段代码错误 因为NULL在保留区 保留区不允许访问 而解引用则访问了保留区
.data段放的是已初始化且初始化不为0的数据
.bss段放的是未初始化或初始化为0的数据
为什么两个段保留数据?节省的是哪块空间?
.bss为了节省空间 .bss节省的是文件空间(其实.bss段在文件中不存在) 只是从section header 把.bss信息保存了下来
符号表
l:本文件 g:全局
强符号:全局已初始化的变量
弱符号:全局未初始化的变量
强弱符号的规则:
- 两个强符号重定义错误
- 一强一弱 选强符号
- 两个弱符号 选数据较大的
.bss弱符号在汇编时 看不到本文件以外存放的强符号 所以先放在common块 链接完后当所有文件都看见后 若有强符号 则用强符号地址 此时弱符号也不再common块 在合理的位置了
引入外部符号放在und符号表中找不到 .test段对于外部变量用虚假的0地址代替真实地址针对函数用-4偏移 此时汇编完成 弱符号以及外部符号要在链接中处理
Ld链接器只关心全局符号g,80%错误发生在符号解析 main函数是默认入口地址 链接完后弱符号都放入.bss段
Call:近址相对偏移位移 所调用的函数入口地址为下一行指令地址+call所对应的偏移 运行过程中不需要common块 只需指令和数据段