预编译(.i)
1.删除#define 文本替换
2.处理#include 递归展开
3.处理#if #endif 等预编译指令
4.删除注释
5.添加行号和文件标识
6.保留#pragma
编译(.s)
1.词法分析
2.语法分析 一行表达式
3.语义分析 结合上下文
4.代码优化
5.生成汇编指令 低级语言
**汇编(.o)**目标文件 可重入的二进制文件
1.翻译指令 (把低级语言翻译为机器语言)
链接(.exe) 能运行 可执行文件
数据段(.data 和 .bss)存放数据的区别
1…data 已初始化且初始化不为0的数据
2…bss 未初始化或初始化为0的数据
#include<stdio.h>
int gdata1 = 10;//全局变量生成的都是数据 数据都在数据段存放 .data段
int gdatal = 0;//.bss段
int gdata3;//.bss段
static int gdata4 = 20;//静态的全局变量也是数据 .data段
static int gdata5 = 0;//.bss段
static int gdata6;//.bss段
int main()//函数名生成的是指令 .text段
{
static int ldata = 30;//静态局部变量生成的也是数据 .data段
static int ldata2 = 0;//.bss段
static int ldata3;//.bss段
int ldata4 = 40;//普通局部变量生成的是指令 .text段
int ldata5 = 0;//.text段
int ldata6;//.text段
return 0;
}
问题1:汇编过程 int ldata = 20 是在text段,而不在stack区?
运行前 所有东西都在文件中
运行后 程序才在内存中 栈是内存区域
问题2:汇编后,文件中存在什么内容?
1.汇编阶段 bss少了4个字节 1个数据 少了那个数据 为什么?
gdata3在comment段
gdata3在comment段 因为预编译、编译、汇编阶段都是对源文件进行处理
这里涉及到强弱符号的问题。而强弱符号是在链接阶段处理的
静态的全局变量和静态的局部变量没有强弱符号一说,因为静态文件仅本文件可见
举例:
因为在汇编阶段,编译器对test.c的源文件进行处理时是看不见main.c的源文件的
所以在对test.c的源文件处理时,虽然int a是弱符号,但是编译器不能确定其他源文件中是否存在强符号,所以编译器没有把该弱符号放进.bss段,而是暂时存放在comment段,若当汇编对所有源文件处理完后,进行链接的时候,没有发现其他强符号,在将该弱符号从comment段取出。相反,如果其他源文件存在强符号,那么该弱符号就依旧存在comment段
2.bss段不存在?为什么?信息从哪里来?REL布局
.o的布局,数据和指令存放的位置。指令数据生成的符号存放位置和属性
文件 虚拟地址空间映射 方便映射
文件标识 main.c
段信息 .text .data .bss
数据 gdata1 gdata3
函数 main()
//main.c
#include<stdio.h>
short a = 10;//强符号
short b = 20;
int main()
{
func();
printf("a:%d\n",a);
printf("b:%d\n",b);
return 0;
}
//test.c
int a;//弱符号
void func()
{
a = 30;
}
强弱符号(是在链接阶段处理的)
强符号
已初始化的符号
弱符号
未初始化的符号
强弱符号的规则
1.两强符号 报错
2.一强一弱 选强符号做最终处理
3.两弱符号 谁先编译就选谁 或 编译器报错
注:静态的全局变量和静态的局部变量没有强弱符号一说,因为静态文件仅本文件可见
#include<stdio.h>
int gdata1 = 10;//全局变量生成的都是数据 数据都在数据段存放 .data段
int gdatal = 0;//.bss段
int gdata3;//.bss段
static int gdata4 = 20;//静态的全局变量也是数据 .data段
static int gdata5 = 0;//.bss段
static int gdata6;//.bss段
char* p = "hello";//error
/*
char* 4个字节 hello+\0 六个字节
*/
int main()//函数名生成的是指令 .text段
{
static int ldata = 30;//静态局部变量生成的也是数据 .data段
static int ldata2 = 0;//.bss段
static int ldata3;//.bss段
int ldata4 = 40;//普通局部变量生成的是指令 .text段
int ldata5 = 0;//.text段
int ldata6;//.text段
return 0;
}
问题:hello存放的位置在哪里?
在.rodata read-only data 只读数据段
常量的字符串都在只读数据段存储
以下是链接阶段
汇编阶段那些事情没做处理
1.强弱符号处理
2.外部符号 符号表
3.虚假地址 虚假偏移 指令段
//main.c
#include<stdio.h>
int gdata1 = 10;//全局变量生成的都是数据 数据都在数据段存放 .data段
int gdatal = 0;//.bss段
int gdata3;//.bss段
static int gdata4 = 20;//静态的全局变量也是数据 .data段
static int gdata5 = 0;//.bss段
static int gdata6;//.bss段
extern int gdata;//申明有个外部的全局变量
extern int Sum(int,int);//申明有个外部的函数
int main()//函数名生成的是指令 .text段
{
static int ldata = 30;//静态局部变量生成的也是数据 .data段
static int ldata2 = 0;//.bss段
static int ldata3;//.bss段
int ldata4 = 40;//普通局部变量生成的是指令 .text段
int ldata5 = gdata;
int ldata6 = Sum(ldata4,ldata5);
return 0;
}
//test.c
int gdata = 1000;
int Sum(int a,int b)
{
return a + b ;
}
问题:为什么不把gdata放在.data段,为什么不把Sum放在.text段,而是把它们放在UND(未定义区)?
extern int gdata;//申明有个外部的全局变量
extern int Sum(int,int);//申明有个外部的函数
.o下不知道gdata和Sum的定义点,所以只能将gdata和Sum暂时放在放在未定义区
问题:根据第11行,a1后面的应该是gdata的地址,gdata能在0x0000 0000开辟空间存放吗?为什么用虚假的地址代替gdata真实的地址呢?
gdata不能再0x0000 0000,因为最终进行映射的时候是在保留区的,所以a1后面的地址是gdata的虚假地址
现在还没有链接,我们看的是目标文件的东西,看的是指令段,针对gdata来说,是外部符号,我们不知道他的定义点在哪里,所以无法得知gdata的真实地址,所以只能用一个虚假的地址做一个替换
call指令:函数调用指令 近址相对位移 调用指令
近址:下一行指令得地址
问题:调用Sum函数 得知道Sum函数入口地址,那么如何得知函数入口地址?
近址 + 相对位移 = 函数的入口地址
ff ff ff fc = -4
2E + ff ff ff fc = 2A
而0xFF FF FF FC是内核地址,只有操作系统可以操纵,所以这也是虚假的地址。因为Sum()的定义点看不到,所以用一个虚假的地址去代替真实的地址
汇编阶段没有处理的:**
1.强弱符号
2.符号表 外部符号处理
3.指令段 虚假地址和虚假偏移
链接阶段处理的:
1.合并段和符号表 强弱符号
2.符号解析
3.分配地址和空间 程序和虚拟地址空间的映射
4.符号的重定位
处理UND区域符号
通过声明找到定义的位置
符号解析
在符号引用的地方找到符号定义的地方
进程
运行
1.建立虚拟地址空间和物理内存的映射(创建内核映射结构体),创建页目录页表
2.加载指令和数据
3.把入口地址放在下一行指令寄存器