《程序员自我修养》读书笔记 第三章 目标文件里面有什么
这是关于《程序员的自我修养——装载,链接和库》一书的读书笔记,从第三章开始。第一章和第二章主要介绍操作系统和编译器的内容,不加赘述。
目标代码(英语:object code)指计算机科学中编译器或汇编器处理源代码后所生成的代码,它一般由机器代码或接近于机器语言的代码组成。目标文件(英语:object file)即存放目标代码的计算机文件,它常被称作二进制文件(binaries)。(From WIKI)
3.1 目标文件的格式
目标文件,例如,windows下的PE文件,Linux下的ELF文件等等。“从广义上看,目标文件和可执行文件的格式几乎一样,我们将两者看成一种文件”。除了可执行文件,静态链接库(dll,so)文件和动态链接库(lib,a)文件也按照这种格式存储。
在linux下,可以用file命令来查看文件类型。
3.2 目标文件是什么样的
这一节大致描述了目标文件的格式,以ELF为例,包括.text段,.data段,.bss段等等。做过操作系统实习JOS,或者编译实习什么的,对这个肯定比较熟悉了。
以及,书中提问,为什么要把code和data分开?首先从OS的角度讲,在内存空间中data和code的区域的权限不同,code一般不会被写。其次,data和code的分离对cache的设计和运行效率有帮助。最后,code这片实际的物理内存,也许会被许多进程的虚拟内存同时映射,code可以重用。
3.3 挖掘SimpleSection.o
好吧,我们也编译一下这个SimpleSection.c.电脑是64位的,linux也是64位的,只好用32位的虚拟机来尝试。
![](http://images0.cnblogs.com/blog/543611/201307/05182058-52ac8130e533477ab078577e5fbbe734.png)
![](http://images0.cnblogs.com/blog/543611/201307/05182058-58967fa517bb41c399337deadbd9569d.png)
Size指的是这个段的大小,text段的大小是50,是从地址0x00000034开始的。
利用objdump -s -d 命令查看16进制及反汇编。
![](http://images0.cnblogs.com/blog/543611/201307/05182059-3bca149193194781ae09596ef8e13474.png)
text段的内容就是代码(汇编指令)
data段中存放的是已经初始化了的全局静态变量和局部静态变量,有两个,global_init_varabal 和 static_var 共8字节
rodata段(貌似是 read only data )存放的是字符串常量 和 只读变量 %d
comment段,版本信息
以及,书中提到一个问题,如何将一个图片image.jpeg作为目标文件的一个段?
利用objcopy工具。
有介绍
3.4 ELF文件结构描述
ELF文件描述
这个链接有ELF文件的详细介绍:
查看ELF文件的内容用readelf命令,关于readelf命令可以参考这个文章:
总结一下:
读取ELF文件头 readelf -h 文件名
读取ELF程序头表 readelf -l 文件名
ELF节头表 readelf -S 文件名
符号表 readelf -s 文件名
![](http://images0.cnblogs.com/blog/543611/201307/05182059-fe8a814c5e9d41f58dd7ad196804fc0d.png)
段类型
w:可写
A:需要分配空间
S:可执行
字符串表
把ELF文件中的各种字符串(比如段名,变量名)统一管理的表。
.strtab 和 .shstrtab
重定位表
存放重定位的信息
![](http://images0.cnblogs.com/blog/543611/201307/05182100-5c34c98ea9214efd80500f320d5199dd.png)
3.5 链接的接口——符号
链接的过程的本质是把不同的目标文件之间相互“粘”到一起
“粘”到一起的方式就是通过符号
对“粘”可能有帮助的符号包括,本目标文件中的全局符号,外部符号(比如例子中的printf)。而局部符号,段名等,对于别的目标文件是不可见的。
ELF符号表,.symtab
特殊符号
我们使用ld链接器的时候,ld链接器会自动生成一些特殊的符号,比如:
__executable_start
__etext
_edata
_end等等。
我们在程序中 用 extern char etext[]; 声明了之后,可以直接使用。
符号修饰和函数签名
这一节,主要讲了如何处理符号名冲突的问题
最简单的,加一个下划线
C++中,对符号进行修饰,函数进行签名之类的,其实就是把一个名字加上一些修饰,使得不仅仅通过符号的“名字”作为区别符,而是把“名字”加上一些信息作为区别符。