linux可执行文件格式
来源:互联网 发布:c语言随机数头文件 编辑:程序博客网 时间:2024/06/08 05:03
1. 可执行文件的任务:
- 可执 行文件的创建:
- 编译(compile): 源程序文件被编译成目标文件,
- 连接(link): 多个目标文件 被连接成一个最终的可执行文件,
- 可执行文件的运行: 可执行文件被加载(load)到内存中执行。
2. a.out
assembler and link editor output-汇编器和链接编辑器的输出格式(简述)
a.out 是一种古老的文件格式,简单,紧凑, 但其精华犹在,略作研究
标准 a.out 文件包含 7 个 块,1个头部+6个段
格式如下:
头部的数据结构:
struct exec {unsigned long a_midmag; /* 魔数和其它信息 */unsigned long a_text; /* 文本段的长度 */unsigned long a_data; /* 数据段的长度 */unsigned long a_bss; /* BSS段的长度 */unsigned long a_syms; /* 符号表的长度 */unsigned long a_entry; /* 程序进入点 */unsigned long a_trsize; /* 文本重定位表的长度 */unsigned long a_drsize; /* 数据重定位表的长度 */};
头部中有标识本文件类型为a.out 的魔数, 程序入口点. 占8byte
还有6个段的长度,24byte, 共32个bytes.
这6个段的长度,是文件体中各个段的长度, 没有string table表长度,因为它在最后,但添加了BSS 段长度.
我们看到, a.out 格式非常紧凑, 头部只记录了段的长度, 没有记录段的地址. 所以
它要求各个段的排序位置是固定的, 各段的开始地址通过前面段长度累加得到.
由a.out 可以看出可执行文件的基本要素:
1. 代码和数据是必要的。
2. 因为文件会引用外部文件定义的符号(变量和函数),因此重定位信息和符号信息也是需要的
3. ascii 字符串, 把它作为一个单独的表放到最后.
举例: hello.c 程序
printf("hello world\n");printf 就是一个外部符号,"hello world" 是只读数据,这个格式的文件太古老了, 已经难以见到它的身影, 就让它过去吧.
3. ELF
(Executable and Linking Format 可执行和链接格式)
网上内容不少,但我要用最简单的语言和例子来描述清楚问题.
elf文件格式由elf头, program 表头+program, setion 表头 + section 來构成,
但一个实际的elf文件可能并不包含以上所有项,其中elf头位置及大小是固定的.
section 体与 program体可以有交叉.
1. elf文件头
这个文件是对elf文件整体信息的描述,在32位系统下是56的字节,在64位系统下是64个字节。
对于可执行文件来说,文件头的完整描述参考后面描述.
文件头包含的以下信息与进程启动相关
e_entry 程序入口地址e_phoff program表偏移e_phnum program 数量
2. program 表
表中每一项叫program 头
typedef struct
{
Elf64_Word p_type;
Elf64_Word p_flags; /* 权限: 6表示可读写,5表示可读可执行*/
Elf64_Off p_offset; /* 段在文件中的偏移*/
Elf64_Addr p_vaddr; /* 虚拟内存地址,??*/
Elf64_Addr p_paddr; /* 物理内存地址,??*/
Elf64_Xword p_filesz; /* file size ,段在文件中的长度*/
Elf64_Xword p_memsz; /* memory size 在内存中的长度,一般和p_filesz的值一样*/
Elf64_Xword p_align; /* 段对齐*/} Elf64_Phdr;
3. 节表及节头
我这里就忽略了, 它们与运行无关,只与链接有关.
4. 汇编级别的elf64 实例,
用汇编,因为生成的文件小
cat t1.s
.section .data.global data_itemdata_item:.long 0x12345678,0x90abcdef.section .text.global _start_start: mov $1,%eax mov $4,%ebx int $0x80as t1.s -o t1.o //编译及链接ld t1.o -o t1strip t1 //顺便剔除符号信息:观察其16进制导出文件: xxd t1 就看到其全貌了.$ xxd t10000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............0000010: 0200 3e00 0100 0000 b000 4000 0000 0000 ..>.......@.....0000020: 4000 0000 0000 0000 e000 0000 0000 0000 @...............0000030: 0000 0000 4000 3800 0200 4000 0400 0300 ....@.8...@.....0000040: 0100 0000 0500 0000 0000 0000 0000 0000 ................0000050: 0000 4000 0000 0000 0000 4000 0000 0000 ..@.......@.....0000060: bc00 0000 0000 0000 bc00 0000 0000 0000 ................0000070: 0000 2000 0000 0000 0100 0000 0600 0000 .. .............0000080: bc00 0000 0000 0000 bc00 6000 0000 0000 ..........`.....0000090: bc00 6000 0000 0000 0800 0000 0000 0000 ..`.............00000a0: 0800 0000 0000 0000 0000 2000 0000 0000 .......... .....00000b0: b801 0000 00bb 0400 0000 cd80 7856 3412 ............xV4.00000c0: efcd ab90 002e 7368 7374 7274 6162 002e ......shstrtab..00000d0: 7465 7874 002e 6461 7461 0000 0000 0000 text..data......00000e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................00000f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................0000100: 0000 0000 0000 0000 0000 0000 0000 0000 ................0000110: 0000 0000 0000 0000 0000 0000 0000 0000 ................0000120: 0b00 0000 0100 0000 0600 0000 0000 0000 ................0000130: b000 4000 0000 0000 b000 0000 0000 0000 ..@.............0000140: 0c00 0000 0000 0000 0000 0000 0000 0000 ................0000150: 0100 0000 0000 0000 0000 0000 0000 0000 ................0000160: 1100 0000 0100 0000 0300 0000 0000 0000 ................0000170: bc00 6000 0000 0000 bc00 0000 0000 0000 ..`.............0000180: 0800 0000 0000 0000 0000 0000 0000 0000 ................0000190: 0100 0000 0000 0000 0000 0000 0000 0000 ................00001a0: 0100 0000 0300 0000 0000 0000 0000 0000 ................00001b0: 0000 0000 0000 0000 c400 0000 0000 0000 ................00001c0: 1700 0000 0000 0000 0000 0000 0000 0000 ................00001d0: 0100 0000 0000 0000 0000 0000 0000 0000 ................
/usr/include/elf.h 中有定义
typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ 16 bytes Elf64_Half e_type; /* Object file type */ uint16 ->0x2 , 可执行 Elf64_Half e_machine; /* Architecture */ uint16 ->0x3e, x86_64 Elf64_Word e_version; /* Object file version */ uint32 ->0x1 Elf64_Addr e_entry; /* Entry point virtual address */ uint64-> 0x4000b0 Elf64_Off e_phoff; /* Program header table file offset */ uint64-> 0x40 Elf64_Off e_shoff; /* Section header table file offset */ uint64-> 0xe0 Elf64_Word e_flags; /* Processor-specific flags */ uint32-> 0 Elf64_Half e_ehsize; /* ELF header size in bytes */ uint16-> 0x40 Elf64_Half e_phentsize; /* Program header table entry size */ uint16-> 0x38 Elf64_Half e_phnum; /* Program header table entry count */ uint16-> 0x2 Elf64_Half e_shentsize; /* Section header table entry size */ uint16-> 0x40 Elf64_Half e_shnum; /* Section header table entry count */ uint16->0x4 Elf64_Half e_shstrndx; /* Section header string table index */ uint16->0x3 } Elf64_Ehdr;
后面的注释是根据二进制文件手工添加的.
5.用工具解读elf:
- $ objdump -x t2
t1: file format elf64-x86-64t1architecture: i386:x86-64, flags 0x00000102:EXEC_P, D_PAGEDstart address 0x00000000004000b0Program Header: LOAD off 0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**21 filesz 0x00000000000000bc memsz 0x00000000000000bc flags r-x LOAD off 0x00000000000000bc vaddr 0x00000000006000bc paddr 0x00000000006000bc align 2**21 filesz 0x0000000000000008 memsz 0x0000000000000008 flags rw-Sections:Idx Name Size VMA LMA File off Algn 0 .text 0000000c 00000000004000b0 00000000004000b0 000000b0 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .data 00000008 00000000006000bc 00000000006000bc 000000bc 2**0 CONTENTS, ALLOC, LOAD, DATASYMBOL TABLE:no symbols
objdump -> elf header显示比较差, section 才显示2个 , 也许它指的是section映射到segment吧
- $ readelf -e t1
ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's 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: 0x4000b0 Start of program headers: 64 (bytes into file) Start of section headers: 224 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 2 Size of section headers: 64 (bytes) Number of section headers: 4 Section header string table index: 3 Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 00000000004000b0 000000b0 000000000000000c 0000000000000000 AX 0 0 1 [ 2] .data PROGBITS 00000000006000bc 000000bc 0000000000000008 0000000000000000 WA 0 0 1 [ 3] .shstrtab STRTAB 0000000000000000 000000c4 0000000000000017 0000000000000000 0 0 1 Key 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) Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000 0x00000000000000bc 0x00000000000000bc R E 200000 LOAD 0x00000000000000bc 0x00000000006000bc 0x00000000006000bc 0x0000000000000008 0x0000000000000008 RW 200000 Section to Segment mapping: Segment Sections... 00 .text 01 .data
readelf: 内容显示的比较清晰!
6. 下面继续完成手工阅读elf 文件.
- 程序头
typedef struct { Elf64_Word p_type; /* Segment type */ 0x01 Elf64_Word p_flags; /* Segment flags */ 0x05 Elf64_Off p_offset; /* Segment file offset */ 0x0 Elf64_Addr p_vaddr; /* Segment virtual address */ 0x400000 Elf64_Addr p_paddr; /* Segment physical address */0x400000 Elf64_Xword p_filesz; /* Segment size in file */ 0xbc Elf64_Xword p_memsz; /* Segment size in memory */ 0xbc Elf64_Xword p_align; /* Segment alignment */ 0x200000 } Elf64_Phdr;
每个program header 占用56 byte, 注释上标记了一个, 跟程序输出可以进行对照!
我们看到:
- 1个可读可运行的段,大小0xbc, 位于文件偏移0, 内存地址0x400000处
- 1个可读可写的段,大小8bytes, 位于文件偏移0xbc, 内存地址0x6000bc处
- 节头
typedef struct { Elf64_Word sh_name; /* Section name (string tbl index) */ 0x0b, 0x11,0x01 Elf64_Word sh_type; /* Section type */ 0x1,0x1,0x3 Elf64_Xword sh_flags; /* Section flags */ 0x06,0x3,0x0 Elf64_Addr sh_addr; /* Section virtual addr at execution */ 0x4000b0,0x6000bc,0x0 Elf64_Off sh_offset; /* Section file offset */ 0xb0,0xbc,0xc4 Elf64_Xword sh_size; /* Section size in bytes */ 0x0c,0x03,0x17 Elf64_Word sh_link; /* Link to another section */ 0x0,0x0,0x0 Elf64_Word sh_info; /* Additional section information */ 0x0,0x0,0x0 Elf64_Xword sh_addralign; /* Section alignment */ 0x1,0x1,0x1 Elf64_Xword sh_entsize; /* Entry size if section holds table */ 0x0,0x0,0x0 } Elf64_Shdr;
手工分析, 从0xe0开始, 存在包含4个节头的节表.每个节头0x40个byte, 谁说的? elf header 说的.
第一个节表头项闲置不用, 其它三个已经标注在注释里了. 依次可解释4个section header
这个表跟program 没有关系,不影响程序执行
这4个节(实际是3个,第0个为空项)是说:
- 第1个节,名字叫.text, 可分配可运行(0x6),文件位置0xb0,内存地址0x4000b0,大小0xc,
- 第2个节,名字叫.data,可分配可写(0x3),文件位置0xbc,内存地址0x6000bc,大小0x8,
- 第3个节,名字叫.shstrtab, 未分配内存,文件位置0xc4,内存地址给0(默认),大小0x17, 这个是字符串索引节
至此,这个简单的elf 数据已经被分割完毕!
看起来内容还是不少,几个简单的运行指令竟前呼后涌跟随着不少数据, 这是因为可执行的elf 不是给人看得,是给加载器看的,加载器总是按照固定的套路把数据加载到内存中.
- linux可执行文件格式
- linux可执行文件格式
- linux可执行文件格式
- LINUX 平台可执行文件格式分析
- Linux下可执行文件格式详解
- Linux下可执行文件格式详解
- Linux下可执行文件格式详解
- Linux下可执行文件的格式
- linux下可执行文件格式详解
- Linux下可执行文件格式详解
- Linux下可执行文件的格式
- UNIX/LINUX 平台可执行文件格式分析
- UNIX/LINUX 平台可执行文件格式分析
- UNIX/LINUX 平台可执行文件格式分析
- UNIX/LINUX 平台可执行文件格式分析
- UNIX/LINUX 平台可执行文件格式分析
- UNIX/LINUX 平台可执行文件格式分析
- [转载]UNIX/LINUX 平台可执行文件格式分析
- Listener&&Filter
- jQuery选择器
- C++库常用函数一览(<string><cctype><algorithm><cmath><cstdlib><iomanip><numeric>)
- java数据结构和算法-3,快速排序
- 常用数据库随笔记-初级
- linux可执行文件格式
- 两个链表的第一个公共结点
- 反射机制功能用法
- Linux链接boost库错误error while loading shared libraries错误解决方法
- Android 开发飞机大战
- hdoj1024 Max Sum Plus Plus(DP!DP!)
- iOS Category中添加属性和成员变量的区别
- 深入理解Spring Redis的使用 (三)、使用RedisTemplate的操作类访问Redis
- mac系统安装虚拟机ubuntu14后,如何在mac终端连接ubuntu?