elf详解
电子定域函数_ELF_理论方法简介_一种描述化学键的新模型

知识介绍电子定域函数(E LF)理论方法简介———一种描述化学键的新模型郑世钧1 李晓艳13 默丽欣2(1河北师范大学化学与材料科学学院计算量子化学研究所 石家庄 050016;2河北师范大学汇华学院 石家庄 050091)摘 要 电子定域函数理论可以清晰和定量化描述理论化学研究的基本对象———化学键,已在原子、分子及固体体系中得到广泛应用。
本文系统介绍了电子定域函数(E LF)理论方法的基本物理思想;对E LF函数的一些特点如数值稳定性、E LF与分子中的原子理论(A I M理论)方法的异同及E LF的拓扑分析进行了说明;对电子定域函数在研究原子壳层结构、化学键的性质、孤对电子的位置、反应过程化学键的断裂和生成等方面的应用进行了介绍。
关键词 电子密度拓扑分析 电子定域函数 化学键I n troducti on to Electron L oca li za ti on Functi on(EL F)———A New Chem i ca l Bond M odelZheng Shijun1,L i Xiaoyan13,Mo L ixin2(1I nstitute of Co mputati onal Quantu m Che m istry,College of Che m istry and Material Science,Hebei Nor mal University, Shijiazhuang050016;2College of Huihua,Hebei Nor mal University,Shijiazhuang050091)Abstract The Electr on Localizati on Functi on(E LF)which can give a clear and quantitative descri p ti on on the basic che m ical concep t–the che m ical bond has been app lied t o vari ous syste m s fr om at om t o molecules and s olids widely.The basis theory and s ome characters of E LF,such as the nu merical stability,the anal ogies and differences with A I M and t opol ogical analysis of E LF are intr oduced.The app licati ons of E LF in the studies of at om ic electr on structure,the natural of che m ical bond,the l one pair regi on,the breakage and f or mati on of the che m ical bonding al ong the reacti on p r ocess are als o discussed.Keywords Topol ogical analysis of electr on density,E LF,Che m ical Bond化学家通过应用量子力学可以获得与实验对照的可观测量的数值并帮助加深对化学基本概念的理解。
ELF分析

4.2.1可执行文件ELF分析1.在Linux下ELF文件主要有三种类型阴:(1)可重定位文件(RelocatableFile)包含适合于与其他目标文件链接来创建可执行文件或者共享目标文件的代码和数据"(2)可执行文件(ExecutableFile)包含适合于执行的一个程序,此文件规定了exec()如何创建一个程序的进程映像"(3)共享目标文件(SharedObjectFile)包含可在两种上下文中链接的代码和数据"首先链接编辑器可以将它和其它可重定位文件和共享目标文件一起处理,生成另外一个目标文件"其次,动态链接器(DynamicLinker)可能将它与某个可执行文件以及其它共享目标一起组合,创建进程映像"2.ELF文件的组织结构文件ELF参与程序的连接(建立一个程序)和程序的执行(运行一个程序),编译器和链接器将其视为节头表(seetionheadertable)描述的一些节(seetion)的集合,而加载器则将其视为程序头表(programheadertable)描述的段(segmellt)的集合,通常一个段可以包含多个一节"可亚定位文件都包含一个节头表,可执行文件都包含一个程序头表"共享文件两者都包含有"为此,ELF文件格式同时提供了两种看待文件内容的方式,反映了不同行为的不同要求"下图4.1显示了ELF文件的组织结构"Elf头在程序的开始部位,作为引路表描述整个ELF的文件结构,其信扁!大致分为四部分:一是系统相关信息, 二是目标文件类型, 三是加载相关信息, 四是链接相关信息其中系统相关信息包括elf文件魔数(标识elf文件),平台位数,数据编码方式,elf头部版本,硬件平台emaehine,目标文件版本eversion,处理器特定标志e_ftags:这些信息的引入极大增强了elf文件的可移植性,使交叉编译成为可能"目标文件类型用e一yPe的值表示, 可重定位文件为1, 可执行文件为2, 共享文件为3; 加载相关信息有:程序入口e_entry,程序头表偏移量e夕hoff,elf头部长度e_ehsize,程序头表中一个条目的长度e夕hentsize,程序头表条目数目e一hnum;链接相关信息有:节头表偏移量e_shoff,节头表中一个条目的长度eShentsize,节头表条目个数eShnum,节头表字符索引eShstmdx"可使用readelf一hfilename来察看文件头的内容"程序头表(Programheadertable)告诉系统如何建立一个进程映像,它是从加载执行的角度来看待elf文件"从它的角度来看,elf文件被分成许多段,elf文件中的代码!链接信息和注释都以段的形式存放"每个段都在程序头表中有一个表项描述,包含以下属性:段的类型,段的驻留位置相对于文件开始处的偏移,段在内存中的首字节地址,段的物理地址,段在文件映像中的字节数,段在内存映像中的字节数,段在内存和文件中的对齐标记"可用readelf一1filename察看程序头表中的内容"节头表(sectionheadertable)描述程序节,为编译器和链接器服务"它把elf文件分成了许多节,每个节保存着用于不同目的的数据,这些数据可能被前面的程序头重复使用,完成一次任务所需的信息往往被分散到不同的节里"由于节中数据的用途不同,节被分成不同的类型,每种类型的节都有自己组织数据的方式"每一个节在节头表中都有一个表项描述该节的属性,节的属性包括小节名在字符表中的索引,类型,属性,运行时的虚拟地址,文件偏移,以字节为单位的大小,小节的对齐等信息,可使用:eadelf一5filename来察看节头表的内容"3.ELF文件的加载过程相对可执行文件有三个重要的概念:编译(comPile)!连接(1ink,也可称为链接!联接)!加载(load)"源程序文件被编译成目标文件,多个目标文件被连接成一个最终的可执行文件,可执行文件被加载到内存中运行"因为本文重点是讨沦可执行文件格式,因此加载过程也相对重点讨论"下面是LINUX平台下ELF文件加载过程的一个简单描述[39]"(1)内核首先读ELF文件的头部,然后根据头部的数据指示分别读入各种数据结构,找到标记为可加载(loadable)的段,并调用函数mmapo把段内容加载到内存中"在加载之前,内核把段的标记直接传递给mmapo,段的标记指示该段在内存中是否可读!可写,可执行"显然,文本段是只读可执行,而数据段是可读可写"这种方式是利用了现代操作系统和处理器对内存的保护功能"著名的Shdlcodc的编写技巧则是突破此保护功能的一个实际例子"(2)内核分析出ELF文件标记为PT--INTERP的段中所对应的动态连接器名称,并加载动态连接器"现代L州UX系统的动态连接器通常是/lib/ld一linux.so.2, 相关细节在后面有一详细描述"(3)内核在新进程的堆栈中设置一些标记一值对,以指示动态连接器的相关操作"(4)内核把控制传递给动态连接器"(5)动态连接器检查程序对外部文件(共享库)的依赖性,并在需要时对其进行加载"(6)动态连接器对程序的外部引用进行重定位,通俗的讲,就是告诉程序其引用的外部变量/函数的地址,此地址位于共享库被加载在内存的区间内"动态连接还有一个延迟定位的特性,即只在真正需要引用符号时才重定位,这对提高程序运行效率有极大帮助"(7)动态连接器执行在ELF文件中标记为.init的节的代码,进行程序运行的初始化"在早期系统中,初始化代码对应函数_init(Void)(函数名强制固定),在现代系统中,则对应形式为!oid_attribute((eonstruetor))ini仁funetion(void){,,}其中函数名为任意"(8)动态连接器把控制传递给程序,从ELF文件头部中定义的程序进入点开始执行"在a.out格式和ELF格式中,程序进入点的值是显式存在的,在COFF格式中则是由规范隐含定义"4.2.3传统加壳方法分析从上面的分析可以看出,在ELF文件的加载过程中,首先将文件读入内存,然后根据ELF头部表查找文件的入口地址,找到入口地址然后执行程序,传统的加壳方式是:将程序的入口地址修改成另外一个地址,一般是ELF文件的某些空闲节区,然后执行反跟踪分析处理,如果发现可执行程序被跟踪调试则退出,否则继续执行代码段解密工作,解密完成后转入正常的程序入口地址执行"下图4.3 是传统加壳方法的简单示意图"当系统将加壳后ELF文件读入内存中,加壳后的ELF文件的处理流程如下: (1)读取程序的入口地址,该入口地址是修改后的入口地址(处于B位置),真正的入口地址被放置在D位置位置"(2)程序跳转到B位置执行"(3)执行后面的常用处理例程,例如反跟踪检测和代码混淆等,如果这些检测通过,则解密程序的主体(A部分),接着顺序执行到D位置"(4)根据D位置存放的真正程序入口地址,然后跳转到程序主体部分,运行解密后的真正程序"传统的方法有很大的局限性,首先外壳程序(就是常用处理例程)是存放在ELF文件空闲区域,但并不是每个文件的空闲区域都是相同的,如果外壳程序功能较多,那就意味着需要更多的空闲区域,很有可能会出现空闲区域不足的情况"其次在对ELF文件进行加壳处理过程中,必须对ELF格式非常熟悉且谨慎处理,微小的疏忽就将导致加壳程序或者加壳后的程序无法运行的情况"同时传统加壳方式的扩展性不好,很难具有通用性"4.3一种新的ELF加壳方法针对上面对于加壳原理的分析和常用加壳技术的了解,在这里提出了一种新的加壳方法即:SRELF(ShellingandRe一eonstruetedExeeutableandLinkil:9Forlnat)" SRELF方法的最大特点就是R(重新构造)"该方法是将目标elf文件中的核心部分提取出来,再和准备好的解密或解压缩程序,反静态分析和反动态跟踪等程序结合在一起,按照e!f文件标难格式一重新构造一个全新的clf可执行文件"它将加壳的各项功能程序和目标程序很好的融合为一体"4.3.1SRELF方法的设计在这里SRELF加壳程序将采用多层次加密!强密码保护机制和内存中可执行文件解密的方式来确保ELF文件的安全性"SRELF加壳方法需要实现下而的功能: (l)防止系统调用跟踪(SystemCallTraeing)"(2)防止库调用跟踪(LibraryCallTraeing)"(3)防1卜程序执行路径跟踪(ExeeutionpathTraeing)"(4)防止应用级调试跟踪(ApplieationLevelDebugging)"(5)防止内存镜像(Memo仃Dumping)"(6)关键数据加密(Ene仃ptimportantcode)"从上面可以看出SRELF程序需要解决6类问题,上述问题的解决思路如下:(l)针对功能1到4"在Linux下实现跟踪的原理都是基于基于Ptrace()调试的API,所以只要使得这个API函数对于加密的二进制文件无效,这样就将会有效阻止对于程序的跟踪分析,采用这些工具将很难攻击加壳的ELF程序"(2)针对内存镜像是基于这样的设想,对程序进行内存镜像的前提条件是:在内存中运行的程序必须是明文方式,所以可采用加壳程序使程序运行时部分解密来实现"(3)如何解决关键数据加密"系统采用加密算法对于ELF的核心部分进行加毖1.加壳程序的文件结构设计采用SRELF加密后的文件结构如下所示:如上图所示,S旺LF的结构其实是:=SRELF头8=加密后的程序>"其中S贬LF头由ELF头!程序头和SRELF常用处理例程这三部分组成"在编译SRELF命令时,生成了一个很大的数组s灿ea走binll,其实就是一个通用的ELF头十程序头十SRELF常用处理例程,当加密程序的时候,只需要对数组里的某些字段稍作修改即可"其中SR卫LF的常用处理例程采用分层结构,分别由代码混淆层!反跟踪分析层和块加密层组成"2.SRELF的加解密过程(l)加密过程sRELF首先以加密的文件作为对象,将S灿ead_bin[1作为加密后文件的ELF头十程序头十SRELF常用例程,然后按照SRELF的命令选项,对文件进行逐层的处理,分别是代码混淆!反跟踪分析和块加密处理"对于每层的处理,都会在SRELF头中保存相应的标志和信息(例如加密时的随机序列,shal哈希值),然后将结果放到最后一部分中"在这里采用主机的某些特征(fillgerprillt)和密码作为密钥KEY,首先算出密钥的SHAI哈希值,然后从随机发生序列器中获得160位的随机数,并与哈希值异或得到KEYZ,然后用KEYZ对-需要程序进行RC4加密"将加密之前的明文SHAI 哈希值保存到Srhead头的相应部分,这样做密码验证时就能够判断密码的正确性"(2)解密过程首先去掉第一层保护,接下来处理运行程序的栈结构,并将系统控制权交给SRELF的处理函数"处理函数会对反跟踪分析进行处理,如果通过的话接下来是fingcrprint的检测,如果通过检验就将加密的程序解密,然后调用s贬LF中自带的Loader,将解密后的程序映射一到内存中,如果解密的程序需要inte甲rcter,loader 也会负责将interp咒ter装入到内存中,最后将控制权交给illterpreter或者解密后的FIF"3.SRELF的加载器(Loader)由于解密以后的程序与SRELF的例程函数是位于同一进程空间的,因此如果想要执行解密后的程序,例程就需要自己装载程序,在一般情况下程序是由内核来完成装载任务的"SRELF是通过bejemap来实现的,如果加密之前的程序是动态链接的,还需调用be皿apinte印reter来装载解释器(inte印reter)#4.SRELF的实现流程图下图4.5是SRELF的实现流程图"图4.5SRELF方法实现程序框图4.3.2SRELF方法性能分析1.安全性首先SRELF的方法理论上可以使用任意一种加密或压缩算法来对程序.text段的二进制代码进行加密或压缩"从加解密角度来说,SRELF的安全性比其他方法要高"其次SRELF方法没有对加壳功能程序的大小进行限制,所以在二进制代码中可以插入足够的花指令,并加上高复杂度的加密或压缩算法,想要进行静态的分析的难度会极大"再次SRELF方法是通过重构elf文件,完全改变了elf文件中的内容,在Start位置上的内容将不再是原来的内容,这对想要通过静态分析代码来破解软件的破解者来说又增加了难度"SRELF对反动态跟踪也给予了充分的支持"和反静态跟踪一样,SRELF在其中加入了充分的反动态跟踪指令,想当与对elf文件又增加了一层保护措施"2.透明性当加壳的elf文件运行时,由于解码的过程是在内存中完成的,所以对于用户来说是感觉不到程序被加了保护壳,这个过程对用户来说是透明的"3.伪装性一般加壳后的可执行文件在结构和大小上是有一定程度变化的"经过SRELF方法加壳后的elf文件仍然保持了elf文件默认的文件结构,所以大多数人都是无法看出加壳前后"1f文件的区别"而且SRELF不需要改变程序入口地址,这样即便是使用特定工具也分辨不出来"就像elf文件没有经过特殊处理,这样给加壳后的可执行文件增加了伪装"4.扩展性由于SRELF理论上没有对壳程序大小进行限制,这使得它具备了良好的扩展性"因此随着技术的史新,可以同步更新SRELF中的加密方式!反静态分析和反动态跟踪等方法,这样SRELF将具有良好的发展性,不会迅速被淘汰"5.3!5软件保护模块第四章提出了SRELF加壳方法,该方法重点在于/提取目标文件中的核心部分0和/将目标文件的核心部分和加壳部分整合0这两个部分功能的实现"所以在此,主要阐述这两部分的实现方法"1.提取}8标文件中的核心部分核心部分包括可执行指令,动态链接表等,段或节的信息"提取这些部分也就是将这些部分单独复制出来,用以后面代码整合中使用"这里以.text节为例,说明如何提取核心部分"该节保存着程序的币文或者说是可执行指令,可以说它是这部分的核心"根据elf文件头中的信息,首先定位到程序头表"在程序头表中再定位到代码段,整个代码段都是需要的核心部分"这时再定位到.text位置,将其中的数据代码复制出来"其他段或节都以同样的方法获得"这里只给出定位.text 的程序代码"V oidGetElfCore(FILE*fP)/*获取程序头表和节头表的起始地址*/Phdr=(EI仍2_Phdr*)((unsignedlong)ehdr+ehdr一>e夕ho均:shdr=(EI招2_Shdr*)((unsignedlong)ehdr+ehdr一>屯sho均;/*定位文本段*/while(l){if(判断是否是文本段){不是文本段,则将指针指向下一个段}Break;刀若是文本段,跳出循环2.将目标文件的核心部分和加壳部分整合这部分功能是整个SRELF的核心"它不是简单的将各个程序段或节一一对应相加"它需要进行精确的计算,来正确的设置elf文件头,程序头表,节头表等的数据"这样新生成的elf文件才能正常运行"这里还是以.text节为例"首先要分别计算出加壳部分.text节占物理文件的大小和载入内存所占内存的大小,目标文件核心部分.text节占物理文件的大小和载入内存所占内存的大小"然后将目标文件核心部分.text节内容续写到加壳部分.text节的后面"再将程序头表,节头表中.text 属性中与大小相关的参数对应设置成新计算出的大小"到此为止,.text部分的整合完成"其他段或节都以同样的方法整合"这里只给出.text的整合伪代码" VOidIntegrate(char*sre,ehar*des){刀通过GetElfCore函数将目标文件的.text段的地址赋值给Src//将准备好的壳程序的.text段的地址赋值给desxntegrate_segment(ehar*sre.ehar*des);//将目标程序的.text段续写在壳程序的.text段之后Set_EI仁Attribute(ehar*sre,ehar*des);//重新设置elf头和程序段表和节表中的参数.............}。
elf 结构解析

elf 结构解析一、ELF文件的基本结构1. ELF文件头(ELF Header):ELF文件的入口点,包含了文件的基本信息和描述,如魔数、文件类型、入口地址等。
2. 程序头表(Program Header Table):描述了ELF文件的各个段(Segment)的加载和运行时的属性,如可执行代码段、数据段、只读段等。
3. 节头表(Section Header Table):描述了ELF文件的各个节(Section)的属性和位置信息,如代码节、数据节、字符串节等。
4. 节区(Section):包含了ELF文件的各种数据和代码,如程序的实际指令、全局变量、局部变量等。
5. 字符串表(String Table):存储了ELF文件中使用的所有字符串,如符号表中的符号名、节名等。
6. 符号表(Symbol Table):记录了ELF文件中定义和引用的各个符号的信息,如函数、变量等。
7. 重定位表(Relocation Table):用于修正程序中的跳转地址,使得程序可以正确地链接和执行。
二、ELF文件加载和链接过程1. 加载:当操作系统执行一个ELF可执行文件时,将会执行加载过程。
操作系统会按照程序头表中的描述,将各个段加载到内存中的相应位置,建立虚拟地址和物理地址之间的映射关系。
2. 符号解析:在加载过程中,操作系统会解析符号表,建立符号名与符号地址之间的映射关系。
这样,在程序执行过程中,可以通过符号名来访问对应的函数或变量。
3. 重定位:由于不同的目标文件可能会引用其他目标文件中的符号,所以在加载过程中,操作系统会根据重定位表的信息,修正程序中的跳转地址,确保程序可以正确地链接和执行。
三、ELF文件的应用1. 可执行文件:ELF格式的可执行文件可以直接在操作系统中运行,例如Linux系统中的可执行文件就是以ELF格式存储的。
2. 共享库:ELF格式的共享库包含了一组可被多个可执行文件共享的代码和数据,可以在程序运行时动态加载和链接。
elf格式文件解析

ELF可执行文件的解析与加载ELF文件格式简介1.1前言ELF-可执行链接格式最初是由UNIX系统实验室(USL)作为应用程序二进制接口(ABI)开发和发行。
工具接口标准委员会TIS已经将ELF作为运行在Intel32位架构之上的各类型操作系统的可导出对象文件格式标准。
ELF标准为开发者提供了一组横跨多运行环境的二进制接口定义来组织软件开发。
1.2对象文件1.2.1 介绍本部分描述了ABI对象文件格式,也称之为ELF。
有三种主要类型的对象文件:1. 可重组(relocatable)文件包含了适合用来链接其他对象文件的代码和数据,从而创建出可执行或可共享的对象文件;2. 可执行(executable)文件包含了用于执行的程序,该文件规定了exec如何创建一个程序的进程映像;3. 可共享对象(shared object)文件包含了用来在两个上下文之间链接的代码和数据。
首先,链接器ld将该文件和其他的可重组文件或可共享对象文件进行处理后,创建出新对象文件,其次,动态链接器将该新对象文件与可执行文件或共享对象组合,来共同创建一个进程映像;经过汇编器以及链接器创建成的对象文件,其是在处理器上可直接执行的程序的二进制代表。
本部分主要描述文件格式以及其如何用来构建程序。
后一部分也描述了对象文件,集中在程序执行所必须的信息上。
1.2.1.1 文件格式在程序链接和程序执行过程都涉及到对象文件。
出于方便和效率,对象文件格式图从链接和运行两个视角来展示文件的内容。
ELF header位于文件的开始处,其用来描述文件的组织结构。
Section包含了大量的对象文件信息,从链接的视角来看就是指令、数据、符号表、重组信息等等。
Segment和Program是从程序执行视角来观看的,这将在下部分讲解。
如果存在Program Header table的话,其将告诉操作系统如何创建进程映像。
用来创建进程映像(执行程序)的文件必须包含program header table。
elf函数

elf函数ELF是英文Executable and Linkable Format的缩写,即可执行和可链接格式。
它是一种通用的二进制文件格式,常用于Linux和UNIX类操作系统以及其他一些嵌入式系统中。
对于Linux来说,ELF格式的可执行文件是Linux系统下常见的一种可执行文件格式。
ELF文件由多个节(Section)组成。
每个节都有一个唯一的名称,用来描述程序中的一种特定类型的数据。
主要的节类型包括.text、.data、.bss以及.symtab等。
每种节类型都有自己的作用和特定的内容。
.text节是可执行文件中最重要的节,它包含了程序的代码。
在Linux系统中,函数的执行是由.text节中的代码驱动的。
.data节用于存储一些静态的全局变量,.bss节则用于存储一些未初始化的全局变量。
.symtab节是符号表,用于记录程序中的所有符号,包括函数和变量等等。
在ELF文件中,函数的定义和调用是通过符号表来实现的。
符号表中记录了所有的符号信息,包括符号名称、符号类型、符号值等等。
函数被定义时,在符号表中会被记录一个符号类型为函数的符号。
而函数被调用时,程序会在符号表中查找该函数的符号信息,并跳转到该函数的代码地址开始执行。
在ELF文件中,具体的函数实现是由函数的代码段来实现的。
函数的执行流程是先把函数的参数压入栈中,然后通过调用指令跳转到函数代码段的入口处开始执行。
在函数执行过程中,栈用于存储函数的局部变量、临时变量以及一些返回值等等。
函数的返回是通过返回指令来实现的。
在函数执行完成后,程序会使用返回指令返回到函数调用点的下一条指令处。
如果函数有返回值,那么返回值也会存储在寄存器或者栈中,并在返回时传递给函数调用点。
ELF文件不仅可以包含可执行代码,还可以包含动态链接库和静态链接库。
动态链接库在程序运行时才会被加载,而静态链接库是在编译时被链接到程序中的。
使用静态链接库可以使程序的可执行文件更小,但动态链接库可以使多个程序共享库中的代码,从而减少程序运行时的内存占用。
ELF文件格式分析

ELF文件格式分析ELF(Executable and Linkable Format)是一种常见的可执行文件格式,用于在UNIX和类UNIX系统上存储和执行程序。
在本文中,我们将详细分析ELF文件格式的结构和各个部分的作用。
首先是标识部分,它占据ELF文件的最开始位置,有以下几个字段:1. Magic Number:ELF文件的前四个字节,用于识别文件类型。
对于32位系统,它的值是0x7F、'E'、'L'和'F';对于64位系统,它的值是0x7F、'E'、'L'和'F'以及0x022.类型:指定了ELF文件的类型,比如可执行文件、目标文件、共享库等等。
3.类型机器:指定了目标机器的体系结构,如x86、ARM等等。
4.版本:指定了ELF文件的版本号。
5.入口点:对于可执行文件,指定程序的入口点。
6.程序头表和节头表的偏移地址和大小。
接下来是文件头部分,它描述了ELF文件的整体结构和布局:1.类型:表示ELF文件的类型,如可重定位文件、可执行文件、共享目标文件等。
2.目标机器:标识目标机器的类型。
3.版本:指定了ELF文件的版本号。
4.入口点:指定程序的入口点。
5.程序头表偏移地址和节头表偏移地址。
6.程序头表项的大小、数量和节头表项的大小、数量。
7.字符串表索引,用于找到文件中的字符串。
最后是节头表部分,它包含了所有节(Section)的相关信息,比如代码段、数据段等等。
每个节头表项包含以下字段:1.节的名称:用于标识节的名称。
2.节的类型:描述节的属性,如代码段、数据段、符号表等。
3.节的标志:描述节的属性,如可写、可执行等。
4.节的虚拟地址:指定节在内存中的虚拟地址。
5.节的文件偏移地址:指定节在文件中的偏移地址。
6.节的大小:指定节的大小。
7.链接和信息:用于指定其他与节相关的信息。
通过分析ELF文件的结构,我们可以获得有关程序入口点、机器类型、节的信息等重要的元数据。
计算机操作系统实验_解析ELF文件

西北工业大学操作系统实验实验报告一、实验目的熟悉可执行链接文件(ELF)的结构,了解GeekOS将ELF格式的可执行程序加载到内存,建立内核线程并运行的实现技术。
二、实验要求1.修改Project1项目中的/GeekOS/elf.c文件:在函数Parse_ELF_Executable()中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头),获取可执行文件长度、代码段、数据段等信息,并打印输出。
并且,填充Exe_Format 数据结构中的值域。
2.掌握GeekOS在核心态运行可执行程序的原理,绘制出可执行程序在内核中加载、运行的流程图(需反映关键函数的调用关系)。
3.回答实验讲义P125页的思考题。
三、实验过程及结果1、修改Project1项目中的/GeekOS/elf.c文件:在函数Parse_ELF_Executable()中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头),获取可执行文件长度、代码段、数据段等信息,并打印输出。
并且,填充Exe_Format 数据结构中的值域。
答:修改Project1项目中的/GeekOS/elf.c文件:在函数Parse_ELF_Executable()中添加代码,如下:==============elf.c===================int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength, struct Exe_Format *exeFormat){int i;elfHeader *head=(elfHeader*)exeFileData;programHeader *proHeader=(programHeader *)(exeFileData+head->phoff);KASSERT(exeFileData!=NULL);KASSERT(exeFileLength>head->ehsize+head->phentsize*head->phnum);KASSERT(head->entry%4==0);exeFormat->numSegments=head->phnum;exeFormat->entryAddr=head->entry;for(i=0;i<head->phnum;i++){exeFormat->segmentList[i].offsetInFile=proHeader->offset;exeFormat->segmentList[i].lengthInFile=proHeader->fileSize;exeFormat->segmentList[i].startAddress=proHeader->vaddr;exeFormat->segmentList[i].sizeInMemory=proHeader->memSize;exeFormat->segmentList[i].protFlags=proHeader->flags;proHeader++;}return 0;}==============elf.c===================运行结果如图:2、掌握GeekOS在核心态运行可执行程序的原理,绘制出可执行程序在内核中加载、运行的流程图(需反映关键函数的调用关系)。
LinuxELF详解2--SectionHeaderSection【转】

LinuxELF详解2--SectionHeaderSection【转】ELF Section Header & Section先看 Section Header 的定义typedef struct {Elf32_Word sh_name;Elf32_Word sh_type;Elf32_Word sh_flags;Elf32_Addr sh_addr;Elf32_Off sh_offset;Elf32_Word sh_size;Elf32_Word sh_link;Elf32_Word sh_info;Elf32_Word sh_addralign;Elf32_Word sh_entsize;} Elf32_Shdr;typedef struct {Elf64_Word sh_name; // 4 B (B for bytes)Elf64_Word sh_type; // 4 BElf64_Xword sh_flags; // 8 BElf64_Addr sh_addr; // 8 BElf64_Off sh_offset; // 8 BElf64_Xword sh_size; // 8 BElf64_Word sh_link; // 4 BElf64_Word sh_info; // 4 BElf64_Xword sh_addralign; // 8 BElf64_Xword sh_entsize; // 8 B} Elf64_Shdr; // total size: 64 B我们只关注 Elf64_Shdr(64位系统的定义)。
⽤ readelf 查看 program.o 的 Section Header 列表。
# -S是查看Section Header,-W是拓展显⽰的宽度$ readelf -SW program.oThere are 14 section headers, starting at offset 0x418:Section Headers:[Nr] Name Type Address Off Size ES Flg Lk Inf Al[ 0] NULL 0000000000000000 000000 000000 00 0 0 0[ 1] .text PROGBITS 0000000000000000 000040 000051 00 AX 0 0 1[ 2] .rela.text RELA 0000000000000000 0002d8 0000a8 18 I 12 1 8[ 3] .data PROGBITS 0000000000000000 000098 000010 00 WA 0 0 8[ 4] .rela.data RELA 0000000000000000 000380 000018 18 I 12 3 8[ 5] .bss NOBITS 0000000000000000 0000a8 000000 00 WA 0 0 1[ 6] .rodata PROGBITS 0000000000000000 0000a8 000013 00 A 0 0 1[ 7] .comment PROGBITS 0000000000000000 0000bb 000036 01 MS 0 0 1[ 8] .note.GNU-stack PROGBITS 0000000000000000 0000f1 000000 00 0 0 1[ 9] .eh_frame PROGBITS 0000000000000000 0000f8 000038 00 A 0 0 8[10] .rela.eh_frame RELA 0000000000000000 000398 000018 18 I 12 9 8[11] .shstrtab STRTAB 0000000000000000 0003b0 000066 00 0 0 1[12] .symtab SYMTAB 0000000000000000 000130 000180 18 13 10 8[13] .strtab STRTAB 0000000000000000 0002b0 000024 00 0 0 1Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), l (large)I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)重新看⼀下 ELF Header$ readelf -h program.oELF Header:...Start of section headers: 1048 (bytes into file)...Size of section headers: 64 (bytes) #跟 Section Header 定义中的 total size ⼀致Number of section headers: 14 #跟 Section Header 列表中的 Header 的个数⼀致Section header string table index: 11接下来从字节级别查看⼀下这些 Section Header 的内容。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
ARM-ELF文件格式与GNU ARM Linker机制作者:admin 日期:2008-10-13字体大小: 小中大里面除了二进制的机器代码,还有一些可用于进行重定位的信息。
它主要是作为LINKER(ld)的输入,LINKER将跟据这些信息,将需要重定位的符号重定位,进而产生可执行的OBJECT 文件。
ELF格式的可重定位OBJECT文件由header与section 组成。
Header 包括ELF header 与 section header. ELF header 位于文件的头部,用于存储目标机器的架构,大小端配置,ELF header 大小,object文件类型,section header 在文件中的偏移,section header 的大小,section header 中的项目数等信息。
Section header 则定义了文件中每个section 的类型,位置,大小等信息。
Linker就是通过查找ELF header,找到section header 的入口,再在section header 中找到相应的section 入口,进而定位到目标section 的。
Section 包括.text :经过编译的机器代码。
.rodata :只读的数据,例如printf(“hello!”)中的字符串hello。
.data :已初始化的全局变量,局部变量将在运行时被存放在堆栈中,不会在.data或 .bss段中出现。
.bss :未初始化的全局变量,在这里只是一个占位符,在object 文件中并没有实际的存储空间。
.symtab :符号表,用于存放程序中被定义的或被引用到的全局变量和函数的信息。
.rel.text :一个保存着一系列在.text中的位置的列表。
这些位置将在linker把这个文件与其它object文件合并时被修改,一般来说,这些位置都是保存着一些引用到全局变量或者外部函数的指令。
引用局部变量或者本地函数的指令是不需要被修改的,因为局部变量和本地函数的地址一般都是使用PC相对偏移地址的。
需要注意的是,这个section 和下面的.rel.data在运行时并不需要,生成可执行的ELF object文件时会去掉这个section。
.rel.data :保存全局变量的重定位信息。
一般来说,如果一个全局变量它的初始化值是另一个全局变量的地址,或者是外部函数的地址,那么它就需要被重定位。
.debug :保存debug信息。
.strtab :一个字符串表,保存着.symtab和.debug ,和各个section的名字。
.symtab,.debug 和section table里面,凡是保存name的域,其实都是保存了一个偏移值,通过这个偏移值在这个字符串表里面可以找到相应得字符串。
下面仔细讨论一下.symtab:每一个可重定位的object文件,都会有一个.symtab。
这个符号表保存了在这个object文件中所有被定义的和被引用的符号。
当源程序是C 语言程序时,.symtab 中的符号直接来源于C编译器(cc1)。
这里所说的符号主要有三种:1) 在这个object文件中被定义的可以被其他object文件全局符号。
在C语言源程序中,主要就是那些非静态(没有static 修饰的)的全局变量和非静态的函数。
在ARM汇编语言中,就是那些被EXPORT 指令导出的变量。
2) 在这个object文件中引用到,但是在其他文件中定义的全局变量。
在ARM汇编语言中就是通过IMPORT命令引入的变量3) 本地变量。
本地变量只在本object文件内可见。
这里的本地变量指的是连接器本地变量,应该和一般的程序本地变量作区别。
这里所指的本地变量,包括用static 修饰的全局变量,object 文件中section名称,源代码文件名称。
一般意义上的本地变量,是在运行时由系统的运行时环境管理的,linker并不关心。
每个符合上面条件的符号在.symtab文件中都会有一个数据项。
这个数据项的数据结构是:Typedef struct{int name;//符号名称,其实就是.strtab的偏移值int value;//在section中的位置,以相对section地址的偏移表示int size;//大小char type;//类型,一般是数据或函数char binding;//是本地变量还是全局变量char reserved;//保留的位char section;//符号所属的section。
可选有:.text(用数字1代表),.data(用数//3代表),ABS(不应被重定位的符号),UND (在本object文件//中未定义的符号,可能在别的文件中定义),COM(一般的未初//始化的变量符号)。
}ELF_sym现在假设组成应用的各个模块都已经被汇编,构建出了可重定位的object文件。
这些object的结构都是一样的,有各自的.text, .data section, 有各自的.symtab. GCC下一步要做的就是使用linker (ld),把这些object文件,加上必要的库连接成具有绝对运行时地址的可执行文件,就是可执行的ELF格式的文件。
Linker 的连接动作可以分为两部分:1) 符号解析。
确定引用符号的指向。
2) 符号重定位。
合并section, 分配运行时环境地址,引用符号重定位。
符号解析:在一个object文件中,有指令定义了符号,也有指令引用了符号。
可能存在这样一种情况,一个被引用到的符号,有多重的定义。
符号解析的作用就是确定,在这个object文件中,一个符号引用真正引用的是哪个符号。
在编译的时候,除了在本文件中定义的全局变量会由编译器生成一个符号表项之外,当发现一个被引用到的符号在本文件中并没有被定义,编译器也会自动产生一个符号表项,把确定这些引用的工作留给linker。
汇编器在汇编时将读取这些符号表项,生成.symtab。
在读取的过程中,如果发现有在无法确定的符号引用项,汇编器会为这些符号额外生成一个数据项,称作重定位数据项,存放于rel.text或rel.data section中,交由linker确定。
下面是重定位数据项(relocation entry)的数据结构:Typedef struct{int offset;//指明需要被重定位的引用在object中的偏移,实际上就是需要被重定位的引用//在object中的实际位置int symbol;//这个被重定位的引用真实指向的符号int type;//重定位类型:R_ARM_PC24:使用24bit的PC相对地址重定位引用//R_ARM_ABS32:使用32bit绝对地址重定位引用}Elf32_RelLinker 需要解析的,就是那些被生成了重定位数据项的引用。
Linker将根据C语言定义的规则,对于每一个重定位数据项,在输入的各个object文件中查找适合的符号,把这个符号填入symbol项中。
但是由于还不知道这个符号的真实地址,所以现在就算知道了引用的真实指向,但我们还是不能确定这个引用指向的地址。
符号重定位:符号重定位用来解决上面的问题。
Linker首先进行section 的合并。
Linker合并object文件的过程很简单,一般就是相同属性的section合并,例如不同object文件的.text section 将被合并成一个.text。
同样,.symtab section也被合并成一个.symtab。
这里面涉及到两个问题:1) 各个object文件合并的顺序。
这个问题涉及到最终指令和符号的运行地址。
最为重要的是,究竟是哪个section排在最前头?在ARM RAW 系统得开发过程中,这个最为重要。
ARM系统CPU上电后,系统会自动的从0x00000000地址取指令并执行,这个地址上映射着存储器。
这个动作是不可编程的。
所以排在最前面的section一定要包含有程序的入口点,否则系统无法正常运行。
2) 输入段与输出端之间的对应关系。
理论上,任何section,都可以被随意的映射到一个输出段中。
一个.data section是可以与一个.text section 组成输出一个.text的。
当然这样的动作毫无意义。
我们必须告诉linker使用那些section作为输入,产生一个输出section.以上这两个问题,都是通过一个称为连接脚本的文件控制的。
Linker通过读取连接脚本,来决定section 从输入到输出的映射,设置程序的入口点,设置哪个section应该在整个可执行文件的头部等问题。
连接脚本还有另外一个作用,那就是指定每个section的地址。
在section 合并完成后,linker将跟据.symtab,对符号进行统一的编址,分配一个绝对的运行时地址。
这个地址是以section地址作为基地址的。
假设.text section的地址是0x00000000,那么.text里面的符号将以0x00000000这个地址作为基准地址。
指定section地址的工作也是由连接脚本完成。
在嵌入式开发中常见的在编译工程时需指定的text_base, data_base等参数,最后会被加入到连接脚本中,从而完成section的地址分配。
以上两步完成后,linker 执行引用符号重定位操作。
Linker遍历.rel section (包括.rel text 和 .rel data),对于其中的每个数据项,根据symbol域到.symtab 中查出相应的引用的真实地址(经过上面的地址分配,现在.symtab里面的符号都具有绝对的运行地址),再根据offset域提供的偏移,将这个地址填入相应的位置上。
至此,符号重定位工作全部完成。
Linker删除用于保存重定位信息的rel.text和rel.data section,加入一个segment header和一个.init section。
生成可执行的ELF格式的object文件。
Segment header保存了用于操作系统内存映射的信息。
.init section 包含了一个_init 的函数。
程序加载时,操作系统的程序加载器通过读取segment header,将程序加载到用户内存空间,并根据segment header里面映射信息,分别将.text 段和.data段映射到适当的地址上。
然后再调用.init中的_init函数,完成初始化工作。
由于ELF文件具有通用性强的优点,现在流行的开发模式是,先通过编译工具生成ELF文件格式的可执行文件,在使用外部工具,抽离出ELF文件中的相应部分,生成BIN文件。
例如著名的GNU bootloader U-Boot,就采用了这种做法,编译器工具集是GCC,BIN生成工具是elf2bin。