第3章 目标文件里有什么

.obj是目标文件,所以可以知道目标文件是指编译后生成的文件,目标文件几乎和可执行文件相同只是稍微有点不同而已。其不同之处在于有些符号和地址没有被调整。

3.1目标文件的格式

正是因为目标文件与可执行文件几乎相同,所以它们的存储格式是一样的,可以把它们近似看成同一种文件。

Linux下的动态链接库格式为.so,Windows和Linux下的静态链接库格式分别为.lib和.a。

静态链接库是一个文件,该文件包含了很多目标文件,它是一个整体。

Linux下的可执行文件是按照ELF格式存储的,ELF标准包含4种文件,请看P81。我所熟悉的Windows下的DLL就属于共享目标文件。

3.2目标文件是什么样的

目标文件一般包含了哪些内容?编译后的机器指令代码、数据、连接所需的信息、符号表、调试信息、字符串等。

目标文件把信息按照属性的不同分段存储。写到这里我感觉这书上说的与老师课上讲的程序在内存中的分段方法有些相似。在目标文件中,编译后的机器指令代码放在代码段(Code

Section)中,段名一般为.code和.text。全局变量和静态变量放在数据段(Data

Section)中,段名一般为.data。

BSS段(Block Started By Symbol)用来存储未初始化的静态变量和全局变量。话虽如此bss中并没有这些变量的内容,它只是为这些变量按照所占空间大小预留空间而已。由于这些变量默认就是0,所以压根没必要再为它们分配一个数据0,也没有必要让它们待在data段中。因此bss的作用是为这些变量预留空间。

另外目标代码还有一个文件头用来保存该目标文件的信息,它里面还有一个段表。

源代码被编译以后生成两种段数据段和指令段,.code.text属于指令段.data.bss属于数据段。

这样分主要有3点好处:

1、防止程序被有意无意篡改。这是因为指令段只读,数据段可读写。

2、提高了缓存命中率。

3、节省内存空间。因为指令段可被多个副本共享,但是副本可以拥有自己的数据段。

3.3挖掘SimpleSection.o

原来目标文件中的段还有只读数据段(.rodata)、注释信息段(.comment)、堆栈提示段(.note.GNU-stack)。

从书中所给的例子来看一个ELF文件只有4个段是由内容的,即.data、.text、.rodata、.comment。

从图3-3可以看出在内存中,从低地址到高地址是按照ELF

header、text、data、rodata、comment、other

data的顺序存放的。

3.3.3

BSS段

由本小节可知,全局变量可能因为语言和编译器的不同不一定存放在bss段,但是静态变量一定存放在bss段。

虽说bss存放的是未初始化的静态和全局变量,但是有些变量如果被初始化为0,它也会被放在bss中,这是编译器的优化,有时候这种优化会带来麻烦。

3.3.4其他段

表3-2列出了其他段及意义。

此外,这个段还可以自定义。

3.4

ELF文件结构描述

图3-4展示了ELF的层次结构。

最重要的两个部分就是ELF文件头和段表。ELF文件头描述整个文件的基本属性,段表描述各段的信息。

3.4.1文件头

清单3-2清楚地描述了ELF文件头的信息,P95黑体部分列举了ELF文件头包含的信息。

ELF文件兼容各平台,它的文件结构和相关参数定义在”/usr/include/elf.h”里,它有32位和64位两种。

表3-3展示了elf.h的自定义变量体系。

表3-4展示了ELF文件头结构成员含义。

ELF魔数:ELF文件头的第一个字段是Magic,包含16bytes,对应于Elf32_Ehdr中的e_ident成员。Magic用来表示平台的各种属性。

1~4个字节是所有ELF文件都相同的标识码,分别对应del、E、L、F,这四个字节就是ELF魔数。操作系统通过确认魔术是否正确以决定是否加载可执行文件。

第5个字节用来表示ELF文件是32位的还是64位的。

第6个字节用来表示ELF字节序。

第7个字节用来表示ELF文件版本号。

后面的9个字节用来预留,有些平台可能用来作为扩展标志。

Elf32_Ehdr中的e_type成员表示ELF文件类型,ELF总共有三种文件类型如表3-5所示。操作系统是通过判断文件类型而不是扩展名来确定ELF文件类型的。

Elf32_Ehdr中的e_machine成员表示ELF文件的平台属性。虽然ELF遵循统一标准但不代表同一ELF文件可以在不同平台上使用。

3.4.2段表

它用来表示各个段的信息,ELF文件中的段是由段表决定的。

一个ELF文件不仅仅包含像data、text、bss这样的段,还包括其他的辅助性段。

段表是一个Elf32_Shdr类型的结构体数组,元素的个数代表段的个数,每个元素对应一个段。这个Elf32_Shdr被称为段描述符。

表3-7描述了Elf32_Shdr中各字段的意义。

段的名称对于编译和链接有意义,对操作系统无意义。决定段的类型的是段的类型字段,并不是段的后缀名和名称。

段的类型和段的标志位字段决定了段的属性。表3-8展示了段的各种类型。

段的标志位表示该段在进程虚拟地址空间中的属性,如是否可读。表3-9列出了段的各种属性。

表3-10列出了系统保留段的各种属性。

段的连接信息包括sh_link和sh_info,它们与链接相关,如表3-11所示。

3.4.3重定位表

目标文件中有一个SHT_REL的.rel.text字段,它是重定位表。重定位发生在连接的过程中,这个在前面已经讲过,重定位表记录了重定位相关信息。

3.4.4字符串表

顾名思义,就是用来表示各种名称的字符串的表。它是一个装有各种字符串的表格,每个字符在表中都有一个固定的位置。

这种表在ELF文件中保存为2种形式——.strtab和.shstrtab,它们分别是字符串表和段字符串表,它们在ELF文件中都以独立的段而存在。为了轻松地找到这个段,在ELF文件头中包含了这两个段的下标,名为e_shstrndx。

3.5链接的接口——符号

链接是组合目标文件的过程,目标文件是根据彼此之间的地址相互引用,从而组合成可执行文件的。而,这个地址可以简单地理解为目标文件中的函数和变量。在这里,函数和变量统称为符号,函数名和变量名统称为符号名。

链接器的着眼点主要在定义在本目标文件和定义在其他目标文件的全局性符号,因为只有这些涉及到目标文件之间的组合。

3.5.1

ELF符号表结构

ELF文件的符号表是一个段,段名为“.symtab”,它是一个Elf32_sym类型的数组,每个数组元素代表一个符号。

在Elf32_sym结构体中有一个32bit成员叫st_info,低4bit表示符号的类型,高28bit符号的绑定信息。绑定信息具体可见表3-15,符号类型可参见表3-16。

Elf32_sym.st_shndx:如果符号定义在本目标文件中,它表示该符号所在的段在段表中的下标,否则它具有其他意义。st_shndx具体信息可见表3-17。

Elf32_sym.st_value:每个符号都有一个对应值,它一般为变量和函数的地址。st_value的意义有如下几种:

1、如果符号定义在目标文件中,并且它不是COMMON类型,则st_value代表符号在段中的偏移。

2、如果符号定义在目标文件中并且是COMMON类型,则st_value表示符号的对齐属性。

3、在可执行文件中st_value表示符号的虚拟地址。

3.5.2特殊符号

链接器本身自带的,不是你定义的,定义在链接脚本中的,但是你可以用的,这样的符号是特殊符号。它们存在的时机是链接器链接生成可执行文件时,此时链接器会将它们解析成正确的值,

书中P110举了几个具有代表性的特殊符号。

3.5.3符号修饰与函数签名

本小节明确了函数签名的概念。

函数签名:主要是指函数名和参数类型,其次是所在类和命名空间等。它用于区分不同函数。

编译器和连接器会使用名称修饰的办法加工函数签名使之成为修饰后名称,在C++中为符号名。

不同的编译器对函数签名的修饰方法不同,这导致不同种类的目标文件无法互连。

原来C++编译器已经默认定义了宏__cplusplus来兼容C语言和C++。

3.5.5弱符号和强符号

在不同目标文件中含有相同全局性符号定义,这种情况被称为强符号,它会引起符号重定义。

C/C++编译器认为未初始化的全局变量是弱符号。

这个强弱符号是可以被定义的,所以强弱之别是根据定义来划分的,并不针对符号的引用,P117代码说明了这一点。

链接器根据符号的强弱来处理和选择定义的全局变量:

1、不允许多次定义强符号,否则报错。

2、同一个符号在各目标文件中出现了多次,但只有一个是强符号,那么编译器选择强符号的那个。

3、如果一个符号在所有目标文件中都是弱符号,那么编译器选择占用空间最大的一个。由此可见编译器对于弱符号的选择并不明显,所以由弱符号造成的错误也相对难以发现。

强引用:目标文件对于非本目标文件的符号引用,在链接成可执行文件的过程中,如果找不到该符号的定义,就报未定义错误。

弱引用:与强引用差不多,只不过在找不到符号时不报错。

强弱引用主要用于库的链接。对于未定义的弱引用,编译器为便于识别把它看作是某一值,一般为0。

弱符号与COMMON块联系较密切。

弱引用是可以手动声明的,如P118第一段代码所示。

弱符号的作用在于提供一个默认的库符号,但是当用户想要自定义该符号的时候,该自定义符号就获得了更高的优先级。而弱引用的作用在于增强了程序的可扩展性,因为有了弱引用程序功能更强,没有弱引用程序也能正常运行。

3.6调试信息

目标文件和可执行文件中都可能保存调试信息,ELF文件采用DWARF格式保存调试信息。

由于调试信息与可执行文件最终结果无关,而且占用大量空间,所以在发布软件时应该去掉这些调试信息。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容