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

目标文件内容

1. “文件头”:描述整个文件的文件的文件属性,包括文件是否可执 行、是静态链接还是动态链接及入口地址(可执行文件)、目标硬件、目标操作系统等信息,文件头还包括一个段表。段表其实就是一个描述文件中各个段的数组,描述段在文件中的偏移位置及段的属性等。
2. .text段:编译后执行语句都编译成机器代码,保存在该段。
3. .data段:已经初始化的全局变量和局部静态变量都保存在该段。
4. .bass段:未初始化的全局变量和局部静态变量一般放在该段。

总体来说,程序源代码被编译之后主要分成两种段:程序指令和程序数据。代码段属于程序指令,而数据段和.bss段属于程序数据。
.rodata段:存放的是只读数据,一般是程序里面的只读变量和字符串变量。
以“.” 前缀的段是系统保留的,程序可以室友一些非系统保留的段名创建新段。

 __attribute__((section("name"))) int val = 21;
  //GCC的扩展机制,指定变量所处的段

初始化为0或未初始化的变量会被编译器优化放在.bss段(不占磁盘空间),这样可节省磁盘空间。


数据和指令分开好处主要有如下几个方面:

  1. 权限:数据区域对进程来说是可读写的,指令区域对于进程来说是只读的。程序被装载后,数据和指令分别被映射到两个虚存区域,权限可以分别被设置为可读写和只读。防止程序的指令被有意或无意修改。
  2. 指令区和数据区分离有利于提高程序的局部性。对CPU的缓存命中率提高有好处。
  3. 最重要的原因。系统中运行多个该程序的副本时,他们的指令都是一样的,所以内存中只要保存一份改程序的指令部分(共享指令)。对于只读指令来说是这样,对于其他只读数据也是一样。共享指令在现代操作系统中占有极为重要的地位,特别是在有动态链接库的系统中,可以节省大量内存。

用binutils的工具objdump查看目标文件

-h:把elf 文件的各个段的基本信息打印出来。
-x:打印更多信息。
size命令:可以查看elf文件的代码段、数据段和BSS 段的长度。
-s:所有段的内容以十六进制打印出来。
-d:反汇编所有包含指令的段。


链接的接口——符号

每个定义的符号有一个定义的值(符号值),就是变量和函数的地址。
符号分类:
1. 定义子在本目标文件的全局符号,可被其他目标文件引用。
2. 在本目标文件引用的全局符号,却没有定义在本目标文件。(外部符号)
3. 段名:一般由编译器产生,值为该段起始地址。
4. 局部符合,只在编译单元内部可见。
5. 行号信息,目标文件指令与源代码中代码行的对应关系,可选。
6. 特殊符号:没有在程序中定义,但可以直接声明并且引用它。
1). __executable_start,程序最开始的地址(不是入口地址)。
2). __etext 或 _etext 或 etext ,代码最末尾地址。
3). _edata 或 edata ,数据段最末尾地址。
4). _end 或 end ,程序结束地址。

c++ 编译器会将在 extern “C” 的大括号内的代码当作C语言代码处理,C++的名称修饰机制将不会起作用。
C++的宏 “__cplusplus” :C++编译器在编译C++程序时默认定义该宏,可以使用该条件宏判断当前编译单元是不是C++代码。


弱符号和强符号(针对定义)

C/C++语言,编译器默认将函数和初始化了的全局变量作为强符号,未初始化的全局变量作为弱符号。(可用 GCC 的 “__attribute__((weak))“定义任何一个强符号为弱符号)
链接器对于全局符号处理规则:
1. 不允许强符号多次定义,否则会报错。
2. 如果一个符号在某个目标文件中是强符号,其他文件都是弱符号,选择强符号。
3. 所有目标文件中都是弱符号,选择占用空间最大一个。

正是由于第三条,所以编译时弱符号所需空间大小是未知的,编译器无法为某一弱符号分配空间。但链接器在链接过程中可以确定弱符号大小。

强引用和弱引用

在链接成最终可执行文件时,需正确决议,若没能找到该符号定义,链接器报符号未定义错误,称为强引用。
弱引用,如果符号未定义,链接器不抱错。未定义的弱引用,链接器一般默认为0。(__attribute__((weakref))声明外部函数引用为弱引用)

弱符号和弱引用主要用于库的链接过程。

  1. 库中定义的弱符号可以被用户定义的强符号覆盖,从而使得程序可以使用自定义版本的库函数;
  2. 程序可以对某些扩展功能模块的引用定义为弱引用,当我们将扩展模块与程序链接在一起时,功能模块就可以正常使用;若去掉也可以正常链接,只是少了某些功能,是程序功能更加容易裁剪和组合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-小蛙-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值