zjhqlmzldx 2011-07-07
Linux中ELF格式文件介绍
ELF(ExecutableandLinkableFormat)即可执行连接文件格式,是一种比较复杂的文件格式,但其应用广泛。与linux下的其他可执行文件(a.out,cof)相比,它对节的定义和gnu工具链对它的支持使它十分灵活,它保存的足够了系统相关信息使它能支持不同平台上的交叉编译和交叉链接,可移植性很强.同时它在执行中支持动态链接共享库。
通过本文,可以大致了解Linux系统中ELF格式文件的分类,组成,作用,以及其中包含的内容。另外后面介绍了几种常用的对elf文件进行操作的工具,并且对其使用进行简单举例,便于对elf文件有一个比较直观的理解。
主要内容:
[描述]
1ELF文件简介
2ELF文件格式
3ELF的特性
[举例]
1readelf工具
2objcopy
3objdump
4nm
5ldd
[其它]
[描述]
1ELF文件简介
ELF(ExecutableandLinkableFormat)即可执行连接文件格式,是Linux,SVR4和Solaris2.0默认的目标文件格式,目前标准接口委员会TIS已将ELF标准化为一种可移植的目标文件格式,运行于32-bitIntel体系微机上,可与多种操作系统兼容。分析elf文件有助于理解一些重要的系统概念,例如程序的编译和链接,程序的加载和运行等。
(1)ELF文件类型:
有三种类型的ELF文件:
a)可重定位文件:用户和其他目标文件一起创建可执行文件或者共享目标文件,例如lib*.a文件。
b)可执行文件:用于生成进程映像,载入内存执行,例如编译好的可执行文件a.out。
c)共享目标文件:用于和其他共享目标文件或者可重定位文件一起生成elf目标文件或者和执行文件一起创建进程映像,例如lib*.so文件。
(2)ELF文件作用:
ELF文件参与程序的连接(建立一个程序)和程序的执行(运行一个程序),所以可以从不同的角度来看待elf格式的文件:
a)如果用于编译和链接(可重定位文件),则编译器和链接器将把elf文件看作是节头表描述的节的集合,程序头表可选。
b)如果用于加载执行(可执行文件),则加载器则将把elf文件看作是程序头表描述的段的集合,一个段可能包含多个节,节头表可选。
c)如果是共享文件,则两者都含有。
(3)ELF文件总体组成:
elf文件头描述elf文件的总体信息。包括:
系统相关,类型相关,加载相关,链接相关。
系统相关表示:elf文件标识的魔术数,以及硬件和平台等相关信息,增加了elf文件的移植性,使交叉编译成为可能。
类型相关就是前面说的那三个类型。
加载相关:包括程序头表相关信息。
链接相关:节头表相关信息。
下面对其进行了详细的介绍。
2ELF文件格式
2.1ELF文件的类型
ELF文件主要有三种类型:
(1)可重定位文件:包含了代码和数据.可与其它ELF文件建立一个可执行或共享的文件:
(2)可执行文件:是可以直接执行的程序:
(3)共享目标文件:包括代码和数据,可以在两个地方链接。第一,连接器可以把它和其它可重定位文件和共享文件一起处理以建立另一个ELF文件;第二,动态链接器把它和一个可执行文件和其它共享文件结合在一起建立一个进程映像。
2.2ELF文件的组织
ELF文件参与程序的连接(建立一个程序)和程序的执行(运行一个程序),编译器和链接器将其视为节头表(sectionheadertable)描述的一些节(section)的集合,而加载器则将其视为程序头表(programheadertable)描述的段(segment)的集合,通常一个段可以包含多个节。可重定位文件都包含一个节头表,可执行文件都包含一个程序头表。共享文件两者都包含有。为此,ELF文件格式同时提供了两种看待文件内容的方式,反映了不同行为的不同要求。
从链接的角度看,ELF文件从开始到结束,可以看成是如下组成的:
a)ELF文件头
b)程序头表(可选)
c)第1节,第2节,...,第n节,...
d)节头表
从执行的角度看,ELF文件从开始到结束,可以看成是如下组成的:
a)ELF文件头
b)程序头表
c)第1段,第2段,...,
d)节头表(可选)
2.3文件头(Elfheader)
Elf头在程序的开始部位,作为引路表描述整个ELF的文件结构,其信息大致分为四部分:一是系统相关信息,二是目标文件类型,三是加载相关信息,四是链接相关信息。
其中系统相关信息包括elf文件魔数(标识elf文件),平台位数,数据编码方式,elf头部版本,硬件平台e_machine,目标文件版本e_version,处理器特定标志e_ftags:这些信息的引入极大增强了elf文件的可移植性,使交叉编译成为可能。目标文件类型用e_type的值表示,可重定位文件为1,可执行文件为2,共享文件为3;加载相关信息有:程序进入点e_entry.程序头表偏移量e_phoff,elf头部长度e_ehsize,程序头表中一个条目的长度e_phentsize,程序头表条目数目e_phnum;链接相关信息有:节头表偏移量e_shoff,节头表中一个条目的长度e_shentsize,节头表条目个数e_shnum,节头表字符索引eshstmdx。可使用命令"readelf-hfilename"来察看文件头的内容。
文件头的数据结构如下:
typedefstructelf32_hdr{
unsignedchare_ident[EI_NIDENT];
Elf32_Halfe_type;//目标文件类型
Elf32_Halfe_machine;//硬件平台
Elf32_Worde_version;//elf头部版本
Elf32_Addre_entry;//程序进入点
Elf32_Offe_phoff;//程序头表偏移量
Elf32_Offe_shoff;//节头表偏移量
Elf32_Worde_flags;/处理器特定标志
Elf32_Halfe_ehsize;//elf头部长度
Elf32_Halfe_phentsize;//程序头表中一个条目的长度
Elf32_Halfe_phnum;//程序头表条目数目
Elf32_Halfe_shentsize;//节头表中一个条目的长度
Elf32_Halfe_shnum;//节头表条目个数
Elf32_Halfe_shstrmdx;//节头表字符索引
}Elf32_Ehdr;
2.4程序头表(programheadertable)
程序头表告诉系统如何建立一个进程映像.它是从加载执行的角度来看待elf文件.从它的角度看.elf文件被分成许多段,elf文件中的代码、链接信息和注释都以段的形式存放。每个段都在程序头表中有一个表项描述,包含以下属性:段的类型,段的驻留位置相对于文件开始处的偏移,段在内存中的首字节地址,段的物理地址,段在文件映像中的字节数.段在内存映像中的字节数,段在内存和文件中的对齐标记。可用"readelf-lfilename"察看程序头表中的内容。程序头表的结构如下:
typedefstructelf32_phdr{
Elf32_Wordp_type;//段的类型
Elf32_Offp_offset;//段的位置相对于文件开始处的偏移
Elf32_Addrp_vaddr;//段在内存中的首字节地址
Elf32_Addrp_paddr;//段的物理地址
Elf32_Wordp_filesz;//段在文件映像中的字节数
Elf32_Wordp_memsz;//段在内存映像中的字节数
Elf32_Wordp_flags;//段的标记
Elf32_Wordp_align;,/段在内存中的对齐标记
)Elf32_Phdr;
2.5节头表(sectionheadertable)
节头表描述程序节,为编译器和链接器服务。它把elf文件分成了许多节.每个节保存着用于不同目的的数据.这些数据可能被前面的程序头重复使用,完成一次任务所需的信息往往被分散到不同的节里。由于节中数据的用途不同,节被分成不同的类型,每种类型的节都有自己组织数据的方式。每一个节在节头表中都有一个表项描述该节的属性,节的属性包括小节名在字符表中的索引,类型,属性,运行时的虚拟地址,文件偏移,以字节为单位的大小,小节的对齐等信息,可使用"readelf-Sfilename"来察看节头表的内容。节头表的结构如下:
typedefstruct{
Elf32_Wordsh_name;//小节名在字符表中的索引
E1t32_Wordsh_type;//小节的类型
Elf32_Wordsh_flags;//小节属性
Elf32_Addrsh_addr;//小节在运行时的虚拟地址
Elf32_Offsh_offset;//小节的文件偏移
Elf32_Wordsh_size;//小节的大小.以字节为单位
Elf32_Wordsh_link;//链接的另外一小节的索引
Elf32Wordsh_info;//附加的小节信息
Elf32Wordsh_addralign;//小节的对齐
Elf32Wordsh_entsize;//一些sections保存着一张固定大小入口的表。就像符号表
}Elf32_Shdr;
3ELF的特性
3.1平台相关
在ELF文件头中包含了足够的平台相关信息,如数据编码方式,平台位数,硬件平台e_machine等,这些平台相关信息可在编译由编译器决定。例如,与平台位数的相关的数据结构的定义在elf.h的头文件中.在编译预处理时确定:
#ifELFCLASS==ELFCLASS32
externElf32_Dyn_DYNAMIC[];
#defineelfhdrelf32_hdr;
#defineelf_phdrelf32_phdr;
#defineelf_noteelf32_note;
#else
externElf64_Dyn_DYNAMIC[];
#defineelfhdrelf64_hdr;
#defineelf_phdrelf64_phdr;
#defineelf_noteelf64_note;
#endif
linux系统加载ELF可执行文件时,必须首先做一些简单的一致性检查.其代码如下:
if(memcmp(elf_ex.e_ident,ELFMAG,SELFMAG)!=0)
gotoout;//检查文件头开始四个字符是否为ELF魔数'\0177ELF
if(elf_ex.e_type!=ET_EXEC&&elf_ex.e_type!=ET_DYN)
gotoout;//检查文件类型是否为可执行文件或共享目标文件
if(!elf_check_arch(&elf_ex))
gotoout;//检查硬件平台是否一致
其中的elf_check_arch(x)在不同的硬件平台上有不同的定义,其由系统的硬件平台决定。这样,在硬件平台相同的系统上,ELF可以不作修改的执行。因此,它可以支持不同平台上的交叉编译(cross_compilation)和交叉链接(cross_linking)。
3.2PIC
ELF可以生成一种特殊的代码——与位置无关的代码(position-independentcode,PIC)。用户对gcc使用-fPIC指示GNU编译系统生成PIC代码。它是实现共享库或共享可执行代码的基础.这种代码的特殊性在于它可以加载到内存地址空间的任何地址执行.这也是加载器可以很方便的在进程中动态链接共享库。
PIC的实现运用了一个事实,就是代码段中任何指令和数据段中的任何变量之间的距离都是一个与代码段和数据段的绝对存储器位置无关的常量。因此,编译器在数据段开始的地方创建了一个表.叫做全局偏移量表(globaloffsettable.GOT)。GOT包含每个被这个目标模块引用的全局数据目标的表目。编译器还为GOT中每个表目生成一个重定位记录。在加载时,动态链接器会重定位GOT中的每个表目,使得它包含正确的绝对地址。PIC代码在代码中实现通过GOT间接的引用每个全局变量,这样,代码中本来简单的数据引用就变得复杂,必须加入得到GOT适当表目内容的指令。对只读数据的引用也根据同样的道理,所以,加上IC编译成的代码比一般的代码开销大。
如果一个elf可执行文件需要调用定义在共享库中的任何函数,那么它就有自己的GOT和PLT(procedurelinkagetable,过程链接表).这两个节之间的交互可以实现延迟绑定(lazybinging),这种方法将过程地址的绑定推迟到第一次调用该函数。为了实现延迟绑定,GOT的头三条表目是特殊的:GOT[0]包含.dynamic段的地址,.dynamic段包含了动态链接器用来绑定过程地址的信息,比如符号的位置和重定位信息;GOT[1]包含动态链接器的标识;GOT[2]包含动态链接器的延迟绑定代码的入口点。GOT的其他表目为本模块要引用的一个全局变量或函数的地址。PLT是一个以16字节(32位平台中)表目的数组形式出现的代码序列。其中PLT[0]是一个特殊的表目,它跳转到动态链接器中执行;每个定义在共享库中并被本模块调用的函数在PLT中都有一个表目,从PLT[1]开始.模块对函数的调用会转到相应PLT表目中执行,这些表目由三条指令构成。第一条指令是跳转到相应的GOT存储的地址值中.第二条指令把函数相应的ID压入栈中,第三条指令跳转到PLT[O]中调用动态链接器解析函数地址,并把函数真正地址存入相应的GOT表目中。被调用函数GOT相应表目中存储的最初地址为相应PLT表目中第二条指令的地址值,函数第一次被调用后.GOT表目中的值就为函数的真正地址。因此,第一次调用函数时开销比较大.但是其后的每次调用都只会花费一条指令和一个间接的存储器引用。
3.3强大的工具支持
由于gnu有大量的工具支持elf文件格式.随着gnu工具的功能的扩展.程序员对ELF文件的运用也越来越灵活。例如,在C++中全局的构造函数和析构函数必须非常小心的处理碰到的语言规范问题。构造函数必须在main函数之前被调用。析构函数必须在main函数返回之后被调用。ELF文件格式中,定义了两个特殊的节(section),.init和.fini,.init保存着可执行指令,它构成了进程的初始化代码。当一个程序开始运行时,在main函数被调用之前(c语言称为main),系统安排执行这个section的中的代码。.fini保存着可执行指令,它构成了进程的终止代码。当一个程序正常退出时.系统安排执行这个section的中的代码。C++编译器利用这个特性.构造正确的.init和.finisections.并结合.ctors(该section保存着程序的全局的构造函数的指针数组)和.dtors(该section保存着程序的全局的析构函数的指针数组)两个section,完成全局的构造函数和析构函数的处理。
GCC还有许多扩展的特性.有些对ELF特别的有用。其中一个就是_attribute_。使用_attribute_可以使一个函数放到_CTOR_LIST_或者_DTOR_LIST_里。_attribute_((constructor))促使函数在进入main之前会被自动调用。_attribute_((destructor))促使函数在main返回或者exit调用之后被自动调用。这种函数必须是不能带参数的而且必须是staticvoid类型的函数。在ELF下,这个特性在一般的可执行文件和共享库中都能很好的工作。另外一个GCC的特性是attribute_(section("sectionname")),使用这个,能把一个函数或者是数据结构放到任何的section中。
[举例]
这里,通过使用一些用于操作ELF文件的工具的例子,来对其有一个直观的了解。
1readelf工具
readelf用来显示ELF格式目标文件的信息.可通过参数选项来控制显示哪些特定信息。
*读取elf文件头信息:
$readelf-hfbtest
输入之后,输出如下:
ELFHeader:
Magic:7f454c46010101000000000000000000
Class:ELF32
Data:2'scomplement,littleendian
Version:1(current)
OS/ABI:UNIX-SystemV
ABIVersion:0
Type:EXEC(Executablefile)
Machine:Intel80386
Version:0x1
Entrypointaddress:0x80484d0
Startofprogramheaders:52(bytesintofile)
Startofsectionheaders:5924(bytesintofile)
Flags:0x0
Sizeofthisheader:52(bytes)
Sizeofprogramheaders:32(bytes)
Numberofprogramheaders:8
Sizeofsectionheaders:40(bytes)
Numberofsectionheaders:36
Sectionheaderstringtableindex:33
这里,fbtest是在本地使用gcc编译生成的一个简单的可执行程序。
*查看elf文件程序头表信息:
$readelf-lfbtest
输入之后,输出如下:
ProgramHeaders:
TypeOffsetVirtAddrPhysAddrFileSizMemSizFlgAlign
PHDR0x0000340x080480340x080480340x001000x00100RE0x4
INTERP0x0001340x080481340x080481340x000130x00013R0x1
[Requestingprograminterpreter:/lib/ld-linux.so.2]
LOAD0x0000000x080480000x080480000x00df40x00df4RE0x1000
LOAD0x000f0c0x08049f0c0x08049f0c0x001280x00178RW0x1000
DYNAMIC0x000f200x08049f200x08049f200x000d00x000d0RW0x4
NOTE0x0001480x080481480x080481480x000200x00020R0x4
GNU_STACK0x0000000x000000000x000000000x000000x00000RW0x4
GNU_RELRO0x000f0c0x08049f0c0x08049f0c0x000f40x000f4R0x1
SectiontoSegmentmapping:
SegmentSections...
00
01.interp
02.interp.note.ABI-tag.hash.gnu.hash.dynsym.dynstr.gnu.version.gnu.version_r.rel.dyn.rel.plt.init.plt.text.fini.rodata.eh_frame
03.ctors.dtors.jcr.dynamic.got.got.plt.data.bss
04.dynamic
05.note.ABI-tag
06
07.ctors.dtors.jcr.dynamic.got
*查看elf文件的节信息:
$readelf-Slibmy.so
输入之后,输出如下:
Thereare33sectionheaders,startingatoffset0xfd0:
SectionHeaders:
[Nr]NameTypeAddrOffSizeESFlgLkInfAl
[0]NULL0000000000000000000000000
[1].hashHASH000000d40000d40000a004A304
[2].gnu.hashGNU_HASH0000017400017400004004A304
[3].dynsymDYNSYM000001b40001b400015010A414
[4].dynstrSTRTAB0000030400030400018a00A001
[5].gnu.versionVERSYM0000048e00048e00002a02A302
[6].gnu.version_rVERNEED000004b80004b800002000A414
[7].rel.dynREL000004d80004d80000e808A304
[8].rel.pltREL000005c00005c000001008A3104
[9].initPROGBITS000005d00005d000003000AX004
[10].pltPROGBITS0000060000060000003004AX004
[11].textPROGBITS000006300006300002a400AX0016
[12].finiPROGBITS000008d40008d400001c00AX004
[13].rodataPROGBITS000008f00008f000000600A001
[14].eh_frame_hdrPROGBITS000008f80008f800003400A004
[15].eh_framePROGBITS0000092c00092c0000d800A004
[16].ctorsPROGBITS00001a04000a0400000c00WA004
[17].dtorsPROGBITS00001a10000a1000000800WA004
[18].jcrPROGBITS00001a18000a1800000400WA004
[19].dynamicDYNAMIC00001a1c000a1c0000d008WA404
[20].gotPROGBITS00001aec000aec00000c04WA004
[21].got.pltPROGBITS00001af8000af800001404WA004
[22].dataPROGBITS00001b0c000b0c00000800WA004
[23].bssNOBITS00001b14000b1400000800WA004
[24].commentPROGBITS00000000000b140000d200001
[25].debug_arangesPROGBITS00000000000be800005000008
[26].debug_infoPROGBITS00000000000c3800011a00001
[27].debug_abbrevPROGBITS00000000000d5200002400001
[28].debug_linePROGBITS00000000000d7600010200001
[29].debug_rangesPROGBITS00000000000e7800004000008
[30].shstrtabSTRTAB00000000000eb800011600001
[31].symtabSYMTAB000000000014f80004c01032564
[32].strtabSTRTAB000000000019b800031c00001
KeytoFlags:
W(write),A(alloc),X(execute),M(merge),S(strings)
I(info),L(linkorder),G(group),x(unknown)
O(extraOSprocessingrequired)o(OSspecific),p(processorspecific)
这里的libmy.so是自行生成的一个共享库。
2objcopy
objcopy可以把一种目标文件中的内容复制到另一种类型的目标文件中.
通过objcopy的各种选项,可以对目标文件进行各种类型的操作。例如去掉可执行文件的调试信息(效果等同于strip)等等,具体需要做什么操作,要求我们对elf文件中每个部分的内容有所理解。
这里只给出一个例子:
*使用objcopy把.comment段和.note段的信息去掉:
$objcopy-R.comment-R.notehellohello.min
这里,hello是一个可执行文件,通过"readelf-lhello"或者"readelf-Shello"命令可以知道文件中包含一个.note段和.comment段,通过这个命令,将这两个段从文件中删除,不会改变原来的文件,而是将删除了这些信息的文件存放在hello.min中。实际通过这个方法,可以减少可执行文件的大小,且不影响可执行文件的功能。
选项的含义是:
-R.note-R.comment表示移掉.note与.comment段
-Obinaryxybxyb.bin表示由xyb生成二进制文件xyb.bin
3objdump
objdump是用查看目标文件或者可执行的目标文件的构成的GCC工具。
以下给出几个常用的例子:
*输出目标文件的所有段概括:
#objdump-hmain
输入之后,输出信息大致如下:
main:fileformatelf32-i386
Sections:
IdxNameSizeVMALMAFileoffAlgn
0.interp000000130804813408048134000001342**0
CONTENTS,ALLOC,LOAD,READONLY,DATA
1.note.ABI-tag000000200804814808048148000001482**2
CONTENTS,ALLOC,LOAD,READONLY,DATA
2.gnu.hash000000300804816808048168000001682**2
CONTENTS,ALLOC,LOAD,READONLY,DATA
3.dynsym000000d00804819808048198000001982**2
CONTENTS,ALLOC,LOAD,READONLY,DATA
4.dynstr000001830804826808048268000002682**0
CONTENTS,ALLOC,LOAD,READONLY,DATA
5.gnu.version0000001a080483ec080483ec000003ec2**1
CONTENTS,ALLOC,LOAD,READONLY,DATA
6.gnu.version_r000000600804840808048408000004082**2
CONTENTS,ALLOC,LOAD,READONLY,DATA
7.rel.dyn000000100804846808048468000004682**2
CONTENTS,ALLOC,LOAD,READONLY,DATA
8.rel.plt000000480804847808048478000004782**2
CONTENTS,ALLOC,LOAD,READONLY,DATA
9.init00000017080484c0080484c0000004c02**2
CONTENTS,ALLOC,LOAD,READONLY,CODE
10.plt000000a0080484d8080484d8000004d82**2
CONTENTS,ALLOC,LOAD,READONLY,CODE
11.text000002380804858008048580000005802**4
CONTENTS,ALLOC,LOAD,READONLY,CODE
12.fini0000001c080487b8080487b8000007b82**2
CONTENTS,ALLOC,LOAD,READONLY,CODE
13.rodata00000013080487d4080487d4000007d42**2
......其余内容省略......
这里,main是一个可执行文件。
*输出目标文件的符号表:
#objdump-tmain
输入之后,输出类似如下:
main:fileformatelf32-i386
SYMBOLTABLE:
08048134ld.interp00000000.interp
08048148ld.note.ABI-tag00000000.note.ABI-tag
08048168ld.gnu.hash00000000.gnu.hash
08048198ld.dynsym00000000.dynsym
08048268ld.dynstr00000000.dynstr
080483ecld.gnu.version00000000.gnu.version
08048408ld.gnu.version_r00000000.gnu.version_r
08048468ld.rel.dyn00000000.rel.dyn
08048478ld.rel.plt00000000.rel.plt
080484c0ld.init00000000.init
080484d8ld.plt00000000.plt
08048580ld.text00000000.text
080487b8ld.fini00000000.fini
080487d4ld.rodata00000000.rodata
080487e8ld.eh_frame_hdr00000000.eh_frame_hdr
08048824ld.eh_frame00000000.eh_frame
08049914ld.ctors00000000.ctors
08049920ld.dtors00000000.dtors
08049928ld.jcr00000000.jcr
0804992cld.dynamic00000000.dynamic
08049a0cld.got00000000.got
08049a10ld.got.plt00000000.got.plt
08049a40ld.data00000000.data
08049a48ld.bss00000000.bss
00000000ld.comment00000000.comment
080485a4lF.text00000000call_gmon_start
00000000ldf*ABS*00000000crtstuff.c
08049914lO.ctors00000000__CTOR_LIST__
08049920lO.dtors00000000__DTOR_LIST__
08049928lO.jcr00000000__JCR_LIST__
08049ad4lO.bss00000004dtor_idx.5793
08049ad8lO.bss00000001completed.5791
080485d0lF.text00000000__do_global_dtors_aux
......其余信息省略......
*以某种分类信息的形式把目标文件的数据组织(被分为几大块)输出
#objdump-xmain
输入之后,输出信息类似如下:
main:fileformatelf32-i386
main
architecture:i386,flags0x00000112:
EXEC_P,HAS_SYMS,D_PAGED
startaddress0x08048580
ProgramHeader:
PHDRoff0x00000034vaddr0x08048034paddr0x08048034align2**2
filesz0x00000100memsz0x00000100flagsr-x
INTERPoff0x00000134vaddr0x08048134paddr0x08048134align2**0
filesz0x00000013memsz0x00000013flagsr--
LOADoff0x00000000vaddr0x08048000paddr0x08048000align2**12
filesz0x00000914memsz0x00000914flagsr-x
LOADoff0x00000914vaddr0x08049914paddr0x08049914align2**12
filesz0x00000130memsz0x000001ccflagsrw-
DYNAMICoff0x0000092cvaddr0x0804992cpaddr0x0804992calign2**2
filesz0x000000e0memsz0x000000e0flagsrw-
NOTEoff0x00000148vaddr0x08048148paddr0x08048148align2**2
filesz0x00000020memsz0x00000020flagsr--
EH_FRAMEoff0x000007e8vaddr0x080487e8paddr0x080487e8align2**2
filesz0x0000003cmemsz0x0000003cflagsr--
STACKoff0x00000000vaddr0x00000000paddr0x00000000align2**2
...省略...
DynamicSection:
NEEDEDlibstdc++.so.6
NEEDEDlibm.so.6
NEEDEDlibgcc_s.so.1
NEEDEDlibc.so.6
INIT0x80484c0
FINI0x80487b8
GNU_HASH0x8048168
...省略...
VersionReferences:
requiredfromlibstdc++.so.6:
0x056bafd30x0005CXXABI_1.3
0x089229740x0003GLIBCXX_3.4
requiredfromlibc.so.6:
0x0d6969100x0004GLIBC_2.0
0x09691f730x0002GLIBC_2.1.3
Sections:
IdxNameSizeVMALMAFileoffAlgn
0.interp000000130804813408048134000001342**0
CONTENTS,ALLOC,LOAD,READONLY,DATA
1.note.ABI-tag000000200804814808048148000001482**2
CONTENTS,ALLOC,LOAD,READONLY,DATA
2.gnu.hash000000300804816808048168000001682**2
CONTENTS,ALLOC,LOAD,READONLY,DATA
3.dynsym000000d00804819808048198000001982**2
CONTENTS,ALLOC,LOAD,READONLY,DATA
4.dynstr000001830804826808048268000002682**0
CONTENTS,ALLOC,LOAD,READONLY,DATA
5.gnu.version0000001a080483ec080483ec000003ec2**1
...省略...
SYMBOLTABLE:
08048134ld.interp00000000.interp
08048148ld.note.ABI-tag00000000.note.ABI-tag
08048168ld.gnu.hash00000000.gnu.hash
08048198ld.dynsym00000000.dynsym
08048268ld.dynstr00000000.dynstr
080483ecld.gnu.version00000000.gnu.version
08048408ld.gnu.version_r00000000.gnu.version_r
...省略...
这里可知,分别显示出各个段相关的信息。
*输出指定段的信息:
#objdump-j.text-Smain
输入之后,输出类似如下:
main:fileformatelf32-i386
Disassemblyofsection.text:
08048580<_start>:
8048580:31edxor%ebp,%ebp
8048582:5epop%esi
8048583:89e1mov%esp,%ecx
8048585:83e4f0and$0xfffffff0,%esp
8048588:50push%eax
8048589:54push%esp
...省略...
这里,反汇编会用到类似的命令。
4nm
这个命令可以用来查看库中的符号。
nm列出的符号有很多,常见的有三种,一种是在库中被调用,但并没有在库中定义(表明需要其他库支持),用U表示;一种是库中定义的函数,用T表示,这是最常见的;另外一种是所谓的“弱态”符号,它们虽然在库中被定义,但是可能被其他库中的同名符号覆盖,用W表示。
*假设开发者希望知道hello库中是否定义了printf():
$nmlibhello.so|grepprintf
Uprintf
U表示符号printf被引用,但是并没有在函数内定义,由此可以推断,要正常使用hello库,必须有其它库支持。
5ldd
ldd命令可以用来查看一个可执行文件或者库依赖哪些其他的文件。
*使用ldd命令查看hello依赖于哪些库:
$lddhello
libc.so.6=>/lib/libc.so.6(0x400la000)
/lib/ld-linux.so.2=>/lib/ld-linux.so.2(0x40000000)
这里,结合nm,从上面的结果可以继续查看printf最终在哪里被定义.
[其它]
暂无。
参考:
http://www.dz3w.com/mcu/linux/0070701.html
http://linux.chinaunix.net/techdoc/beginner/2009/04/23/1108716.shtml
http://www.dz3w.com/mcu/linux/0070701.html
http://www.sudu.cn/info/html/edu/20080428/302819.html
本文涉及的关于elf文件介绍的内容大部分是从网络上收集整理的,也可以通过"manelf"或者"infoelf"来查看其相关文档信息;后面几个操作elf文件的工具,也是非常强大和重要的,其更多的信息参见帮助手册。
作者:QuietHeart
Email:[email protected]
日期:2010年12月20日