ELF文件格式简介
??簡單了解下ELF文件的格式。
1 簡介
??可執(zhí)行與可鏈接格式 (Executable and Linkable Format,ELF),常被稱為 ELF格式,是一種用于可執(zhí)行文件、目標代碼、共享庫和核心轉(zhuǎn)儲(core dump)的標準文件格式,一般用于類Unix系統(tǒng),比如Linux,Macox等。ELF 格式靈活性高、可擴展,并且跨平臺。比如它支持不同的字節(jié)序和地址范圍,所以它不會不兼容某一特別的 CPU 或指令架構(gòu)。這也使得 ELF 格式能夠被運行于眾多不同平臺的各種操作系統(tǒng)所廣泛采納。
??ELF文件一般由三種類型的文件:
- 可重定向文件:文件保存著代碼和適當(dāng)?shù)臄?shù)據(jù),用來和其他的目標文件一起來創(chuàng)建一個可執(zhí)行文件或者是一個共享目標文件。比如編譯的中間產(chǎn)物.o文件;
- 可執(zhí)行文件:一個可執(zhí)行文件;
- 共享目標文件:共享庫。文件保存著代碼和合適的數(shù)據(jù),用來被下連接編輯器和動態(tài)鏈接器鏈接。比如linux下的.so文件。
2 ELF文件格式
??在編譯過程中ELF文件格式在鏈接和程序的運行階段的格式不同。鏈接階段每個.o文件都是一個獨立的ELF文件,為了效率和便利性他們的段需要進行合并才能生成對應(yīng)的可執(zhí)行文件。
??ELF文件包含一個Header描述文件的基本信息;程序頭表告訴徐彤如何構(gòu)建進程的內(nèi)存鏡像,因此只有可執(zhí)行文件由程序頭表;Sections描述了鏈接過程中的需要的符號表、數(shù)據(jù)、指令等信息,而在可執(zhí)行文件中是Segments,是經(jīng)過合并的Secitons;節(jié)/段頭表指明了對應(yīng)section/segment在文件中的偏移,鏈接階段的ELF文件必須包含該表頭;而每個節(jié)/段頭描述了對應(yīng)的section/segment的大小,入口等基本信息。
??下圖是32bit系統(tǒng)下面使用的字段的數(shù)大小,64bit系統(tǒng)類似,之后不在贅述。
2.1 ELF Header
??ELF文件頭描述了ELF文件的基本類型,地址偏移等信息,分為32bit和64bit兩個版本,定義于linux源碼的/usr/include/elf.h文件中。
#define EI_NIDENT 16typedef struct elf32_hdr{unsigned char e_ident[EI_NIDENT];Elf32_Half e_type;Elf32_Half e_machine;Elf32_Word e_version;Elf32_Addr e_entry; /* Entry point */Elf32_Off e_phoff;Elf32_Off e_shoff;Elf32_Word e_flags;Elf32_Half e_ehsize;Elf32_Half e_phentsize;Elf32_Half e_phnum;Elf32_Half e_shentsize;Elf32_Half e_shnum;Elf32_Half e_shstrndx; } Elf32_Ehdr; typedef struct elf64_hdr {unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */Elf64_Half e_type;Elf64_Half e_machine;Elf64_Word e_version;Elf64_Addr e_entry; /* Entry point virtual address */Elf64_Off e_phoff; /* Program header table file offset */Elf64_Off e_shoff; /* Section header table file offset */Elf64_Word e_flags;Elf64_Half e_ehsize;Elf64_Half e_phentsize;Elf64_Half e_phnum;Elf64_Half e_shentsize;Elf64_Half e_shnum;Elf64_Half e_shstrndx; } Elf64_Ehdr;??從上面的結(jié)構(gòu)中能夠看出32bit和64bit的區(qū)別僅僅是字長的區(qū)別,字段上沒有實際上的差別。每個字段的含義如下:
- e_ident:ELF文件的描述,是一個16字節(jié)的標識,表明當(dāng)前文件的數(shù)據(jù)格式,位數(shù)等:
- [0,3]字節(jié)為魔數(shù),即e_ident[EI_MAG0-EI_MAG3],取值為固定的0x7f E L F,標記當(dāng)前文件為一個ELF文件;
- [4,4]字節(jié)為EI_CLASS即e_ident[EI_CLASS],表明當(dāng)前文件的類別:
- 0:表示非法的類別;
- 1:表示32bit;
- 2:表示64bit;
- [5,5]字節(jié)為EI_DATA即e_ident[EI_DATA],表明當(dāng)期那文件的數(shù)據(jù)排列方式:
- 0表示非法;
- 1表示小端;
- 2表示大端;
- [6,6]字節(jié)為EI_VERSION即e_ident[EI_VERSION],表明當(dāng)前文件的版本,目前該取值必須為EV_CURRENT即1;
- [7,7]字節(jié)為EI_PAD即e_ident[EI_PAD]表明e_ident中未使用的字節(jié)的起點(值是相對于e_ident[EI_PAD+1]的偏移),未使用的字節(jié)會被初始化為0,解析ELF文件時需要忽略對應(yīng)的字段;
??EI_MAG0,EI_MAG1,EI_MAG2,EI_MAG3,EI_CLASS,EI_DATA,EI_VERSION,EI_OSABI,EI_PAD是linux源碼中定義的宏,取值分別為0-7,分別對應(yīng)各個字段的下標;下面的宏定義將采用類似EI_MAG0(0)的方式,表示EI_MAG0的值為0。
- e_type:文件的標識字段標識文件的類型;
- ET_NONE(0):未知的文件格式;
- ET_REL(1):可重定位文件,比如目標文件;
- ET_EXEC(2):可執(zhí)行文件;
- ET_DYN(3):共享目標文件;
- ET_CORE(4):Core轉(zhuǎn)儲文件,比如程序crash之后的轉(zhuǎn)儲文件;
- ET_LOPROC(0xff00):特定處理器的文件標識;
- ET_HIPROC(0xffff):特定處理器的文件標識;
- [ET_LOPROC,ET_HIPROC]之間的值用來表示特定處理器的文件格式;
- e_machine:目標文件的體系結(jié)構(gòu)(下面列舉了少數(shù)處理器架構(gòu),具體ELF文件支持的架構(gòu)在對應(yīng)的文件中查看即可);
- ET_NONE(0):未知的處理器架構(gòu);
- EM_M32(1):AT&T WE 32100;
- EM_SPARC(2):SPARC;
- EM_386(3):Intel 80386;
- EM_68K(4):Motorola 68000;
- EM_88K(5):Motorola 88000;
- EM_860(6):Intel 80860;
- EM_MIPS(7):MIPS RS3000大端;
- EM_MIPS_RS4_BE(10):MIPS RS4000大端;
- 其他,預(yù)留;
- e_version:當(dāng)前文件的版本;
- EV_NONE(0):非法的版本;
- EV_CURRENT(`):當(dāng)前版本;
- e_entry:程序的虛擬入口地址,如果文件沒有對應(yīng)的入口可以為0;
- e_phoff:文件中程序頭表的偏移(bytes),如果文件沒有該項,則應(yīng)該為0;
- e_shoff:文件中段表/節(jié)表的偏移(bytes),如果文件沒有該項,則應(yīng)該為0;
- e_flags:處理器相關(guān)的標志位,宏格式為EF_machine_flag比如EF_MIPS_PIC;
- e_ehsize:ELF文件頭的大小(bytes);
- e_phentsize:程序頭表中單項的大小,表中每一項的大小相同;
- e_phnum:程序頭表中的項數(shù),也就是說程序頭表的實際大小為ephentsize x e_phnum,如果文件中沒有程序頭表該項為0;
- e_shentsize:節(jié)表中單項的大小,表中每一項的大小相同;
- e_shnum:節(jié)表中項的數(shù)量;
- e_shstrndx:節(jié)表中節(jié)名的索引,如果文件沒有該表則該項為SHN_UNDEF(0)。
2.2 程序頭表(Program Header Table)
??可執(zhí)行文件或者共享目標文件的程序頭部是一個結(jié)構(gòu)數(shù)組,每個結(jié)構(gòu)描述了一個段 或者系統(tǒng)準備程序執(zhí)行所必需的其它信息。程序頭表描述了ELF文件中Segment在文件中的布局,描述了OS該如何裝載可執(zhí)行文件到內(nèi)存。程序頭表的表項的描述如下,類似于ELF Header也有32和64位兩個版本。
typedef struct elf32_phdr {Elf32_Word p_type;Elf32_Off p_offset;Elf32_Addr p_vaddr;Elf32_Addr p_paddr;Elf32_Word p_filesz;Elf32_Word p_memsz;Elf32_Word p_flags;Elf32_Word p_align; } Elf32_Phdr; typedef struct elf64_phdr {Elf64_Word p_type;Elf64_Word p_flags;Elf64_Off p_offset; /* Segment file offset */Elf64_Addr p_vaddr; /* Segment virtual address */Elf64_Addr p_paddr; /* Segment physical address */Elf64_Xword p_filesz; /* Segment size in file */Elf64_Xword p_memsz; /* Segment size in memory */Elf64_Xword p_align; /* Segment alignment, file & memory */ } Elf64_Phdr;- p_type:當(dāng)前Segment的類型;
- PT_NULL(0):當(dāng)前項未使用,項中的成員是未定義的,需要忽略當(dāng)前項;
- PT_LOAD(1):當(dāng)前Segment是一個可裝載的Segment,即可以被裝載映射到內(nèi)存中,其大小由p_filesz和p_memsz描述。如果p_memsz>p_filesz則剩余的字節(jié)被置零,但是p_filesz>p_memsz是非法的。動態(tài)庫一般包含兩個該類型的段:代碼段和數(shù)據(jù)段;
- PT_DYNAMIC(2):動態(tài)段,動態(tài)庫特有的段,包含了動態(tài)鏈接必須的一些信息,比如需要鏈接的共享庫列表、GOT等等;
- PT_INTERP(3):當(dāng)前段用于存儲一段以NULL為結(jié)尾的字符串,該字符串表明了程序解釋器的位置。且當(dāng)前段僅僅對于可執(zhí)行文件有實際意義,一個可執(zhí)行文件中不能出現(xiàn)兩個當(dāng)前段,如果一個文件中包含當(dāng)前段。比如/lib64/ld-linux-x86-64.so.2;
- PT_NOTE(4):用于保存與特定供應(yīng)商或者系統(tǒng)相關(guān)的附加信息以便于兼容性、一致性檢查,但是實際上只保存了操作系統(tǒng)的規(guī)范信息;
- PT_SHLIB(5):保留段;
- PT_PHDR(6):保存程序頭表本身的位置和大小,當(dāng)前段不能在文件中出現(xiàn)一次以上,且僅僅當(dāng)程序表頭為內(nèi)存映像的一部分時起作用,它必須在所有加載項目之前;
- [PT_LPROC(0x70000000),PT_HIPROC(0x7fffffff)]:該范圍內(nèi)的值用作預(yù)留;
- p_offset:當(dāng)前段相對于文件起始位置的偏移量;
- p_vaddr:段的第一個字節(jié)將被映射到到內(nèi)存中的虛擬地址;
- p_paddr:此成員僅用于與物理地址相關(guān)的系統(tǒng)中。因為 System V 忽略所有應(yīng)用程序的物理地址信息,此字段對與可執(zhí)行文件和共享目標文件而言具體內(nèi)容是指定的;
- p_filesz:段在文件映像中所占的字節(jié)數(shù),可能為 0;
- p_memsz:段在內(nèi)存映像中占用的字節(jié)數(shù),可能為 0;
- p_flags:段相關(guān)的標志;
- p_align:段在文件中和內(nèi)存中如何對齊。可加載的進程段的p_vaddr和- p_offset取值必須合適,相對于對頁面大小的取模而言;
- 0和1表示不需要對齊;
- 其他值必須為2的冪次方,且必須p_addr∣p_align==p_offset∣palignp\_addr|p\_align==p\_offset| p_alignp_addr∣p_align==p_offset∣pa?lign。
2.3 節(jié)頭表(Section Header Table)
??節(jié)頭表描述了ELF文件中的節(jié)的基本信息。可執(zhí)行文件不一定由節(jié)頭表但是一定有節(jié),節(jié)頭表可利用特殊的方式去除。
??段和節(jié)的區(qū)別是:
- 段包含了程序裝載可執(zhí)行的基本信息,段告訴OS如何裝載當(dāng)前段到虛擬內(nèi)存以及當(dāng)前段的權(quán)限等和執(zhí)行相關(guān)的信息,一個段可以包含0個或多個節(jié);
- 節(jié)包含了程序的代碼和數(shù)據(jù)等內(nèi)容,鏈接器會將多個節(jié)合并為一個段。
- sh_name:值是節(jié)名稱在字符串表中的索引;
- sh_type:描述節(jié)的類型和語義;
- SHT_NULL(0):當(dāng)前節(jié)是非活躍的,沒有一個對應(yīng)的具體的節(jié)內(nèi)存;
- SHT_PROGBITS(1):包含了程序的指令信息、數(shù)據(jù)等程序運行相關(guān)的信息;
- SHT_SYMTAB(2):保存了符號信息,用于重定位;
- 此種類型節(jié)的sh_link存儲相關(guān)字符串表的節(jié)索引,sh_info存儲最后一個局部符號的符號表索引+1;
- SHT_DYNSYM(11):保存共享庫導(dǎo)入動態(tài)符號信息;
- 此種類型節(jié)的sh_link存儲相關(guān)字符串表的節(jié)索引,sh_info存儲最后一個局部符號的符號表索引+1;
- SHT_STRTAB(3):一個字符串表,保存了每個節(jié)的節(jié)名稱;
- SHT_RELA(4):存儲可重定位表項,可能會有附加內(nèi)容,目標文件可能有多個可重定位表項;
- 此種類型節(jié)的sh_link存儲相關(guān)符號表的節(jié)索引,sh_info存儲重定位所使用節(jié)的索引;
- SHT_HASH(5):存儲符號哈希表,所有參與動態(tài)鏈接的目標只能包含一個哈希表,一個目標文件只能包含一個哈希表;
- 此種類型節(jié)的sh_link存儲哈希表所使用的符號表的節(jié)索引,sh_info為0;
- SHT_DYAMIC(6):存儲包含動態(tài)鏈接的信息,一個目標文件只能包含一個;
- 此種類型的節(jié)的sh_link存儲當(dāng)前節(jié)中使用到的字符串表格的節(jié)的索引,sh_info為0;
- SHT_NOTE(7):存儲以某種形式標記文件的信息;
- SHT_NOBITS(8):這種類型的節(jié)不占據(jù)文件空間,但是成員sh_offset依然會包含對應(yīng)的偏移;
- SHT_REL(9):包含可重定位表項,無附加內(nèi)容,目標文件可能有多個可重定位表項;
- 此種類型節(jié)的sh_link存儲相關(guān)符號表的節(jié)索引,sh_info存儲重定位所使用節(jié)的索引;
- SHT_SHLIB(10):保留區(qū),包含此節(jié)的程序與ABI不兼容;
- [SHT_LOPROC(0x70000000),SHT_HIPROC(0x7fffffff)]:留給處理器專用語義;
- [SHT_LOUSER(0x80000000),SHT_HIUSER(0xffffffff)]:預(yù)留;
- sh_flags:1bit位的標志位;
- SHF_WRITE(0x1):當(dāng)前節(jié)包含進程執(zhí)行過程中可寫的數(shù)據(jù);
- SHF_ALLOC(0x2):當(dāng)前節(jié)在運行階段占據(jù)內(nèi)存;
- SHF_EXECINSTR(0x4):當(dāng)前節(jié)包含可執(zhí)行的機器指令;
- SHF_MASKPROC(0xf0000000):所有包含當(dāng)前掩碼都表示預(yù)留給特定處理器的;
- sh_addr:如果當(dāng)前節(jié)需要被裝載到內(nèi)存,則當(dāng)前項存儲當(dāng)前節(jié)映射到內(nèi)存的首地址,否則應(yīng)該為0;
- sh_offset:當(dāng)前節(jié)的首地址相對于文件的偏移;
- sh_size:節(jié)的大小。但是對于類型為SHT_NOBITS的節(jié),當(dāng)前值可能不為0但是在文件中不占據(jù)任何空間;
- sh_link:存儲節(jié)投標中的索引,表示當(dāng)前節(jié)依賴于對應(yīng)的節(jié)。對于特定的節(jié)有特定的含義,其他為SHN_UNDEF;
- sh_info:節(jié)的附加信息。對于特定的節(jié)有特定的含義,其他為0;
- sh_addralign:地址約束對齊,值應(yīng)該為0或者2的冪次方,0和1表示未進行對齊;
- sh_entsize:某些節(jié)是一個數(shù)組,對于這類節(jié)當(dāng)前字段給出數(shù)組中每個項的字節(jié)數(shù),比如符號表。如果節(jié)并不包含對應(yīng)的數(shù)組,值應(yīng)該為0。
2.3 一些特殊的節(jié)
??ELF文件中有一些預(yù)定義的節(jié)來保存程序、數(shù)據(jù)和一些控制信息,這些節(jié)被用來鏈接或者裝載程序。每個操作系統(tǒng)都支持一組鏈接模式,主要分為兩類(也就是常說的動態(tài)庫和靜態(tài)庫):
- Static:靜態(tài)綁定的一組目標文件、系統(tǒng)庫和庫檔案(比如靜態(tài)庫),解析包含的符號引用并創(chuàng)建一個完全自包含的可執(zhí)行文件;
- Dynamic:一組目標文件、庫、系統(tǒng)共享資源和其他共享庫鏈接在一起創(chuàng)建可執(zhí)行文件。當(dāng)加載此可執(zhí)行文件時必須使系統(tǒng)中其他共享資源和動態(tài)庫可用,程序才能正常運行。
??庫文件無論是動態(tài)庫還是靜態(tài)庫在其文件中都包含對應(yīng)的節(jié),一些特殊的節(jié)其功能如下:
- .bss,類型SHT_NOBITS,屬性SHF_ALLOC|SHF_WRITE:存儲未經(jīng)初始化的數(shù)據(jù)。根據(jù)定義程序開始執(zhí)行時,系統(tǒng)會將這些數(shù)據(jù)初始化為0,且此節(jié)不占用文件空間;
- .comment,類型SHT_PROGBITS,屬性none:存儲版本控制信息;
- .data,類型SHT_PROGBITS,屬性SHF_ALLOC|SHF_WRITE:存放初始化的數(shù)據(jù);
- .data1,類型SHT_PROGBITS,屬性SHF_ALLOC|SHF_WRITE:存放初始化的數(shù)據(jù);
- .debug,類型SHT_PROGBITS,屬性none:存放用于符號調(diào)試的信息;
- .dynamic,類型SHT_DYNAMIC,屬性SHF_ALLOC,是否有屬性SHF_WRITE屈居于處理器:包含動態(tài)鏈接的信息,
- .hash,類型SHT_HASH,屬性SHF_ALLOC:
- .line,類型SHT_PROGBITS,屬性none:存儲調(diào)試的行號信息,描述源代碼和機器碼之間的對應(yīng)關(guān)系;
- .note,類型SHT_NOTE,屬性none:
- .rodata,類型SHT_PROGBITS,屬性SHF_ALLOC:存儲只讀數(shù)據(jù);
- .rodata1,類型SHT_PROGBITS,屬性SHF_ALLOC:存儲只讀數(shù)據(jù);
- .shstrtab,類型SHT_STRTAB,屬性none:存儲節(jié)的名稱;
- .strtab,類型SHT_STRTAB:存儲常見的與符號表關(guān)聯(lián)的字符串。如果文件有一個包含符號字符串表的可加載段,則該段的屬性將包括 SHF_ALLOC 位; 否則,該位將關(guān)閉;
- .symtab,類型SHT_SYMTAB,屬性``````:存儲一個符號表。如果文件具有包含符號表的可加載段,則該節(jié)的屬性將包括 SHF_ALLOC 位;否則,該位將關(guān)閉;
- .text,類型SHT_PROGBITS,屬性SHF_ALLOC|SHF_EXECINSTR:存儲程序的代碼指令;
- .dynstr,類型SHT_STRTAB,屬性SHF_ALLOC:存儲動態(tài)鏈接所需的字符串,最常見的是表示與符號表條目關(guān)聯(lián)的名稱的字符串;
- .dynsym,類型SHT_DYNSYM,屬性SHF_ALLOC:存儲動態(tài)鏈接符號表;
- .fini,類型SHT_PROGBITS,屬性SHF_ALLOC|SHF_EXECINSTR:存儲有助于進程終止代碼的可執(zhí)行指令。 當(dāng)程序正常退出時,系統(tǒng)執(zhí)行本節(jié)代碼;
- .init,類型SHT_PROGBITS,屬性SHF_ALLOC|SHF_EXECINSTR:存儲有助于進程初始化代碼的可執(zhí)行指令。 當(dāng)程序開始運行時,系統(tǒng)會在調(diào)用主程序入口點(C 程序稱為 main)之前執(zhí)行本節(jié)中的代碼;
- .interp,類型SHT_PROGBITS:保存程序解釋器的路徑名。 如果文件有一個包含該節(jié)的可加載段,則該節(jié)的屬性將包括 SHF_ALLOC 位; 否則,該位將關(guān)閉;
- .relname,類型SHT_REL:包含重定位信息。如果文件具有包含重定位的可加載段,則這些部分的屬性將包括 SHF_ALLOC 位;否則,該位將關(guān)閉。通常,名稱由 重定位適用的部分。因此.text的重定位部分通常具有名稱.rel.text或.rela.text;
- .relaname,類型SHT_RELA:同relname。
- 其他:對于C++程序有些版本會有.ctors(有時也會是.init_array,見Can’t find .dtors and .ctors in binary)和dtors兩個節(jié)存儲構(gòu)造和析構(gòu)相關(guān)的代碼。
??帶有點 (.) 前綴的部分名稱是為系統(tǒng)保留的,但如果它們的現(xiàn)有含義令人滿意,應(yīng)用程序可以使用這些部分。 應(yīng)用程序可以使用不帶前綴的名稱以避免與系統(tǒng)部分沖突。 目標文件格式允許定義不在上面列表中的部分。 一個目標文件可能有多個同名的部分。
2.4 字符串表
??字符串表是一個存儲字符串的表格,而每個字符串是以NULL也就是\0為結(jié)尾的。字符串表格中索引為0處的字符串被定義為空字符串。符號表中保存的字符串是節(jié)名和目標文件中使用到的符號。而需要使用對應(yīng)字符串時,只需要在需要使用的地方指明對應(yīng)字符在字符串表中的索引即可,使用的字符串就是索引處到第一個\0之間的字符串。
2.5 符號表
??目標文件的符號表包含定位和重定位程序的符號定義和引用所需的信息。符號表索引是該數(shù)組的下標。索引0既指定表中的第一個條目,又用作未定義的符號索引。
typedef struct elf32_sym{Elf32_Word st_name;Elf32_Addr st_value;Elf32_Word st_size;unsigned char st_info;unsigned char st_other;Elf32_Half st_shndx; } Elf32_Sym; typedef struct elf64_sym {Elf64_Word st_name; /* Symbol name, index in string tbl */unsigned char st_info; /* Type and binding attributes */unsigned char st_other; /* No defined meaning, 0 */Elf64_Half st_shndx; /* Associated section index */Elf64_Addr st_value; /* Value of the symbol */Elf64_Xword st_size; /* Associated symbol size */ } Elf64_Sym;- st_name:存儲一個指向字符串表的索引來表示對應(yīng)符號的名稱;
- st_value:存儲對應(yīng)符號的取值,具體值依賴于上下文,可能是一個指針地址,立即數(shù)等。另外,不同對象文件類型的符號表條目對 st_value 成員的解釋略有不同:
- 在重定位文件中在可重定位文件中,st_value保存節(jié)索引為SHN_COMMON的符號的對齊約束;
- 在可重定位文件中,st_value保存已定義符號的節(jié)偏移量。 也就是說,st_value是從st_shndx標識的部分的開頭的偏移量;
- 在可執(zhí)行文件和共享對象文件中,st_value保存一個虛擬地址。 為了使這些文件的符號對動態(tài)鏈接器更有用,節(jié)偏移(文件解釋)讓位于與節(jié)號無關(guān)的虛擬地址(內(nèi)存解釋)。
- st_size:符號的大小,具體指為sizeof(instance),如果未知則為0;
- st_info:指定符號的類型和綁定屬性。可以用下面的代碼分別解析出bind,type,info三個屬性:
- BIND
- STB_LOCAL(0):局部符號在包含其定義的目標文件之外是不可見的。 同名的本地符號可以存在于多個文件中,互不干擾;
- STB_GLOBAL(1):全局符號對所有正在組合的目標文件都是可見的。 一個文件對全局符號的定義將滿足另一個文件對同一全局符號的未定義引用;
- STB_WEAK(2):弱符號類似于全局符號,但它們的定義具有較低的優(yōu)先級;
- [STB_LOPROC(13),STB_HIPROC(15)]:預(yù)留位,用于特殊處理器的特定含義;
- TYPE:
- STT_NOTYPE(0):符號的類型未指定;
- STT_OBJECT(1):符號與數(shù)據(jù)對象相關(guān)聯(lián),例如變量、數(shù)組等;
- STT_FUNC(2):符號與函數(shù)或其他可執(zhí)行代碼相關(guān)聯(lián);
- STT_SECTION(3):該符號與一個節(jié)相關(guān)聯(lián)。 這種類型的符號表條目主要用于重定位,通常具有STB_LOCALBIND屬性;
- STT_FILE(4):一個有STB_LOCAL的BIND屬性的文件符號的節(jié)索引為SHN_ABS。并且如果存在其他STB_LOCAL屬性的符號,則當(dāng)前符號應(yīng)該在其之前;
- [STT_LOPROC(13),STT_HIPROC(15)]:預(yù)留位,用于特殊處理器的特定含義;
- INFO:
- SHN_ABS:符號有一個絕對值,不會因為重定位而改變;
- SHN_COMMON:該符號標記尚未分配的公共塊。 符號的值給出了對齊約束,類似于節(jié)的 sh_addralign 成員。 也就是說,鏈接編輯器將為符號分配存儲空間,該地址是 st_value 的倍數(shù)。 符號的大小表明需要多少字節(jié);
- SHN_UNDEF:此節(jié)表索引表示該符號未定義。 當(dāng)鏈接編輯器將此對象文件與另一個定義指定符號的文件組合時,此文件對符號的引用將鏈接到實際定義;
- st_other:該成員當(dāng)前持有 0 并且沒有定義的含義;
- st_shndx:每個符號都有屬于的節(jié),當(dāng)前成員存儲的就是對應(yīng)節(jié)的索引。
3 ELF文件示例
??下面是使用下面的代碼編譯生成動態(tài)庫libadd.so作為示例:
//add.h int add(int a, int b); static int mult(int a, int b); //add.c //編譯命令gcc add.c -shared -o libadd.so extern int extern_value; static int static_value = 1; static int static_value1;int add(int a, int b){return 0; }static int mult(int a, int b){return 1; }3.1 ELF Header
??使用命令readelf -h <ELF文件名>查看ELF文件的Header。
//readelf -h libadd.so ELF Header:Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00Class: ELF64Data: 2's complement, little endianVersion: 1 (current)OS/ABI: UNIX - System VABI Version: 0Type: DYN (Shared object file)Machine: Advanced Micro Devices X86-64Version: 0x1Entry point address: 0x4a0Start of program headers: 64 (bytes into file)Start of section headers: 6000 (bytes into file)Flags: 0x0Size of this header: 64 (bytes)Size of program headers: 56 (bytes)Number of program headers: 7Size of section headers: 64 (bytes)Number of section headers: 24Section header string table index: 23??從上面的Magic Number中能夠看出:當(dāng)前文件類型為64bit的共享庫,小端存儲,版本為1,機器架構(gòu)為x86-64,程序頭表項有7項,節(jié)頭表項有24項。
3.2 Program Header Table
??使用命令readelf -l <ELF文件名>查看程序頭表;
//readelf -l libadd.so Elf file type is DYN (Shared object file) Entry point 0x4a0 There are 7 program headers, starting at offset 64 Program Headers:Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000674 0x0000000000000674 R E 0x200000LOAD 0x0000000000000e80 0x0000000000200e80 0x0000000000200e80 0x00000000000001a4 0x00000000000001b0 RW 0x200000DYNAMIC 0x0000000000000e90 0x0000000000200e90 0x0000000000200e90 0x0000000000000150 0x0000000000000150 RW 0x8NOTE 0x00000000000001c8 0x00000000000001c8 0x00000000000001c8 0x0000000000000024 0x0000000000000024 R 0x4GNU_EH_FRAME 0x00000000000005a8 0x00000000000005a8 0x00000000000005a8 0x000000000000002c 0x000000000000002c R 0x4GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 0x10GNU_RELRO 0x0000000000000e80 0x0000000000200e80 0x0000000000200e80 0x0000000000000180 0x0000000000000180 R 0x1Section to Segment mapping:Segment Sections...00 .note.gnu.build-id .gnu.hash .dynsym .dynstr .rela.dyn .init .plt .plt.got .text .fini .eh_frame_hdr .eh_frame01 .init_array .fini_array .dynamic .got .got.plt .data .bss02 .dynamic03 .note.gnu.build-id04 .eh_frame_hdr0506 .init_array .fini_array .dynamic .got??從上面看出上半部分的內(nèi)容基本和程序頭表項的每個字段基本對應(yīng)。從下面的Segment Sections可以看出一個Segment是多個Section的集合。
3.3 Section Header Table
??使用命令readelf -S <ELF文件名>查看節(jié)頭表的內(nèi)容。
? tmp readelf -S libadd.so There are 24 section headers, starting at offset 0x1770:Section Headers:[Nr] Name Type Address Offset Size EntSize Flags Link Info Align[ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0[ 1] .note.gnu.build-i NOTE 00000000000001c8 000001c8 0000000000000024 0000000000000000 A 0 0 4[ 2] .gnu.hash GNU_HASH 00000000000001f0 000001f0 000000000000003c 0000000000000000 A 3 0 8[ 3] .dynsym DYNSYM 0000000000000230 00000230 0000000000000108 0000000000000018 A 4 1 8[ 4] .dynstr STRTAB 0000000000000338 00000338 000000000000007d 0000000000000000 A 0 0 1[ 5] .rela.dyn RELA 00000000000003b8 000003b8 00000000000000a8 0000000000000018 A 3 0 8[ 6] .init PROGBITS 0000000000000460 00000460 0000000000000017 0000000000000000 AX 0 0 4[ 7] .plt PROGBITS 0000000000000480 00000480 0000000000000010 0000000000000010 AX 0 0 16[ 8] .plt.got PROGBITS 0000000000000490 00000490 0000000000000008 0000000000000008 AX 0 0 8[ 9] .text PROGBITS 00000000000004a0 000004a0 00000000000000fc 0000000000000000 AX 0 0 16[10] .fini PROGBITS 000000000000059c 0000059c 0000000000000009 0000000000000000 AX 0 0 4[11] .eh_frame_hdr PROGBITS 00000000000005a8 000005a8 000000000000002c 0000000000000000 A 0 0 4[12] .eh_frame PROGBITS 00000000000005d8 000005d8 000000000000009c 0000000000000000 A 0 0 8[13] .init_array INIT_ARRAY 0000000000200e80 00000e80 0000000000000008 0000000000000008 WA 0 0 8[14] .fini_array FINI_ARRAY 0000000000200e88 00000e88 0000000000000008 0000000000000008 WA 0 0 8[15] .dynamic DYNAMIC 0000000000200e90 00000e90 0000000000000150 0000000000000010 WA 4 0 8[16] .got PROGBITS 0000000000200fe0 00000fe0 0000000000000020 0000000000000008 WA 0 0 8[17] .got.plt PROGBITS 0000000000201000 00001000 0000000000000018 0000000000000008 WA 0 0 8[18] .data PROGBITS 0000000000201018 00001018 000000000000000c 0000000000000000 WA 0 0 8[19] .bss NOBITS 0000000000201024 00001024 000000000000000c 0000000000000000 WA 0 0 4[20] .comment PROGBITS 0000000000000000 00001024 0000000000000029 0000000000000001 MS 0 0 1[21] .symtab SYMTAB 0000000000000000 00001050 00000000000004c8 0000000000000018 22 41 8[22] .strtab STRTAB 0000000000000000 00001518 0000000000000193 0000000000000000 0 0 1[23] .shstrtab STRTAB 0000000000000000 000016ab 00000000000000c3 0000000000000000 0 0 1 Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),l (large), p (processor specific)??從上面看出內(nèi)容基本和程序頭表項的每個字段基本對應(yīng)。除了上面提到的特殊的節(jié)也有一些額外的節(jié),比如.got.plt。
3.4 符號表
??readelf -s <ELF文件名>查看符號表。
//readelf -s libadd.so Symbol table '.dynsym' contains 11 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND1: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __cxa_finalize2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab4: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__5: 0000000000201024 0 NOTYPE GLOBAL DEFAULT 18 _edata6: 0000000000201030 0 NOTYPE GLOBAL DEFAULT 19 _end7: 0000000000000460 0 FUNC GLOBAL DEFAULT 6 _init8: 000000000000057a 17 FUNC GLOBAL DEFAULT 9 add9: 0000000000201024 0 NOTYPE GLOBAL DEFAULT 19 __bss_start10: 000000000000059c 0 FUNC GLOBAL DEFAULT 10 _finiSymbol table '.symtab' contains 51 entries:Num: Value Size Type Bind Vis Ndx Name0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND1: 00000000000001c8 0 SECTION LOCAL DEFAULT 12: 00000000000001f0 0 SECTION LOCAL DEFAULT 23: 0000000000000230 0 SECTION LOCAL DEFAULT 34: 0000000000000338 0 SECTION LOCAL DEFAULT 45: 00000000000003b8 0 SECTION LOCAL DEFAULT 56: 0000000000000460 0 SECTION LOCAL DEFAULT 67: 0000000000000480 0 SECTION LOCAL DEFAULT 78: 0000000000000490 0 SECTION LOCAL DEFAULT 89: 00000000000004a0 0 SECTION LOCAL DEFAULT 910: 000000000000059c 0 SECTION LOCAL DEFAULT 1011: 00000000000005a8 0 SECTION LOCAL DEFAULT 1112: 00000000000005d8 0 SECTION LOCAL DEFAULT 1213: 0000000000200e80 0 SECTION LOCAL DEFAULT 1314: 0000000000200e88 0 SECTION LOCAL DEFAULT 1415: 0000000000200e90 0 SECTION LOCAL DEFAULT 1516: 0000000000200fe0 0 SECTION LOCAL DEFAULT 1617: 0000000000201000 0 SECTION LOCAL DEFAULT 1718: 0000000000201018 0 SECTION LOCAL DEFAULT 1819: 0000000000201024 0 SECTION LOCAL DEFAULT 1920: 0000000000000000 0 SECTION LOCAL DEFAULT 2021: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c22: 00000000000004a0 0 FUNC LOCAL DEFAULT 9 deregister_tm_clones23: 00000000000004e0 0 FUNC LOCAL DEFAULT 9 register_tm_clones24: 0000000000000530 0 FUNC LOCAL DEFAULT 9 __do_global_dtors_aux25: 0000000000201024 1 OBJECT LOCAL DEFAULT 19 completed.769826: 0000000000200e88 0 OBJECT LOCAL DEFAULT 14 __do_global_dtors_aux_fin27: 0000000000000570 0 FUNC LOCAL DEFAULT 9 frame_dummy28: 0000000000200e80 0 OBJECT LOCAL DEFAULT 13 __frame_dummy_init_array_29: 0000000000000000 0 FILE LOCAL DEFAULT ABS add.c30: 0000000000201020 4 OBJECT LOCAL DEFAULT 18 static_value31: 0000000000201028 4 OBJECT LOCAL DEFAULT 19 static_value132: 000000000000058b 17 FUNC LOCAL DEFAULT 9 mult33: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c34: 0000000000000670 0 OBJECT LOCAL DEFAULT 12 __FRAME_END__35: 0000000000000000 0 FILE LOCAL DEFAULT ABS36: 0000000000200e90 0 OBJECT LOCAL DEFAULT 15 _DYNAMIC37: 0000000000201028 0 OBJECT LOCAL DEFAULT 18 __TMC_END__38: 0000000000201018 0 OBJECT LOCAL DEFAULT 18 __dso_handle39: 00000000000005a8 0 NOTYPE LOCAL DEFAULT 11 __GNU_EH_FRAME_HDR40: 0000000000201000 0 OBJECT LOCAL DEFAULT 17 _GLOBAL_OFFSET_TABLE_41: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __cxa_finalize42: 0000000000000460 0 FUNC GLOBAL DEFAULT 6 _init43: 000000000000057a 17 FUNC GLOBAL DEFAULT 9 add44: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMCloneTable45: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterTMCloneTab46: 0000000000201024 0 NOTYPE GLOBAL DEFAULT 19 __bss_start47: 000000000000059c 0 FUNC GLOBAL DEFAULT 10 _fini48: 0000000000201024 0 NOTYPE GLOBAL DEFAULT 18 _edata49: 0000000000201030 0 NOTYPE GLOBAL DEFAULT 19 _end50: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__??符號表可以看出有兩個分別為dynsym和symtab,symtab中包含所有在程序中出現(xiàn)的符號以及一些庫函數(shù)的符號,而dynsym中的符號是symtab中符號的子集,僅僅出現(xiàn)了外部可以看到的符號(靜態(tài)函數(shù)mult的符號在dynsym就看不到)。這是因為dynsym中的符號只有在動態(tài)鏈接時也就是運行時才能被解析。
4 參考文獻
- Executable and Linkable Format
- Tool Interface Standard (TIS) Executable and Linking Format (ELF)Specification Version 1.2
- What’s the difference of section and segment in ELF file format
- ELF文件格式
總結(jié)
- 上一篇: 【java】本地客户端内嵌浏览器1 -
- 下一篇: input子系统基础之按键5——按键驱动