ELF 二进制格式(二进制中的进位是什么意思)
看《深入Linux内核架构》时,经常提到ELF格式,故去阅读其附录,并做下此笔记ELF 是什么全称“Executable and Linkable Format”,即可执行和可连接格式,Linux下的可执行文件,链接文件,共享库文件(.so),目标文件(.o)等都是此格式
ELF 文件结构下图左为链接文件,右则为执行文件视图

各部分的基本介绍ELF文件头:存储文件类型和大小的有关信息,以及文件加载后程序执行的入口信息程序头表:可执行文件的数据在虚拟地址空间中的组织方式,以及段的数目,位置以及用途节:将文件分成一个个节区,每个节区都有其对应的功能,如符号表,哈希表等
段:就是将文件分成一段一段映射到内存中段中通常包括一个或多个节区节头表:存储段的附加信息通过实例深入了解ELF文件简单写一份c代码用于测试,采用linux下的readelf工具进行探究// elf-test.c 。
#includeintadd(inta,intb){printf("Add number\n");returna+b;}intmain(){inta,b;a=1;b=2;intres=
add(a,b);printf("Result: %d\n",res);return0;}然后执行以下命令,通过gcc生成相应的可执行文件和目标文件dman@DESKTOP:/mnt/a/os/pla/code$ gcc elf-test.c -o
test dman@DESKTOP:/mnt/a/os/pla/code$ gcc elf-test.c -c -o test.o PS: 但是我发现这样,使用file命令查看test文件发现文件类型是
shared object(共享库类型)dman@DESKTOP:/mnt/a/os/pla/code$ file test test: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV)...
经过一番搜索发现gcc会默认开--enable-default-pie 模式,如下dman@DESKTOP:/mnt/a/os/pla/code$ gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/7/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-linux-gnu Configured with: .... --enable-default-pie .... Thread model: posix gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)
gcc中PIE(Position-Independent-Executable)模式能用来创建介于共享库和通常可执行代码之间的代码,能像共享库一样可重分配地址的程序,故test文件类型是share object
引入PIE的原因是让程序能装载在随机的地址,通常情况下,内核都在固定的地址运行,如果能改用位置无关,那攻击者就很难借助系统中的可执行码实施攻击了类似缓冲区溢出之类的攻击将无法实施故可以添加-no-pie。
参数关闭该功能,故生成可执行文件test命令为dman@DESKTOP:/mnt/a/os/pla/code$ gcc elf-test.c -no-pie -o test 通过file命令可以看到test
文件类型为executabletest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked...以及test.o
文件类型为relocatabletest.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped使用readelf查看文件头
这里为了方便就直接在显示结果上添加注释信息先查看可执行文件dman@DESKTOP:/mnt/a/os/pla/code$ readelf -h test ELF Header: Magic: 7f 45(E)4c(L)46(F)02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64(64位机) Data: 2s complement, little endian(小端序) Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type(文件类型): EXEC (Executable file) (可执行文件) Machine: Advanced Micro Devices X86-64 (运行设备) Version: 0x1 Entry point address: 0x400450 Start of program headers: 64 (bytes into file) Start of section headers: 6456 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 9 Size of section headers: 64 (bytes) Number of section headers: 29 Section header string table index: 28
再查看链接文件test.o,这里略去和test文件头相同部分 dman@DESKTOP:/mnt/a/os/pla/code$ readelf -h test.o ELF Header: ... Type: REL
(Relocatable file) (可重定位文件) ... Entry point address: 0x0 Start of program headers:
0(bytes into file) (不需要程序头表) Start of section headers: 992(bytes into file) ... Size of program headers:
0(bytes) (不需要程序头表) Number of program headers: 0 (不需要程序头表) Size of section headers:
64(bytes) Number of section headers: 13 Section header string table index: 12readelf查看程序头表
dman@DESKTOP:/mnt/a/os/pla/code$ readelf -l test Elf file type is EXEC (Executable file) Entry point 0x400450 There are
9 program headers, starting at offset 64 Program Headers: Type Offset(偏移) VirtAddr(虚拟地址)PhysAddr(物理地址) FileSiz(文件大小)MemSiz(内存大小) Flags(访问权限) Align(字段对齐) PHDR (保存程序头表) 0x0000000000000040 0x0000000000400040 0x0000000000400040 0x00000000000001f8 0x00000000000001f8 R 0x8 INTERP(需要调用的解释器) 0x0000000000000238 0x0000000000400238 0x0000000000400238 0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD (一个需要从二进制文件映射到虚拟地址空间的段,其中保存了程序目标代码等) 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x00000000000007a0 0x00000000000007a0 R E 0x200000 LOAD 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10 0x0000000000000228 0x0000000000000230 RW 0x200000 DYNAMIC(保存动态链接器需要的信息) 0x0000000000000e20 0x0000000000600e20 0x0000000000600e20 0x00000000000001d0 0x00000000000001d0 RW 0x8 NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254 0x0000000000000044 0x0000000000000044 R 0x4 GNU_EH_FRAME 0x000000000000063c 0x000000000040063c 0x000000000040063c 0x0000000000000044 0x0000000000000044 R 0x4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 0x10 GNU_RELRO 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10 0x00000000000001f0 0x00000000000001f0 R 0x1 Section to Segment mapping: Segment Sections...
0001 .interp 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .dynamic .got .got.plt .data .bss 04 .dynamic 05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr 0708 .init_array .fini_array .dynamic .gotreadelf查看节头表可执行文件dman@DESKTOP:/mnt/a/os/pla/code$ readelf -S test.o There are
13 section headers, starting at offset 0x3e0: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align
[ 0] NULL 00000000000000000000000000000000000000000000000000000000000[ 1
] .text PROGBITS(程序必须要解释的类型) 00000000000000000000004000000000000000690000000000000000
AX 001[ 2] .rela.text RELA(重定位信息) 0000000000000000 000002d0 0000000000000078
0000000000000018 I 1018[ 3] .data PROGBITS 0000000000000000 000000a9
00000000000000000000000000000000 WA 001[ 4] .bss NOBITS 0000000000000000
000000a9 00000000000000000000000000000000 WA 001[ 5] .rodata PROGBITS
0000000000000000 000000a9 00000000000000170000000000000000 A 001[ 6] .comment PROGBITS
0000000000000000 000000c0 000000000000002a 0000000000000001 MS 001[ 7] .note.GNU-stack PROGBITS
0000000000000000 000000ea 00000000000000000000000000000000001[ 8] .eh_frame PROGBITS
0000000000000000 000000f0 00000000000000580000000000000000 A 008[ 9] .rela.eh_frame RELA
00000000000000000000034800000000000000300000000000000018 I 1088[10] .symtab SYMTAB(符号表类型)
000000000000000000000148000000000000015000000000000000181198[11] .strtab STRTAB 0000000000000000
0000029800000000000000370000000000000000001[12] .shstrtab STRTAB 000000000000000000000378
00000000000000610000000000000000001 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)可重定位文件dman@DESKTOP:/mnt/a/os/pla/code$ readelf -S
test There are 29 section headers, starting at offset 0x1938: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align
[ 0] NULL 00000000000000000000000000000000000000000000000000000000000[ 1
] .interp(保存解释器文件名) PROGBITS 000000000040023800000238 000000000000001c 0000000000000000
A 001[ 2] .note.ABI-tag NOTE 00000000004002540000025400000000000000200000000000000000
A 004[ 3] .note.gnu.build-i NOTE 00000000004002740000027400000000000000240000000000000000
A 004[ 4] .gnu.hash GNU_HASH 000000000040029800000298 000000000000001c
0000000000000000 A 508[ 5] .dynsym DYNSYM 00000000004002b8 000002b8
00000000000000780000000000000018 A 618[ 6] .dynstr STRTAB 0000000000400330
0000033000000000000000440000000000000000 A 001[ 7] .gnu.version VERSYM 0000000000400374
00000374 000000000000000a 0000000000000002 A 502[ 8] .gnu.version_r VERNEED
00000000004003800000038000000000000000200000000000000000 A 618[ 9] .rela.dyn RELA 00000000004003a0 000003a0
00000000000000300000000000000018 A 508[10] .rela.plt RELA 00000000004003d0 000003d0
00000000000000300000000000000018 AI 5228[11] .init(程序初始化代码) PROGBITS 0000000000400400
0000040000000000000000170000000000000000 AX 004[12] .plt PROGBITS 0000000000400420
0000042000000000000000300000000000000010 AX 0016[13] .text(保存二进制代码) PROGBITS
000000000040045000000450 00000000000001c2 0000000000000000 AX 0016[14] .fini(程序结束代码) PROGBITS
00000000004006140000061400000000000000090000000000000000 AX 004[15] .rodata(保存只读数据) PROGBITS
000000000040062000000620 000000000000001b 0000000000000000 A 004[16] .eh_frame_hdr PROGBITS 000000000040063c 0000063c
00000000000000440000000000000000 A 004[17] .eh_frame PROGBITS 0000000000400680
0000068000000000000001200000000000000000 A 008[18] .init_array INIT_ARRAY 0000000000600e10 00000e10
00000000000000080000000000000008 WA 008[19] .fini_array FINI_ARRAY 0000000000600e18 00000e18
00000000000000080000000000000008 WA 008[20] .dynamic DYNAMIC 0000000000600e20 00000e20 00000000000001d0
0000000000000010 WA 608[21] .got PROGBITS 0000000000600ff0 00000ff0
00000000000000100000000000000008 WA 008[22] .got.plt PROGBITS 0000000000601000
0000100000000000000000280000000000000008 WA 008[23] .data(保存初始化过的数据) PROGBITS
00000000006010280000102800000000000000100000000000000000 WA 008[24] .bss NOBITS
00000000006010380000103800000000000000080000000000000000 WA 001[25] .comment PROGBITS
00000000000000000000103800000000000000290000000000000001 MS 001[26] .symtab SYMTAB
000000000000000000001068 00000000000005e8 000000000000001827438[27] .strtab STRTAB
000000000000000000001650 00000000000001e4 0000000000000000001[28] .shstrtab STRTAB
00000000000000000000183400000000000001030000000000000000001 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
)内核中的数据结构注意这里是linux2.6里的代码,mark一下以后再改基本数据类型/* 32-bit ELF base types. */typedef__u32Elf32_Addr;typedef
__u16Elf32_Half;typedef__u32Elf32_Off;typedef__s32Elf32_Sword;typedef__u32Elf32_Word;/* 64-bit ELF base types. */
typedef__u64Elf64_Addr;typedef__u16Elf64_Half;typedef__s16Elf64_SHalf;typedef__u64Elf64_Off;typedef__s32
Elf64_Sword;typedef__u32Elf64_Word;typedef__u64Elf64_Xword;typedef__s64Elf64_Sxword;头部ELF头// 64位和32位ELF头结构大体相同便不列出
typedefstructelf32_hdr{unsignedchare_ident[EI_NIDENT];// ELF "magic number" Elf32_Halfe_type;/* 保存ELF文件类型 */
Elf32_Halfe_machine;/* 文件所需体系结构 */Elf32_Worde_version;/* 版本信息 */Elf32_Addre_entry;/* 虚拟内存的入口点 */Elf32_Off
e_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_shstrndx;/* 节头表一项的索引 */}Elf32_Ehdr;2. 程序头表程序头表由几项构成,表项数据结构如下(32位和64位没有什么太大区别)
typedefstructelf64_phdr{Elf64_Wordp_type;Elf64_Wordp_flags;Elf64_Offp_offset;/* 段文件偏移量 */Elf64_Addrp_vaddr
;/* 段虚拟地址 */Elf64_Addrp_paddr;/* 段物理地址 */Elf64_Xwordp_filesz;/* 文件中段的长度 */Elf64_Xwordp_memsz;/* 内存中段的长度 */
Elf64_Xwordp_align;/* 段的对齐值,在文件和内存中 */}Elf64_Phdr;3. 节头表节头表由数组实现,每个元素包含一个节的信息,节的数据结构如下(32位和64位没有什么太大区别)
typedefstructelf64_shdr{Elf64_Wordsh_name;/* 节名,字符串表中的索引 */Elf64_Wordsh_type;/* 节类型 */Elf64_Xwordsh_flags
;/* 节的各种属性 */Elf64_Addrsh_addr;/* 节在执行时的虚拟地址 */Elf64_Offsh_offset;/* 节在文件中的偏移量 */Elf64_Xwordsh_size;/* 节长度,单位为字节 */
Elf64_Wordsh_link;/* 另一节的索引 */Elf64_Wordsh_info;/* 其他节信息 */Elf64_Xwordsh_addralign;/* 节对齐值 */Elf64_Xword
sh_entsize;/* 如果节存放的是表,各个表项的长度 */}Elf64_Shdr;ELF标准定义了若干固定名称的节这些用于执行大多数目标文件所需的标准任务所有名称都 从点开始,以便与用户定义节或非标准节相区分。
最重要的标准节如下所示.bss保存程序未初始化的数据节,在程序开始运行前填充0字节.data包含已经初始化的程序数据例如,预先初始化的结构,其中在编译时填充了静态数据 这些数据可以在程序运行期间更改.rodata
保存了程序使用的只读数据,不能修改,例如字符串 .dynamic和.dynstr保存了动态信息.interp保存了程序解释器的名称,形式为字符串.shstrtab包含了一个字符串表,定义了节名称.strtab
保存了一个字符串表,主要包含了符号表需要的各个字符串.symtab保存了二进制文件的符号表.init和.fini保存了程序初始化和结束时执行的机器指令这两个节的内容通常是由编译器 及其辅助工具自动创建的,主要是为程序建立一个适当的运行时环境。
.text保存了主要的机器指令字符串表因为其格式非常动态,内核不能提供一个固定的数据结构, 而必须手工分析现存数据符号表符号表表项数据结构(32位和64位没有什么太大区别)typedefstructelf64_sym
{Elf64_Wordst_name;/* 符号名称,字符串表中的索引 */unsignedcharst_info;/* 类型和绑定属性(全局符号,局部符号,弱符号) */unsignedcharst_other
;/* 语义未定义,0 */Elf64_Halfst_shndx;/* 相关节的索引 */Elf64_Addrst_value;/* 符号的值 */Elf64_Xwordst_size;/* 符号的长度 */
}Elf64_Sym;重定位项重定位是将ELF文件中未定义符号关联到有效值的处理过程,在test.o中,这意味着对printf的未定义引用必须替换为该进程的虚拟地址空间中适当的机器代码所在的地址在每个目标文件中,都有一个专门的表,包含了重定位项,标识了需要进行重定位之处。
每个表 项都包含下列信息:一个偏移量,指定了需要修改的项的位置对符号的引用(符号表的索引),提供了需要插入到重定位位置的数据待写:重定位工作原理动态链接待写
以上就是关于《ELF 二进制格式(二进制中的进位是什么意思)》的全部内容,本文网址:https://www.7ca.cn/baike/6863.shtml,如对您有帮助可以分享给好友,谢谢。