编译链接运行原理
0x12345678 ,1的权重高
1.如何将源文件变成一个进程
.c/.cpp—>进程
由于操作系统只能识别机器码(0 1),因此将源文件变成进程,需要五个步骤:
1.预编译:(.cpp—>.i)
1.删除#define,然后进行文本替换
//不建议使用宏定义,不安全,宏处理就是简单的问题替换 没有类型检查和安全检查
//因此在C++中尽量不要用宏定义
#define MUT(a,b) a*b
int main()
{
int rt = MUT(5+5,5+5); //替换会变成5+5*5+5 --》答案会变成35
printf(“rt:%d\n”,rt);
return 0;
}
2.处理以#include的头文件,以递归的形式展开(因为头文件中可能还会包含头文件)
3.处理#if #endif等预编译指令
4.删除注释
5.添加行号和文件标识(便于定位错误的位置)
6.保留#pragma ,留给编译器处理
2.编译:(.i----->.s)
.i是高级语言
词法命名规则:字母数字下划线,数字不能做首字母
1.词法分析
2.语法分析:一行表达式是否正确
3.语义分析:结合上下文,分析是否正确
4.代码优化(可以提高运行效率)
5.生成汇编指令
.s 汇编语言:低级语言
//编译过程生成了汇编语言
int main()
{
int abc=10;
//mov dword ptr[abc],0ah //16进制的10 dword=double word (一个字两个字节dword占四个字节)
//意思就是将10放进abc所代表的内存块中,占4个字节
return 0;
}
操作系统也看不懂低级语言
3.汇编:(.s---->.o)
翻译指令:把低级语言翻译成机器语言,生成了.o目标文件/可重入的二进制文件
.o文件仍然不能运行
3.1 变量存放在哪里
数据段分为:
.data--->已初始化且初始化不为0的数据
.bss---->未初始化或初始化为0的数据
全局变量、静态全局变量、静态局部变量都是数据。
局部变量是指令
int gdata1=10;//.data
int gdata2=0;//.bss
int gdata3;//.bss
static int gdata4=20;//.data
static int gdata5=0;//.bss
static int gdata6;//.bss
char* p="hello";
//字符串包含六个字符,指针大小为4个字节,所以指针中存放的是字符串的地址
//而字符串本身存放在.rodata--->read-only data只读数据段
int main() //函数名生成的是指令 .text段
{
static int ldata1=30; //.data
static int ldata2=0;//.bss
static int ldata3;//.bss
int ldata4=40;//.text
int ldata5=0;//.text
int ldata6;//.text
return 0;
}
关于之前说过的局部变量存储在栈区,为什么现在存储在.text指令段?
答:是两个不同的过程。
在指令段:汇编过程,在运行之前,所有东西都在文件中(文件中是没有栈区和堆区)
在栈区:运行后,程序才在内存中,栈是内存区域,运行指令,才会在栈上给局部变量开辟内存区域。
3.2汇编后生成的.o文件中存在什么内容?(提及了强弱符号)
汇编后生成的.o文件中存在什么内容?
可重入的二进制文件:
ELF格式文件
.text指令段–.data数据段–.bss数据段–.comment注释信息段—.nte.GUN-stack堆栈提示段
File off --起始偏移量 Algn–字节偏移
问题:
-
- 汇编阶段 bss少了4个字节,一个数据,少了哪个数据?为什么?
答:少了gdata3,在comment块
gdata3是一个弱符号,不能直接确定地址,因此不能存在bss中,当链接完成后,如果没有gdata3强符号,则会将gdata3放入bss 段
为什么gdata6和ldata3不放在comment块?
答:强弱符号不确定,是因为存在两个文件中,而静态变量的特点是,只能在本文件中可见。
只有c语言有强弱符号,在c++中,程序会报错
- 汇编阶段 bss少了4个字节,一个数据,少了哪个数据?为什么?
//原因与C语言中的强弱符号有关
强符号:全局的已初始化的符号
弱符号:全局的未初始化的符号
规则:
1.两强符号--》报错
2.一强一弱--》选强
3.两弱------》编译器决定
预编译、编译、汇编三个过程是独立的,一个完成另一个才开始,处理的是同一个源文件
编译main.c的时候是看不到test.c文件的,编译test.c是看不到main.c
//main.c
#include<stdio.h>
short a=10; //强符号
short b=20;
int main()
{
func();
printf("a:%d\n",a);
printf("b:%d\n",b);
}
//test.c
int a;
//弱符号,编译器不能确定其他的源文件中是否存在强符号,因此不能放在bss段,只能先放在comment块
void func()
{
a=30;
}
//编译完成后生成了汇编指令:mov dword ptr[a] 1E
//运行时,a,b分配的内存空间,a和b均只有两个字节,要将汇编指令1E(四字节)放入两字节a中会产生溢出,但此时溢出刚好覆盖了内存b中的值,将b变为0000 0000
-
- 通过计算,bss段不存在,为什么?信息是从哪里来的?
section headers
- 通过计算,bss段不存在,为什么?信息是从哪里来的?
3.3 ELF文件格式与布局
文件的格式(Class)相同的话,文件的布局就是相同的
Data:文件的存放格式(little endian小端存放)
OS/ABI:操作系统UNIX-System V
Type:格式REL(Relocatable file)可重入的文件
Entry point address:0x0 整个程序的入口地址,一般就是main函数的地址
可执行文件最后要和虚拟地址空间做映射,布局比较像就是为了方便映射
3.4指令数据生成的符号存放的位置和属性
文件名称(main.c)、指令段、数据段、数据和指令会生成符号
注意:(所有数据都能生成符号,指令只有函数名可以生成符号,普通局部变量不会生成符号,只会存放在指令段)
3.5外部引入变量的问题(在UND区域存放)
int gdata1=10;//.data
int gdata2=0;//.bss
int gdata3;//.bss
static int gdata4=20;//.data
static int gdata5=0;//.bss
static int gdata6;//.bss
extern int gdata; //声明gdata的定义在外部
extern int Sum(int,int);
int main() //函数名生成的是指令 .text段
{
static int ldata1=30; //.data
static int ldata2=0;//.bss
static int ldata3;//.bss
int ldata4=40;//.text
int ldata5=0;//.text
int ldata6=Sum(ldata4,ldata5);//.text
return 0;
}
test.c
int gdata=1000;
int Sum(int a,int b)
{
return a+b;
}
外部引入的变量都放在UND未定义区
3.6虚假地址、虚假位移问题
4.链接:(.o—>.exe)
1.合并段和符号表
2.符号解析
3.分配地址和空间(程序和虚拟地址空间的映射)
4.符号的重定位
5.运行:(程序变成进程)
1.建立虚拟地址空间和物理内存的映射(创建内核映射结构体),创建页目录页表
2.加载指令和数据
3.把入口地址放到下一行指令寄存器中