程序员的自我修养——(3)目标文件

目标文件

目标文件: 源代码经过编译后,但未进行链接的 “中间文件”(例如,windows下的.obj、linux下的.o)
他 和 exe可执行文件,无论内容和格式,都非常接近! 广义上,两者可以看成是一个东西。


目标文件(Executable),基本可以分为:

  • Windows下的: PE(Portable executable)
  • Linux下的: ELF(Executable linkable format)

他们都遵循 “COFF(Common file format)”格式,COFF是标准的exe可执行文件格式。

COFF不仅包含 exe,例如DLL(Dynamic link lab) SLL(Static link lab) 都遵循COFF格式。


COFF分类:

  • 可重定位文件,例如windows的obj,linux的o
    该文件包含 代码和数据,以后用来被链接成exe或dll (静态sll就属于这一类)
  • 共享目标文件,例如windows的dll,linux的so
    该文件包含 代码和数据,有两种使用方式:(一种是:和其他的dll和sll,进行链接产生exe)
    (另一种是: 动态链接器 将dll 和exe进行结合,作为进程映像的一部分)
  • 可执行文件,例如windows的exe,linux的bash
    该文件包含 可以直接运行的程序
  • 核心转储文件,例如linux下的core dump
    当进程意外终止,系统可以将该进程的地址空间内容 和 终止时的一些信息,转储到该文件里。

在linux下,可以通过file命令,得到文件的格式。


历史
Linux最早是使用.out文件,但共享库出现后 这种文件就捉襟见肘了,于是 产生了COFF格式。

COFF是由Linux提出的,作为可执行文件的格式。
后来,windows在COFF的基础上,引入了PE格式
Linux也在COFF的基础上,引入了ELF格式。

现在linux,都是以ELF作为可执行文件的基础格式。

所以,PE和ELF 非常相似,因为都是基于COFF格式。

目标文件—格式

因为一个程序,分为: 代码 和 数据。 所以,目标文件 也分为: 编译后的机器指令代码 和 数据。

具体的,目标文件里的内容,是划分为了 若干个“段”(其实就是 内存!):

  • “文件头 File Header”
    描述:该文件的一些属性,是静态/动态链接、入口地址、、
  • “代码段 .text”
    他里面存的:即你程序的代码文本。 反汇编后,就可以得到汇编指令。
    这个段的大小,取决于你程序代码文本的大小。
  • “数据段 .data”
    已经初始化了的 全局变量 + 已经初始化的 局部静态变量
    不考虑全局静态变量,你可以思考下,全局静态变量 其实就是全局变量(静态:用处是在,局部域/类 里面)
    比如: int g_data = 1 static local_data = 2
    那么,bss段的大小是8字节。分别是:00,00,00,01 和 00,00,00,02 共8个字节,存储的是 这个变量的
    (顺序,取决于cpu的字节序Byte Order:大端、小端)
    这个段的大小,就是所有这些变量的sizeof之和!!!
  • “预存段 .bss”
    未初始化的 全局变量 + 未初始化的 局部静态变量。
    bss段只是记录了 这些变量 的空间之和!!! “预留 这些大的 空间!”,但并没有将内存空间设置初始值!
    bss: block started by symbol,用于为符号 预留 一块内存空间
  • 上面的主要的一些分段,细分下去 其实还有很多
    比如,还有.rodata(rom data):存 只读const数据、字符串常量
    你的printf("%d\n", 123),其中的%d\n 这就是个字符串常量,他就在.rodata段,占用4[%][d][\][n]字节
    还有.rel段,表示“重定位表”: 即extern的变量和函数

将程序 分为: 代码段.text 和 数据段.data + .bss 的好处:

  • 代码段 和 数据段,被放到 不同的 虚拟内存空间。 代码段是不可以写的!! 这样防止修改代码段的内容
  • cpu的缓存 分为: 数据缓存 和 代码缓存,这样可以提高cpu缓存的 命中率
  • 当运行同一程序的多个副本时,因为他们的代码段 都是完全一样 而且是只读的,这样会极大的节省内存。

自定义段

__attribute__((section("name"))) int a = 123;

指定 该变量/函数 所在的段。

符号

一个目标文件里的:全局变量和函数(可以是自定义的,也可以是extern的),就称为:符号。
符号存放在:符号表Symbol Table,每个目标文件 都有一个符号表

每个符号都有一个符号值,即该变量/函数 的地址。

细分符号有: 内部符号(不带extern的全局量)、外部符号(带extern的)

符号修饰

源代码经过编译 在得到目标文件时,会对“变量、函数”的名称 进行 修饰Decorate
最终得到的:修饰后的名称decorated name,就是“符号名”

所有的符号名,肯定是不同的。
这个修饰,即根据他的:命名空间、原生名、返回值、参数名、、、 得到一个最终的名称

比如: f1函数里有个static的a,f2函数里也有个static的a。那么, 最终他俩最终的修饰名,肯定是不同的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值