微前端架构如何改变企业的开发模式与效率提升
927
2022-09-04
详细解析编译链接原理(上篇)
文章目录
一、引入虚拟地址空间二、虚拟地址空间三、编译链接过程
1. 预编译2. 编译3. 汇编4. 链接
四、解析ELF文件
1. 查看ELF文件头2. 查看段表3. ELF文件不存储.bss段,最后如何给.bss段分配虚拟地址空间?4. 关于强弱符号5. global弱符号链接前暂时记录在\*COM* 块
一、引入虚拟地址空间
C/C++代码经过编译器编译链接后,需要把指令和数据加载到内存执行
计算机由CPU(运算器、控制器)、内存(存储器)、IO(输入设备和输出设备)组成,为了屏蔽硬件的差异,使应用层能够忽略这些差异,操作系统提供了统一调用的接口(比如系统调用open,不仅可以用来打开文件,也可以打开socket,还可以打开字符设备)
为了屏蔽I/O的差异,OS提供了VFS(Virtual File System)为了屏蔽内存和I/O的差异,OS提供了虚拟存储器(虚拟内存)为了屏蔽CPU、内存、I/O,OS提供了进程
cpu的位数代表着cpu一次性能够处理的数据的位数(ALU的宽度或者数据总线的条数),32位代表cpu能够处理32位的数据,就是4个字节的大小。64位cpu代表cpu一次性能够处理64位的数据,也就是8个字节的大小的数据。
32位CPU:数据总线为32条,地址总线32条16位CPU:数据总线为16条,地址总线20条8位CPU:数据总线为8条,地址总线16条
CPU的位数就是地址总线的条数,这是错误的
二、虚拟地址空间
IBM对虚拟地址空间的解释:
如果它存在,而且你能看见它---它是真实的(real)如果它不存在,但你能看见它---它是虚拟的(virtual)如果它存在,但你看不见它---它是透明的(transparent)如果它不存在,而且你也看不见它---那肯定是你把它擦掉了
生成ELF文件后,.text,.data,.bss段的大小在程序运行时都是不变的,这些段是用来存放指令和数据的,指令和数据被生成后都是固定不变的,不可能写的变量一会有一会没有。而这个时候是没有.heap的,只有我们malloc申请内存时,OS才会分配.heap。此外程序刚运行起来还需要有.stack,因为函数就是在栈上运行的
.text:代码段,存放代码,指令.rodata:只读数据区,存放常量.data:存放初始化且初始化值不为0的数据.bss:存放未初始化和初始化为0的数据(包括全局变量,static修饰的变量).heap:堆区.stack:栈区(可存放函数形参和局部变量)
内核空间是共享的,用户空间是独立的
内核也分三部分:ZONE_DMA(0 ~ 16M)、ZONE_NORMAL(16M ~ 896M)、ZONE_HIGHMEM(896M ~ 结束)
ZONE_DMA(Direct Memory Access):加快磁盘和内存交换数据。没有DMA的时候,磁盘和内存之间交换数据时,数据必须通过总线经过CPU寄存器才能到达内存,这非常浪费CPU资源。有了DMA以后,某进程进行I/O的时候CPU就会空闲下来去调度其他的进程,增大了CPU的使用效率ZONE_NORMAL:该区域的物理页面是内核能够直接使用的ZONE_HIGHMEM:32位OS在内核里映射高于1G的物理内存时会用到高端内存。64位系统是没有高端内存的,因为64位系统的内核空间高达512G,不需要使用这一区域
#include
三、编译链接过程
1. 预编译
gcc -E main.c -o main.i
生成预编译文件,进行头文件引入以及宏替换,同时清理注释。不做任何有效的检查
2. 编译
gcc -S main.i -o main.s
词法分析、语法分析、语义分析、代码优化,生成符号(我们编译链接的错误基本上都和符号表有关)
3. 汇编
gcc -c main.s -o main.o
.s文件中有很多汇编指令,汇编这一过程就是把汇编指令转换为特定平台的机器码
*.o称为二进制可重定位目标文件,在*.o文件中有符号表,只有数据(包括全局变量,static修饰的变量)才产生符号,局部变量生成的是指令
4. 链接
gcc main.o -o main
合并所有.o文件的段,并调整段偏移的长度,合并符号表,进行符号解析,然后再给符号分配内存地址(虚拟地址)链接的核心:符号重定位
四、解析ELF文件
1. 查看ELF文件头
readelf -h main.o
2. 查看段表
readelf -S main.o
ELF文件头的大小为52字节,0x34.text:偏移地址为0x34,段大小为0x1b,0x34 + 0x1b = 0x4f,无法被4字节整除(对齐方式),所以补了一个字节,.data从0x50开始.data:偏移地址为0x50,段大小为0x0c.bss:偏移地址为0x5c,段大小为0x14,表示占20个字节,可是我们推测的是有6个int变量存储在.bss段,不对??(我们后面解释).comment:偏移地址为0x5c,段大小为0x2d,偏移地址和.bss的偏移地址重合,并把.bss覆盖,说明该ELF没有存储.bss段,.bss省的是ELF文件的空间ELF文件就是一个文件头,加上各种段,最后一个段的偏移地址 + 该段的大小,就是整个ELF文件的大小
故ELF文件组成格式为:
查看ELF文件的内容
objdump -s main.o
3. ELF文件不存储.bss段,最后如何给.bss段分配虚拟地址空间?
由于.bss存储的都是没有初始化或者初始化为0的数据,ELF文件中不会存储.bss段,那程序运行起来的时候,怎么知道存储在.bss段的信息?
先查看文件头,找到段表的位置,然后查看.bss段占了多少空间,运行的时候就开辟多少虚拟内存,全部初始化为0
4. 关于强弱符号
C语言里面有强符号(初始化)和弱符号(未初始化)的概念,如果在C语言工程里面:
出现多个强符号,编译肯定出错出现同名的强、弱符号,我们选择强符号出现同名的弱符号,我们选择内存占用最大的弱符号
比如:出现多个强符号,编译出错
编译的时候,每个源文件独自编译,链接的时候再整合符号
test.c
// 在这里就应该能看出来,程序运行起来使用的x不一定是当前这个弱符号x// 因为其他文件中可能还存在同名强符号或者内存占用更大的弱符号int x;void func() { // 编译阶段:20写入x的内存,写4个字节 x = 20;}
main.c
#include
在func函数中,编译阶段生成 把立即数20写入x的内存,写4个字节的指令(mov dword ptr [x], 14h ),链接阶段同时发现弱符号int x和强符号short x = 10。于是确定x的地址,就是这个short x的地址,汇编指令中把20写入x的地址,实际上是写入了强符号short x的地址
虽然编译阶段就确定short x和short y存放在.data段,并且确认了初始值都为10。程序执行起来的时候,执行了func函数相关的汇编指令,在x的内存上写入4个字节,写入的是14 00 00 00,所以起始地址为&x,往后的4个字节被赋值为14 00 00 00,于是修改了short x和short y的值
5. global弱符号链接前暂时记录在*COM* 块
在查看段表的部分,我们知道.bss的偏移地址为0x5c,段大小为0x14,表示占20个字节,可是我们推测的是有6个int变量存储在.bss段,我们解释一下:
链接的时候处理所有的obj文件中的global符号,而不处理local符号(本文件可见)。而int gdata3是global的,且是弱符号,可能被其他我文件中同名的强符号覆盖。static int gdata6虽然也是弱符号,但它是local的,只是本文件可见,链接的时候也无法被覆盖。
查看符号表(main.o是最早的那个代码)
objdump -t main.o
shen@NONOR-shen:/mnt/c/Users/shen/Desktop$ objdump -t main.omain.o: file format elf64-x86-64SYMBOL TABLE:0000000000000000 l df
我们可以看到所有的变量都是存放正常的,除了弱符号gdata3存放在*COM*块(表示gdata3当前是一个未决定的符号,需要等到链接完成才能决定具体存放在ELF文件的哪个部分),这就是为什么我们之前分析的6个未初始化和初始化为0的数据应该全部存放在.bss段,实际上只存了5个
我们链接一下,然后查看符号表:
shen@NONOR-shen:/mnt/c/Users/shen/Desktop$ gcc main.o -o mainshen@NONOR-shen:/mnt/c/Users/shen/Desktop$ objdump -t main1: file format elf64-x86-64SYMBOL TABLE:0000000000002000 l d .rodata 0000000000000000 .rodata0000000000003df0 l d .init_array 0000000000000000 .init_array0000000000003e00 l d .dynamic 0000000000000000 .dynamic0000000000004000 l d .data 0000000000000000 .data000000000000401c l d .bss 0000000000000000 .bss0000000000000000 l d .comment 0000000000000000 .comment000000000000401c l O .bss 0000000000000001 completed.80610000000000000000 l df
可以看到链接完成后,gdata3已经确定存放在.bss段了
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~