0. 簡介
在linux系統的可執行文件(ELF文件)中,開頭是一個文件頭,用來描述程序的布局,整個文件的屬性等信息,包括文件是否可執行、靜態還是動態鏈接及入口地址等信息;如下圖所示:?
程序文件中包含了程序頭,程序的入口地址等信息不需要寫死,調用代碼可以通用,根據實際情況加載;此時的文件不是純碎的二進制可執行文件了,因為包含的程序頭不是可執行代碼;將這種包含程序頭的文件讀入到內存后,從程序頭中讀取入口地址,跳轉到入口地址執行;
0.1 文件格式
linux系統中用C語言編寫完代碼,使用編譯器gcc編譯生成的是ELF格式的二進制文件;
ELF指:Executable and Linkable Format,可執行鏈接格式;本文中的目標文件指各種類型符合ELF規范的文件,如:二進制可執行文件、Linux下的.o目標文件和.so動態庫文件;
可執行文件(Executable file):經過編譯鏈接后,可以直接運行的程序文件;
共享目標文件(Shared object file):動態鏈接庫,在可執行文件被加載的過程中動態鏈接,成為程序代碼的一部分;
可重定位文件(Relocatable file):可重定位文件即目標文件,是源文件編譯后但未完成鏈接的半成品,被用于與其他目標文件合并鏈接,以構建出二進制可執行文件或動態鏈接庫;
核心轉儲文件(Core dump file):當進程意外終止時,系統可以將該進程的地址空間的內容及終止時的一些信息轉儲到核心轉儲文件;
0.2 段和節
程序中的段(Segment)和節(Secton)是真正的程序體;段包括代碼段和數據段等,段是由節組成的,多個節經過鏈接后被合并為一個段;
段和節的信息用header描述,程序頭是program header,節頭是section header;段和節的大小和數量都是不固定的,需要用專門的數據結構來描述,即程序頭表(program header table)和節頭表(section header table),這是兩個數組,元素分別是程序頭(program header)和節頭(section header);程序頭表(program header table)中的元素全是程序頭(program header),節頭表(section header table)中的元素全是節頭(section header);程序頭表是用來描述段(Segment)的,成為段頭表;段是程序本身的組成部分;
由于段和節的大小和數量都是不固定的,程序頭表和節頭表的大小也不固定,兩個表在程序文件中的位置也不固定;需要在一個固定的位置,用一個固定大小的數據結構來描述程序頭表和節頭表的大小和位置信息,即位于文件最開始部分的ELF header;
1. ELF header
ELF文件分為文件頭和文件體兩部分;先用ELF header從文件全局概要出程序中程序頭表、節頭表的位置和大小等信息;然后從程序頭表和節頭表中分別解析出各個段和節的位置和大小等信息;
可執行文件和待重定位文件,文件最開頭的部分是ELF header;程序頭表對于可執行文件是必須的,而對于待重定位文件是可選的;
ELF文件頭用Elf32_Ehdr和Elf64_Ehdr結構體表示;
// include/uapi/linux/elf.h
#define EI_NIDENT 16
typedef 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;
e_ident[EI_NIDENT]是16字節大小的數組,用來表示ELF字符等信息,開頭四個字節是固定不變的elf文件魔數,0x7f 0x45 0x4c 0x46;
e_type:2字節,用來指定ELF目標文件的類型;
// include/uapi/linux/elf-em.h
/* These constants define the various ELF target machines */
#define EM_NONE 0
#define EM_M32 1
#define EM_SPARC 2
#define EM_386 3
#define EM_68K 4
#define EM_88K 5
#define EM_486 6 /* Perhaps disused */
#define EM_860 7
#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */
/* Next two are historical and binaries and
modules of these types will be rejected by Linux. */
#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */
#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */
#define EM_PARISC 15 /* HPPA */
#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
#define EM_PPC 20 /* PowerPC */
......
e_machine:2字節,用來描述ELF目標文件的體系結構類型,即要在哪種硬件平臺運行;
// include/uapi/linux/elf-em.h
/* These constants define the various ELF target machines */
#define EM_NONE 0
#define EM_M32 1
#define EM_SPARC 2
#define EM_386 3
#define EM_68K 4
#define EM_88K 5
#define EM_486 6 /* Perhaps disused */
#define EM_860 7
#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */
/* Next two are historical and binaries and
modules of these types will be rejected by Linux. */
#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */
#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */
?
#define EM_PARISC 15 /* HPPA */
#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
#define EM_PPC 20 /* PowerPC */
......
2. 程序頭表
程序頭表(也稱為段表)是一個描述文件中各個段的數組,程序頭表描述了文件中各個段在文件中的偏移位置及段的屬性等信息;從程序頭表里可以得到每個段的所有信息,包括代碼段和數據段等;各個段的內容緊跟ELF文件頭保存;程序頭表中各個段用Elf32_Phdr或Elf64_Phdr結構體表示;
// include/uapi/linux/elf.h
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;
Elf32_Phdr或Elf64_Phdr結構體用來描述位于磁盤上的程序中的一個段;
p_type:4字節,用來指明程序中該段的類型;
// include/uapi/linux/elf.h
/* These constants define the permissions on sections in the program header, p_flags. */
#define PF_R 0x4 // 可讀
#define PF_W 0x2 // 可寫
#define PF_X 0x1 // 可執行
p_flags:4字節,用來指明與本段相關的標志;
// include/uapi/linux/elf.h
/* These constants define the permissions on sections in the program header, p_flags. */
#define PF_R 0x4 // 可讀
#define PF_W 0x2 // 可寫
#define PF_X 0x1 // 可執行
3. 節頭表
節頭表中各個節用Elf32_Shdr或Elf64_Shdr結構體表示;
// include/uapi/linux/elf.h
typedef struct elf32_shdr { 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_shdr { Elf64_Word sh_name; /* Section name, index in string tbl */
Elf64_Word sh_type; /* Type of section */
Elf64_Xword sh_flags; /* Miscellaneous section attributes */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Size of section in bytes */
Elf64_Word sh_link; /* Index of another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
Elf32_Shdr或Elf64_Shdr結構體用來描述位于磁盤上的程序中的一個節;
sh_name
節名稱sh_name的值是字符串的一個索引,節名稱字符串以''結尾,字符串統一存放在字符串表中,使用sh_name的值作為字符串表的索引,找到對應的字符串即為節名稱;
字符串表中包含多個以''結尾的字符串;在目標文件中,這些字符串通常是符號的名字或節的名字,需要引用某些字符串時,只需要提供該字符串在字符串表中的序號即可;
字符串表中的第一個字符串(序號為0)是空串,即'',可以用于表示沒有名字或一個空的名字;如果字符串表為空,節頭中的sh_size值為0;
sh_type:節的類型;
// include/uapi/linux/elf.h
/* sh_flags */
#define SHF_WRITE 0x1 // 可寫
#define SHF_ALLOC 0x2 // 該節包含內存在進程運行時需要占用內存單元
#define SHF_EXECINSTR 0x4 // 該節內容是指令代碼
#define SHF_MASKPROC 0xf0000000
sh_flags:節的標志;
// include/uapi/linux/elf.h
/* sh_flags */
#define SHF_WRITE 0x1 // 可寫
#define SHF_ALLOC 0x2 // 該節包含內存在進程運行時需要占用內存單元
#define SHF_EXECINSTR 0x4 // 該節內容是指令代碼
#define SHF_MASKPROC 0xf0000000
4. readelf命令
readelf命令用于查看ELF格式的文件信息,
$ readelf -h file // 顯示ELF文件頭
$ readelf -l file // 顯示所有的程序頭
$ readelf -S file // 顯示所有的節頭
$ readelf -e file // 顯示ELF文件頭、程序頭、節頭全部的頭信息,相當于同時運行參數-h、-l、-S
命令:
$ readelf -h file // 顯示ELF文件頭
$ readelf -l file // 顯示所有的程序頭
$ readelf -S file // 顯示所有的節頭
$ readelf -e file // 顯示ELF文件頭、程序頭、節頭全部的頭信息,相當于同時運行參數-h、-l、-S
5. ELF文件實例
針對32位系統,通過一個極為簡單的C語言的編譯和分析,進行ELF文件中各結構體成員功能分析;
代碼示例:
int main(void)
{ while (1);
return 0;
}
5.0 生成可執行文件
編譯為中間文件,并鏈接為目標文件;
$ gcc -c -o main.o main.c
$ ld main.o -Ttext 0xc0001500 -e main -o kernel.bin
通過file命令查看main.o文件屬性;main.o文件是32位大端可重定向的ELF文件,機器平臺為PowerPC;
$ file main.o main.o: ELF 32-bit MSB relocatable, PowerPC or cisco 4500, version 1 (SYSV), not stripped
通過file命令查看kernel.bin文件屬性;kernel.bin文件是32位大端可執行的ELF文件,機器平臺為PowerPC;
$ file kernel.bin kernel.bin: ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1 (SYSV), statically linked, not stripped
通過nm命令查看main.o文件的符號表,符號表中僅有一個main函數符號;
$ nm main.o 00000000 T main
通過nm查看kernel.bin文件的符號表,
$ nm kernel.bin c0011510 A __bss_start c0011510 A _edata c0011510 A _end c0001500 T main
5.1 ELF header
$ xxd -u -a -g 1 -s 0 -l 0x100 kernel.bin
0000000: 7F 45 4C 46 01 02 01 00 00 00 00 00 00 00 00 00 .ELF............
0000010: 00 02 00 14 00 00 00 01 C0 00 15 00 00 00 00 34 ...............4
0000020: 00 00 15 74 00 00 00 00 00 34 00 20 00 02 00 28 ...t.....4. ...(
0000030: 00 06 00 03 00 00 00 01 00 00 00 00 C0 00 00 00 ................
00~0x0F是e_ident數組,e_ident[0]~e_ident[3]的四個字節是固定的ELF魔數,0x7f, 0x45, 0x4c, 0x46;e_ident[4] = 1表示文件是32位的ELF文件,e_ident[5] = 2表示文件時大端字節序,e_ident[6] = 1表示默認版本,e_ident[7]~e_ident[15]的9個字節置為0;
0x10~0x11是e_type,2個字節,e_type = 0x00 02,表示類型是ET_EXEC,即可執行文件;
0x12~0x13是e_machine,2個字節,e_machine = 0x00 14,表示機器是EM_PPC,即power pc硬件平臺;
0x14~0x17是e_version,4個字節,e_version = 0x00 00 00 01,表示版本信息;
0x18~0x1B是e_entry,4個字節,e_entry = 0xC0 00 15 00,表示程序的虛擬入口地址;
0x1C~0x1F是e_phoff ,4個字節,e_phoff = 0x00 00 00 34,表示程序頭表在文件中的偏移量;
0x20~0x23是e_shoff,4個字節,e_shoff = 0x00 00 15 74,表示節頭表在文件內的偏移量;
0x24~0x27是e_flags,4個字節,e_flags = 0x00 00 00 00,表示屬性;
0x28~0x29是e_ehsize,2個字節,e_ehsize = 0x00 34,表示ELF header大小是0x34字節;程序頭表緊跟ELF header之后;
0x2A~0x2B是e_phentsize,2個字節,e_phentsize = 0x00 20,表示program header結構ELF32_Phdr的大小,此處為0x20;
0x2C~0x2D是e_phnum,2個字節,e_phnum = 0x00 02,表示程序頭表中段的個數,此處為2個段;
0x2E~0x2F是e_shentsize,2個字節,e_shentsize = 0x00 28,表示節頭表中節的大小,此處為0x28;
0x30~0x31是e_shnum,2個字節,e_shnum = 0x00 06,表示節頭表中節的個數,此處為6個節;
0x32~0x33是e_shstrndx,2個字節,e_shstrndx = 0x00 03,表示string name table在節頭表中的索引為3;
5.2 程序頭表
$ xxd -u -a -g 1 -s 0 -l 0x100 kernel.bin ......
0000030: 00 06 00 03 00 00 00 01 00 00 00 00 C0 00 00 00 ................
0000040: C0 00 00 00 00 00 15 10 00 00 15 10 00 00 00 05 ................
0000050: 00 01 00 00 64 74 E5 51 00 00 00 00 00 00 00 00 ....dt.Q........
0000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 ................
0000070: 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 ................
在ELF header中,程序頭表偏移e_phoff = 0x00 00 00 34,所以程序頭表的偏移位置為0x34,程序頭表中段的大小e_phentsize = 0x00 20,段的個數e_phnum = 0x00 02,表示程序頭表中有兩個段,每個段大小0x20字節;ELF header大小e_ehsize = 0x00 34,程序頭表緊跟ELF header之后;0x34~0x73之間共0x40個字節,是兩個程序頭的內容,從0x34地址開始,按照Elf32_Phdr結構分析;
第一個段頭在0x34~0x53,共0x20個字節;
0x34~0x37是p_type,4個字節,p_type = 0x00 00 00 01,表示類型為PT_LOAD,為可加載程序段;
0x38~0x3B是p_offset,4個字節,p_offset = 0x00 00 00 00,表示本段在文件內的偏移量;
0x3C~0x3F是p_vaddr,4個字節,p_vaddr = 0xC0 00 00 00,表示本段被加載到內存后的起始虛擬地址;
0x40~0x43是p_paddr,4個字節,p_paddr = 0xC0 00 00 00,保留,和p_vaddr一致;
0x44~0x47是p_filesz,4個字節,p_filesz = 0x00 00 15 10,表示本段在文件中的大?。?/p>
0x48~0x4B是p_memsz,4個字節,p_memsz = 0x00 00 15 10,表示本段在內存中的大小;
0x4C~0x4F是p_flags,4個字節,p_flags = 0x00 00 00 05,表示本段的屬性,5 = 4 + 1 = PF_R + PF_X,此處為可讀和可執行權限;
0x50~0x53是p_align,4個字節,p_align = 0x00 01 00 00,表示本段的對齊方式,此處為64K對齊;
第二個段頭在0x54~0x73,共0x20個字節;
0x54~0x57是p_type,4個字節,p_type = 0x64 74 E5 51,表示類型為PT_GNU_STACK,為GNU的棧段;
// include/uapi/linux/elf.h
#define PT_LOOS 0x60000000
#define PT_GNU_STACK (PT_LOOS + 0x474e551)
之后的分析省略;
5.3 節頭表
在ELF header中,節頭表偏移e_shoff = 0x00 00 15 74,所以節頭表的偏移位置為0x1574,節頭表中段的大小e_shentsize = 0x00 28,段的個數e_shnum = 0x00 06,表示節頭表中有六個節,每個節0x28字節;0x1574~0x1663共0xF0個字節,是六個節頭的內容,從0x1574地址開始,按照Elf32_Shdr結構分析;
$ xxd -u -g 1 -s 0x1570 -l 0x100 kernel.bin
0001570: 74 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 t...............
0001580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0001590: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1B ................
00015a0: 00 00 00 01 00 00 00 06 C0 00 15 00 00 00 15 00 ................
00015b0: 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 04 ................
00015c0: 00 00 00 00 00 00 00 21 00 00 00 01 00 00 00 00 .......!........
00015d0: 00 00 00 00 00 00 15 10 00 00 00 38 00 00 00 00 ...........8....
00015e0: 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 11 ................
00015f0: 00 00 00 03 00 00 00 00 00 00 00 00 00 00 15 48 ...............H
0001600: 00 00 00 2A 00 00 00 00 00 00 00 00 00 00 00 01 ...*............
0001610: 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 00 ................
0001620: 00 00 00 00 00 00 16 64 00 00 00 80 00 00 00 05 .......d........
0001630: 00 00 00 04 00 00 00 04 00 00 00 10 00 00 00 09 ................
0001640: 00 00 00 03 00 00 00 00 00 00 00 00 00 00 16 E4 ................
0001650: 00 00 00 25 00 00 00 00 00 00 00 00 00 00 00 01 ...%............
0001660: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
第一個節頭在0x1574~0x159B,共0x28個字節;
0x1574~0x1577是sh_name,4個字節,sh_name = 0x00 00 00 00,表示節名稱是在字符串表索引值為0x00的字符串,該字符串為空;
0x1578~0x157B是sh_type,4個字節,sh_type = 0x00 00 00 00,表示該節是一個無效的節頭,沒有對應的節;該節中其他成員也無意義;
第二個節頭在0x159C~0x15C3,共0x28個字節;
0x159C~0x159F是sh_name,4個字節,sh_name = 0x00 00 00 1B,表示節名稱是在字符串表索引值為0x1B的字符串;該字符串為".text";
0x15A0~0x15A3是sh_type,4個字節,sh_type = 0x00 00 00 01,表示
0x15A4~0x15A7是sh_flags,4個字節,sh_flags = 0x00 00 00 06,0x06 = 0x04 + 0x02 = SHF_EXECINSTR + SHF_ALLOC,表示該節內容是指令代碼,并且包含內存在進程運行時需要占用內存單元;
0x15A8~0x15AB是sh_addr,4個字節,sh_addr = 0xC0 00 15 00,表示該節在內存中的起始地址,節映射到虛擬地址空間中的位置;
0x15AC~0x15AF是sh_offset,4個字節,sh_offset = 0x00 00 15 00,表示該節在文件中的起始位置;
0x15B0~0x15B3是sh_size,4個字節,sh_size = 0x00 00 00 10,表示該節的大??;
0x15B4~0x15B7是sh_link,4個字節,sh_link = 00 00 00 00
0x15B8~0x15BB是sh_info,4個字節,sh_info = 0x00 00 00 00
0x15BC~0x15BF是sh_addralign,4個字節,sh_addralign = 0x00 00 00 04,表示該節的數據在內存中以16字節對齊;
0x15C0~0x15C3是sh_entsize,4個字節,sh_entsize = 0x00 00 00 00
以后各節的解析省略;
5.4 readelf查看結果
1) 顯示程序的ELF文件頭
$ readelf -h kernel.bin
ELF Header: Magic: 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, big endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: PowerPC Version: 0x1 Entry point address: 0xc0001500 Start of program headers: 52 (bytes into file)
Start of section headers: 5492 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 2
Size of section headers: 40 (bytes)
Number of section headers: 6
Section header string table index: 3
2) 顯示程序所有的程序頭
$ readelf -l kernel.bin
Elf file type is EXEC (Executable file)Entry point 0xc0001500There are 2 program headers, starting at offset 52Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x00000000 0xc0000000 0xc0000000
0x00001510 0x00001510 R E 0x10000
GNU_STACK 0x00000000 0x00000000 0x00000000
0x00000000 0x00000000 RW 0x4
Section to Segment mApping:
Segment Sections...
00 .text
01
3) 顯示程序所有的節頭
$ readelf -S kernel.bin
There are 6 section headers, starting at offset 0x1574:
Section Headers:
[Nr] Name Type Addr Off
Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000
000000 00 0 0 0
[ 1] .text PROGBITS c0001500 001500
000010 00 AX 0 0 4
[ 2] .comment PROGBITS 00000000 001510
000038 00 0 0 1
[ 3] .shstrtab STRTAB 00000000 001548
00002a 00 0 0 1
[ 4] .symtab SYMTAB 00000000 001664
000080 10 5 4 4
[ 5] .strtab STRTAB 00000000 0016e4
000025 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings) I (info),
L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
4) 綜合顯示程序所有的頭信息,包含ELF文件頭、程序頭、節頭信息;
$ readelf -e kernel.bin
ELF Header: Magic: 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, big endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: PowerPC Version: 0x1 Entry point address: 0xc0001500 Start of program headers: 52 (bytes into file)
Start of section headers: 5492 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 2
Size of section headers: 40 (bytes)
Number of section headers: 6
Section header string table index: 3
There are 6 section headers, starting at offset 0x1574:
Section Headers:
[Nr] Name Type Addr Off
Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000
000000 00 0 0 0
[ 1] .text PROGBITS c0001500 001500
000010 00 AX 0 0 4
[ 2] .comment PROGBITS 00000000 001510
000038 00 0 0 1
[ 3] .shstrtab STRTAB 00000000 001548
00002a 00 0 0 1
[ 4] .symtab SYMTAB 00000000 001664
000080 10 5 4 4
[ 5] .strtab STRTAB 00000000 0016e4
000025 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings) 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 file type is EXEC (Executable file)
Entry point 0xc0001500
There are 2 program headers, starting at offset 52
Program Headers: Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flg Align LOAD 0x00000000 0xc0000000 0xc0000000
0x00001510 0x00001510 R E 0x10000
GNU_STACK 0x00000000 0x00000000 0x00000000
0x00000000 0x00000000 RW 0x4
Section to Segment mapping:
Segment Sections...
00 .text
01
通過readelf命令查看的結果,和按照ELF文件分析的結果,對比結果一致;
6. 總結
在Linux系統的可執行文件(ELF文件)中,開頭是一個文件頭,用來描述程序的布局,整個文件的屬性等信息,包括文件是否可執行、靜態還是動態鏈接及入口地址等信息;生成的文件不是純碎的二進制可執行文件了,因為包含的程序頭不是可執行代碼;將這種包含程序頭的文件讀入到內存后,從程序頭中讀取入口地址,跳轉到入口地址執行;
參考資料
《操作系統真象還原》
程序編譯-匯編-鏈接的理解!—03-ELF頭和節頭表
ELF文件-節和節頭