




已阅读5页,还剩14页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
程序员的自我修养2目标文件Table of Contents1、概述目标文件格式与结构12、举例说明目标文件中的各段3编译并生成目标文件3使用objdump-h 命令查看各段基本情况4使用objdump s d 挖掘各段内容53、文件头84、段表95、重定位表:116、字符串表:117、链接的接口:符号与符号表118、符号修饰与函数签名149、强符号与弱符号、强引用与弱引用1510、调试信息:1611、用到的命令:1712 动态存储变量和寄存器变量171、概述目标文件格式与结构目标文件(win .obj/linux .o)里有什么?或者说源代码在编译后是怎么存储的?格式:已经是可执行文件的格式,只是没有经过链接,其中有符号或者地址没有被调整,本身按照可执行文件格式存储。现在流行的可执行文件的格式,在windows平台下是PE,在linux平台下是ELF格式,前者是扩展名为.exe的文件后者无扩展名。不光可执行文件和目标文件按照这种格式存储,动态链接库(win .dll/linux .so)和静态链接库(win .lib/linux .a)。ELF文件标准把系统中使用ELF格式的文件归结为:relocatable file (.o .a) , executable file ,share object file (.so .dll)等。可以使用file命令查看文件格式。目标文件和可执行文件的格式发展历史几乎是OS发展史,Unix最早的可执行文件格式为a.out格式,设计很简单,到后来共享库的概念出现时,a.out格式越来越捉襟见肘了,于是设计了COFF格式来解决问题。Unix system V release 3提出了COFF格式,微软基于COFF制订了PE标准,制定、协议、标准!Unix system V release 4在COFF的基础上引入了ELF格式。所以PE和ELF十分相似。COFF的主要贡献是在目标文件中引入了段机制,不同的文件可以拥有不同数量及不同类型的段。我们猜想目标文件中至少有编译后的指令、数据!除这些之外,目标文件中还包含了链接时所需信息,如符号表、调试信息、字符串等。结构:文件内容按照不同的属性放在不同的section/segment,他们都是一定长度的区域,不加区别,唯一的区别实在ELF的链接视图和装载视图。代码段:.code/.text 数据段:.data存放初始化过的全局(静态)变量和静态局部变量.bss段比较特殊,它包含未初始化的静态变量,但并不是存储。因为未初始化的静态变量的值为0,在.data中开辟空间放它们是对磁盘的浪费。.bss段并不属于文件内容,不占空间。记录了未初始化的静态变量的和,为它们预留空间,因为程序运行时需要它们。File head文件头描述了整个文件的属性,包括文件是否可执行、是静态链接还是动态链接(若可执行),目标硬件、目标操作系统等,文件头中还有包括段表,是描述文件中各段信息的数组,描述了各段在文件中的偏移位置及属性。代码和数据为什么要分开放?1、代码只读,数据可读写,当可执行文件被装载后,可以被映射到权限不同的存储空间。2、现代CPU的缓存机制,分指令cache和数据cache,提高命中率。3、当程序存在多个副本时,代码段共享,数据私有。2、举例说明目标文件中的各段编译并生成目标文件使用objdump-h 命令查看各段基本情况-h参数是将各段的基本信息打印出来(显示关键段、省略辅助性段)。-x会打印出更多的信息,但是又多又复杂。暂且忽略后三个段,先关注.txt/.data/.bss最容易理解的是段的长度Size,段所在的位置File Offset。第二行是属性,CONTENTS表示该段在文件中存在。有一个专门的命令size可以查看ELF文件的代码段、数据段、BSS段。使用objdump s d 挖掘各段内容挖掘各段的内容,我们还是离不开objdump这个利器,-s参数可以将所有段的内容以十六进制的形式打印出来,-d参数可以将包指令的段反汇编。最左边一列是偏移量,中间4列是十六进制内容,最右边一列是ASCII形式。对于代码段.text,共84字节的内容与size命令的结果一致。.Data段存放已经初始化了的全局(静态)变量和局部静态变量。前面一共两个这样的变量,每个变量4字节,所以一共8个字节。所以.data段一共8字节大小。.data段的54000000和55000000对应的是84和85,为什么不是00000054呢?这是CPU的字节序的问题,也就是所以为的大小端问题。所谓的小端模式,是指数据的高位保存在内存的高地址中,而数 据的低位保存在内存的低地址中。我们在调用printf时,使用了一个字符串常量“%dn”,它是一种只读数据,所以被放到了.rodata段:25640a00恰好是字符串:%dn0.rodata段存放存放只读数据,一般是程序里面的只读变量(如const修饰的变量)和字符串常量。Global_uinit_var和static_var2存放在.bss段,更确切的说是为他们俩预留了空间,看到该段只有4字节,而不是8字节。我们可以通过符号表看到只有static_var2放在了.bss段,这跟不同语言与不同的编译器有关,有些编译器会将全局未初始化变量放在.bss段,有些则不会。除了.txt/.data/.bss这三个常用的段之外,ELF文件也可能包含其他的段,用来保存与程序相关的其它信息。.rodata已经说过了。.comment存放是编译器版本信息。.debug存放调试信息.dynami动态链接信息.hash符号哈希表.line调试时的行号表.note额外的编译器信息,如程序的公司名称、发布版本号等.symtab符号表.shstrtab段名表.strtab字符串表,用于存储ELF文件中用到的各种字符串。以.开头的表明这些段名是系统保留的,应用程序也可以使用一些非系统保留的名字作为段名。比如我们可以在ELF文件中插入一个叫music的段,里面存放了一首MP3音乐,当ELF文件运行起来以后,可以读取这首音乐。现在有一个image.jpg,大小0x82100字节,我们想把它作为目标文件的一个段,怎么做?Objcopy I binary o elf32-i386 B i386 image.jpg image.oObjdump ht image.o 我们在符号表里看到,“_binary_image_jpg_start”,”end”,”size”,表示该图片在内存中的起始地址、结束地址和大小,我们可以在程序中直接声明并使用它们。我们有时希望某些代码和变量能够到指定的段中去,以实现某些特定的功能,如:为了满足某些硬件内存和IO布局,或者像是linux操作系统内核中用来完成一些初始化等。GCC提供了一种扩展机制,是程序员可以制定变量到所处的段:_attribute_(section(“FOO”)int global = 42;_attribute_(section(“BAR”)void foo()3、文件头ELF header描述了整个文件的基本属性、比如ELF文件版本、目标机器型号、程序入口地址等。使用readelf h查看目标文件,文件头是个结构体,里面的变量代表各个属性值,readelf h 相当于解析了这个结构体。与ELF相关的数据结构在/usr/include的elf.h中。可以看到,文件头中定义了ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台、ABI版本、ELF文件类型、硬件平台、硬件平台版本、入口地址、程序头入口地址、段表的位置和长度。Entry point address:入口地址,规定ELF程序的入口虚拟地址,操作系统在加载完该程序后从这个地址开始执行进程的命令。可重定位文件一般没有入口地址,这个值为0.Start of section header段表在文件中的偏移。也就是说段表从273字节开始。Size of section header段表描述符的大小,即描述 每个段需要多少字节。Number of section header :段表描述符的数量,这个值等于ELF文件中拥有的段的数量ELF标准规定用16个字节的魔数来确认文件类型、来识别ELF文件的平台属性。,比如比如上图中的data/class/version/os/ABI/ABI version等。比如ELF文件的最开始4个字节是所有ELF文件必须相同的标识码,ox7f,ox45,ox4c,ox46,第一个字节对应ASCII中的DEL字符,后三个刚好吃ELF的ASCII码值。几乎所有可执行文件的格式开始都是魔数,a.out格式的最开始两个字节为ox01,ox07;PE/COFF最开始两个字节为ox4d,ox5a,即MZ。用魔数来确认文件的类型,操作系统在加载可执行文件 时会确认魔数是否正确,如果不正确则拒绝加载。4、段表段表。我们知道ELF文件中有很多各种各样的段,这个段表就是保存这些段的基本属性的结构。段表是除了文件头之外最重要的结构。文件头是个结构体数组,里面的结构体是对每个段的描述,也称为段描述符它描述了每个段的信息,比如每段的段名、段的长度、在文件中偏移、读写权限等。也就是说,ELF文件的段结构就是由段表决定的,编译器、链接器、装载器都是靠段表来定位和访问各个段的。段表在ELF文件中的位置由文件头的e_shoff决定,也就是start of section header。之前使用objdump h查看的是关键段信息,忽略了很多辅助性段的信息,如:符号表、字符串表、段名字符串表、重定位表等。使用readelf S 可以查看段表的内容:段表的结构比较简单,它是以ELF32_shdr结构体为 元素的数组。这个结构体大小在文件头中显示为40字节,又被称为段描述符。结构体成员包含段名称、段类型、段标志位、段长度、段偏移等。现在我们终于可以勾勒出完整的文件段图:段名称对于编译器、链接器来说是有意义的,但是对于操作系统来说是没有实质意义的,对于OS来说一个段如何处理取决于段的类型和段的标志。段的类型:用宏表示,举例SHT_NULL 无效段 SHT_PROGBITS 程序段.代码和数据段。.data/.debug/.comment/.line/.rodata/.rodata1/.txtSHT_SYMTAB 表示该段的内容为符号表.symtabSHT_STRTAB表示该段的内容为字符串表/.shstrtab/.strtabSHT_RELA 重定位表,该段包含重定位信息SHT_HASH符号表的哈希表SHT_DYNAMIC动态链接信息表SHT_NOTE提示性信息SHT_NOBITS表示该段在文件中没有内容,比如.bss段SHT_REL 该段包含了重定位信息段的标志位:sh_flag段的标志位表示该段在进程虚拟地址空间的属性,比如是否可写,是否可执行等。SHF_WRITE表示该段在进程空间中可写SHF_ALLOC表示进程空间中需要分配空间。像代码段、数据段、.bss段都会有这个标志。SHF_EXECINSTR表示可执行,一般指代码段。5、重定位表:我们注意到目标文件中有个.rel.txt段,他的类型是SHF_REL,也就是说它是一个重定位表。也就是说我们链接器在处理目标文件时,需要对目标文件中的某些部位进行重定位,即代码段和数据段中那些绝对地址的引用位置。这些重定位信息都记录在重定位表里。对于每个需要重定位的代码段或数据段都有一个相应的重定位表,比如.rel.txt就是对.txt段的重定位表,因为.txt中有一个绝对地址引用,那就是printf。那么sh_link表示符号表的下标,sh_info表示它作用于哪个段,比如.rel.text作用于.text段,.text段的下标为1,那么sh_info值为1.6、字符串表:ELF文件中用到了很多的字符串,比如段名、变量名等。因为字符串的长度往往是不固定的,所以用固定的数据结构来表示它比较困难。一种常见的做法就是把字符串集中起来存放,然后用偏移来引用字符。0 空字符串1 helloworld6world12通过这种方法,在ELF文件中引用字符串只需要给出一个数字下标即可,不用考虑字符串长度问题。字符串表一般以段的形式保存,名称一般为.strtab或者.shstrtab,前者为字符串表,后者为段表字符串表,顾名思义,前者保存普通的字符串,如符号的名字,后者表示段表中用到的字符串,最常见的就是段名sh_name。7、链接的接口:符号与符号表链接的过程本质就是要把多个不同的目标文件之间相互粘到一起。为了使目标文件之间能够相互粘合,这些文件之间必须有固定的规则才行,就想积木必须有凹凸。粘其实是地址引用,即函数和变量的地址引用,我们将函数和变量统称为符号。符号看作是链接时的粘合剂,整个链接过程以符号基础才能完成。每一个目标文件都有一个符号表symbol table,这个表里记录了目标文件用到的符号,每一个定义的符号都有一个值,叫符号值,对于变量和函数来说,符号值就是地址。除了变量和函数以外,还存在其他几种不常用的符号,将符号表中的所有符号进行分类: 定义在本目标文件的全局符号,可以被其他目标文件引用。比如fun1/main/global_init_var 在本目标文件中引用全局符号,却没有在本地定义。叫外部符号,如printf 段名,这种符号往往由编译器产生,它的值就是该段的起始地址。比如.txt/.data等 局部符号,如static_var和static_var2,局部符号对链接过程无用,链接器一般忽略他们。 行号信息,即目标文件指令与源代码中的代码行的对应关系,它也是可选的。对我们来说,最值得关注的是全局符号,即前两种,因为链接过程只关心全局符号的相互粘合,其它的符号对于别的目标文件都是不可见的。我们可以用很多方法查看ELF的符号表。符号表:ELF文件的符号表一般是一个单独的段,段名叫.symtab。符号表的结构比较简单,是一个结构体数组。到这就基本看出,每个表都是一个结构体或者结构体数组,占单独一个段第一个结构体未定义符号。每个结构是一个符号的条目。每个结构体的成员如下:St_name 符号名,这个成员 包含了该符号名在字符串表中的下标St_value符号值,这个值跟符号相关,可能是一个绝对值,也可能是一个地址等。St_size对于一个包含数据的符号,这个值是该数据类型的大小St_info 符号类型和绑定信息st_shndx符号所在段符号类型和绑定信息:该成员低4位表示符号的类型。高28位表示符号绑定信息。对于绑定信息的宏:STB_LOCAL:局部符号,对于目标文件外部不可见STB_GLOBAL:全局符号,外部可见STB_WEAK:若引用符号类型:STT_NOTYPE 未知符号类型STT_OBJECT 该符号是数据对象,比如变量、数组STT_FUNC 该符号是个函数或者其它可执行代码STT_SECTION该符号表示一个段,这种符号必须是STB_LOCALSTT_FILE该符号是文件名,一般都是目标文件对应的源文件,它一定是STB_LOCAL的符号所在段:如果在本文件定义,则就是段的下标,如果不是:SHN_UNDEF,表示该符号未定义,即本地引用,但是在别处定义。如printfSHN_COMMON表示该符号是一个COMMON块类型符号,一般来说,未初始化的全局符号定义就是这种类型。符号值:有这么几种情况: 如果不是COMMON类型符号,则st_value表示该变量或函数在所在段的偏移,这也是目标中定义的全局变量符号最常见的情况。 如果是COMMON类型的符号,则st_value表示该符号的对齐属性。 在可执行文件中,st_value表示符号的虚拟地址。这个虚拟地址对动态链接比较有用。使用readelf s查看详细符号表:最左边是符号表数组下标,第二列是值st_value,第三列即符号大小st_size,第四列和第五列分别是符号类型和绑定信息,即st_info。倒数第二列即st_shndx。需要注意的是:想func1和main的value都是在.text段的偏移。关于两个静态局部变量为什么有了后缀,涉及到符号修饰。那些STT_SECTION类型的符号,他们表示NDX值代表的段名。符号名没有显示,比如2号符号的ndx值为1,则符号名应该为.text。我们使用objdump t 可以看到:特殊符号:在ld工作时,它会为我们定义很多的特殊符号,这些符号我们可以直接用extern声明后,在程序中使用。其实这些符号是在链接脚本中定义的,在链接成可执行文件时会被解析成正确的值。_executable_start程序的起始地址,不是入口,是程序最开始的地方。_etext/_etext/etext 代码段结束的地址_edata/_edata/edata数据段结束的地址_end/end符号为程序结束的地址以上地址都是程序被装载时的虚拟地址。8、符号修饰与函数签名70年代以前,编译器在生成目标文件时,符号名和对应的变量、函数的名字是一样的。比如在源代码中有一个foo函数,那么在目标文件中也有一个foo符号名。后来UNIX和C语言发明时,已经存在相当多的用汇编语言编写的库和目标文件,这样就有一个问题,名称冲突。为了防止符号名冲突,UNIX下的C语言就规定,C语言源码文件中的所有全局的变量和函数经过编译后都在符号名字前加上下划线_,而Fortran语言的源码经过编译后,前后都加下划线。这种原始的方法,降低了冲突的概率,但并未完全解决问题,比如当不同的部门开发不同模块时,如果规范不当就可能导致冲突。C+这样后来设计的语言就考虑到了这个问题,用名称空间解决多模块符号冲突。随着时间推移,Linux的GCC默认情况下已经取消了加_的做法。C+允许函数重载,即名称相同,参数不同,如func(int)和func(double),为了实现此特性,人们发明了符号修饰和符号改编。每一个函数都会有一个函数签名,包含函数名字、参数类型、所在类、名称空间等。在编译器及链接器处理符号时,采用名称修饰,即利用签名元素将符号名加以修改,以解决冲突。签名和名称修饰的机制不光用在函数上,C+的全局变量和静态变量也有相同的机制。由于不同的编译器采用不同的名字修饰方法,必然导致了他们产生的目标文件无法正常的互相链接,这是导致不同编译器之间不能相互操作的主要原因。9、强符号与弱符号、强引用与弱引用多个目标文件中含有相同名字的全局符号的定义,那么这么目标文件链接的时候会出现符号重复定义的错误。对于C/C+来说:编译器默认函数和初始化了的全局变量为强符号未初始化的全局变量为弱符号。我们也可以通过GCC的_attribute_(weak)来定义任何一个强符号为弱符号。强弱都是由定义决定的。如:Extern int extInt weak;Int strong=1;_attribute_(weak) weak2=2;Int main()return 0;Weak和weak2是弱符号,strong和main是强符号。Ext属于外部变量引用,既非强也非弱。编译器对强弱符号的处理规则: 不允许强符号多次定义,否则报错 如果一个符号在某个目标文件中是强符号,在其它的文件中都是弱符号,那么选择强符号。 如果一个符号在多个文件中都是弱符号,那么选择其中占空间最大的。强引用与弱引用我们对外部文件的符号引用在链接成可执行文件时,如果没有找到该符号的定义,链接器就会报符号未定义错误。这种被成为强引用。与之对应的是弱引用。处理弱引用,如果该符号未被定义,则链接时不会报错,默认为0,或者其它特殊值。但是执行的时候会有问题。我们使用_attribute_(weakref)来声明对一个外部函数的引用为弱引用。如attribute_(weakref) void foo();int main()foo();链接的时候不会报错,但是运行这个可执行文件的时候就有错误了。因为当函数试图调用这个函数的时候,foo函数的地址为0,发生了非法访问错误。改进一下main函数中的调用:If(foo) foo();弱符号和弱引用对库来说非常有用,比如库中定义的弱符号,可以被用户定义的强符号所覆盖,从而自己定义自己的库函数。对某些扩展功能模块的引用定义为弱引用,当我们将模块与程序链接在一起的时候,功能就可以正常使用;如果我们去掉了某些模块,链接的时候也不会出错,只是缺了相应的功能,这使程序容易裁剪。10、调试信息:常见的情景是,build工程的时候,target会有两种版本release版本和debug版本,release版本就是纯净版,没有加入调试信息,而且是经过优化的,文件规模比较小。Debug是添加了调试信息的,而且是没有经过优化的。想想也是,没有可执行文件内部的支持,怎么可能实现单步调试和交互呢,我在屏幕设定要watch一个变量,调试器从可执行文件中找到这个变量,并把值读出来,传给上位机;我单步,源码走一行,调试器会解析是执行到目标代码的哪个地址,然后将PC赋值。现在几乎所有的现代编译器都支持源码级别的调试,比如我们可以在函数里设置断点,可以监视变量变化等。前提是编译器必须提前将源码与目标代码之间的关系,比如目标代码中的地址对应源码中的哪一行、函数和变量的类型、结构体的定义、字符串保存到目标文件里。我们在GCC编译时加上-g参数,编译器就会在目标文件中加上debug信息。使用readelf等工具查看,可以看到目标文件中多了很多与debug相关的段。使用strip命令可以去掉ELF文件中的调试信息。11、用到的命令:File查看文件的基本信息Size查看目标文件各段的大小Objdump h查看文件各段的基本信息Objdump /-s d/查看文件各段的详细信息及代码段的反汇编 Objcopy I binary o elf32-i386 B i386 image.jpg image.o将文件编程一个目标文件_attribute_(section(“BAR”)void foo() 将函数或变量放到指定的段Readelf h查看ELF文件的文件头readelf S 可以查看段表的内容nm/readelf s /objdump t查看符号表这样我分析一个ELF文件,分析文件头可以知道文件属性,可以找到段表,通过段表可以找到符号表,通过符号表可以知道函数或变量在哪个段和在该段的偏移,通过段表又能找到这个段。12 动态存储变量和寄存器变量我们看到,在源文件中的局部动态变量a,b是没有体现的,他们的空间是在运行时分配的存储在动态存储区的变量叫动态存储变量。它们在程序执行的某一时刻被动态地建立并在另一时刻又被动态地撤消,即使用到时为其分
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025粮油食品检验人员模考模拟试题附答案详解【研优卷】
- 脑梗塞静脉取栓护理查房
- 2026届安徽省合肥市肥西县化学九年级第一学期期中质量跟踪监视试题含解析
- 内蒙古通辽市科尔沁左翼中学旗县2026届九年级英语第一学期期末达标检测试题含解析
- 义务均衡发展培训
- 广东省佛山禅城区七校联考2026届英语九上期末综合测试试题含解析
- 幼儿园指导纲要解读培训
- 2026届辽宁省沈阳市大东区化学九上期末学业水平测试模拟试题含解析
- 2026届安徽省砀山县化学九上期末调研模拟试题含解析
- 2026届北京六十六中学化学九年级第一学期期中学业质量监测模拟试题含解析
- 水域救援知识课件
- GB 31604.60-2024食品安全国家标准食品接触材料及制品溶剂残留量的测定
- 新国际政治学概论(第三版)-教学课件-陈岳-109503国际政治学概论(第三版)
- XX医院DRG绩效分配方案
- 《研究生英语》(第二版)练习答案及译文
- 加油船租赁油船租赁合同
- 《茶叶审评技术》课程考试复习题库(含答案)
- 专题四“挺膺担当”主题团课
- 智能高速铁路概论-课件-第一章-世界智能铁路发展-
- 部编人教版五年级上册语文 第三单元单元分析
- 空间向量及其运算练习题
评论
0/150
提交评论