PC平台流行的可执行文件格式: linux ELF(executable linkable format) windows PE(portable executable)
它们都是coff格式的变种。
ELF文件可分为如下4类
目标文件将信息按不同的属性,以段的形式存储,比如.code, .data。最开始是文件头,描述整个文件的属性,并且还包含一个段表,用来描述各个段的信息。
分段存储而不是整个一起存储的好处:
挖掘SimpleSection.o
源码:
SimpleSection.c
linux:
gcc -c SimpleSection.c
*/
int printf(const char* format, ...);
int global_init_var = 84;
int global_uninit_var ;
void func1(int i)
{
print("%d\n", i);
}
int main(void)
{
static int static_var = 85;
static int static_var2;
int a=1;
int b;
func1(static_var + static_var2+ a + b);
return a;
}
各个段基本信息:
root@debian:~/compileLinkLoad# objdump -h SimpleSection.o
SimpleSection.o: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .group 00000008 00000000 00000000 00000034 2**2
CONTENTS, READONLY, EXCLUDE, GROUP, LINK_ONCE_DISCARD
1 .text 0000007f 00000000 00000000 0000003c 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
2 .data 00000008 00000000 00000000 000000bc 2**2
CONTENTS, ALLOC, LOAD, DATA
3 .bss 00000004 00000000 00000000 000000c4 2**2
ALLOC
4 .rodata 00000004 00000000 00000000 000000c4 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .text.__x86.get_pc_thunk.ax 00000004 00000000 00000000 000000c8 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
6 .comment 0000002e 00000000 00000000 000000cc 2**0
CONTENTS, READONLY
7 .note.GNU-stack 00000000 00000000 00000000 000000fa 2**0
CONTENTS, READONLY
8 .eh_frame 0000007c 00000000 00000000 000000fc 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
file off 表示段相对于文件起始地址的偏移,size表示段的大小。
BSS段
.bss段存储的是未初始化的全局变量和局部静态变量。但是bss段并未实际分配空间存储空间,而只是预留。等到真正链接成可执行文件时,再在.bss段分配空间。(那是不是相应的要修改段表?)
root@debian:~/compileLinkLoad# readelf -h SimpleSection.o
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 1072 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 15
Section header string table index: 14
ELF 文件头及相关常数被定义在/usr/include/elf.h中。
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
readelf -h的输出与Elf32_Ehdr一一对应。
e_type 三种类型,可重定位文件,可执行文件,共享目标文件,不知道为何不包括核心转储文件
e_entry表示elf程序的入口虚拟地址,操作系统在加载完该程序之后从这个地址开始执行进程的指令。可重定位文件一般没有入口地址,则这个值为0.
e_shoff section header off 段表在文件中的偏移
e_ehsize elf header size
elf_shentsize elf section header entry size 段表描述符每一个条目的大小
elf_shnum number of section headers
e_shstrndx 段表字符串表所在的段在段表中的下标
段表是保存段的属性的基本结构,比如段名,长度,段在文件中的偏移,读写权限等等。。
段表是一个以Elf32_Shdr结构体为元素的数组,每个Elf32_Shdr结构体对应一个段。数据第一个元素为无效段描述符。
Elf32_Shdr:
typedef struct
{
Elf32_Word sh_name; /* Section name (string tbl index) */
Elf32_Word sh_type; /* Section type */
Elf32_Word sh_flags; /* Section flags */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Section size in bytes */
Elf32_Word sh_link; /* Link to another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */
} Elf32_Shdr;
sh_name 段名。段名是一个字符串,位于一个叫做.shstrtab的字符串表,sh_name是段名字符串在.shstrtab中的偏移
sh_type 段的名字不能真正表示段的属性,主要决定段的属性的是段的类型和标志位。
SHT_NULL 0 /* Section header table entry unused */
SHT_PROGBITS 1 /* Program data */
SHT_SYMTAB 2 /* Symbol table */
SHT_STRTAB 3 /* String table */
SHT_RELA 4 /* Relocation entries with addends */
SHT_HASH 5 /* Symbol hash table */
SHT_DYNAMIC 6 /* Dynamic linking information */
SHT_NOTE 7 /* Notes */
SHT_NOBITS 8 /* Program space with no data (bss) */
SHT_REL 9 /* Relocation entries, no addends */
SHT_SHLIB 10 /* Reserved */
SHT_DYNSYM 11 /* Dynamic linker symbol table */
sh_flag 段的标志位 表示该段在进程虚拟地址空间中的属性
最常见的几种
SHF_WRITE (1 << 0) /* Writable */
SHF_ALLOC (1 << 1) /* Occupies memory during execution */
SHF_EXECINSTR (1 << 2) /* Executable */
sh_link sh_info 段的链接信息。如果段的类型是与链接相关的,比如重定位表,符号表等,那么这两个成员包含了一些链接相关的信息(具体含义有待补充)。对于其他段,没有意义。
重定位表的类型为SHT_REL,链接器在对目标文件进行处理时,需要对代码和数据中那些对绝对地址引用的位置进行重定位,每个需要重定位的代码或数据段,都有一个对应的重定位表。重定位表其实就是ELF文件的一个段。它的sh_link表示符号表的下标,sh_info表示作用于哪个表对应的下标。
ELF文件中用到了很多字符串,比如段名,变量名,以及一些常量字符串。因为字符串长度不固定,用固定的结构来表示比较困难,一般做法是将字符串集中起来存储到一个数组中,然后使用字符串在表中的偏移来引用字符串。
字符串在ELF文件中以段的形式保存,常见的段名为 .strtab或.shstrtab. 分别用来存储普通字符串和段表中用到字符串。
在链接中,将函数和变量统称为符号,函数名和变量名就是符号名。每个目标文件都有一个符号表用来记录目标文件中所用到的所用符号。每个定义的符号都有一个对应的值,叫符号值。
符号可以分为以下几类
链接过程中只关注全局符号。
符号表也是作为一个段来存储,段名叫.symtab。符号表是一个Elf32_Sym结构体数组。
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;
st_name 符号名,值为该符号名在字符串表中的偏移。
st_value 符号值。分以下几种情况:
#define STT_NOTYPE 0 /* Symbol type is unspecified */
#define STT_OBJECT 1 /* Symbol is a data object */
#define STT_FUNC 2 /* Symbol is a code object */
#define STT_SECTION 3 /* Symbol associated with a section */
#define STT_FILE 4 /* Symbol's name is file name */
#define STT_COMMON 5 /* Symbol is a common data object */
#define STT_TLS 6 /* Symbol is thread-local data object*/
#define STT_NUM 7 /* Number of defined types. */
#define STT_LOOS 10 /* Start of OS-specific */
#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */
#define STT_HIOS 12 /* End of OS-specific */
#define STT_LOPROC 13 /* Start of processor-specific */
#define STT_HIPROC 15 /* End of processor-specific */
符号绑定信息:
#define STB_LOCAL 0 /* Local symbol */
#define STB_GLOBAL 1 /* Global symbol */
#define STB_WEAK 2 /* Weak symbol */
#define STB_NUM 3 /* Number of defined types. */
#define STB_LOOS 10 /* Start of OS-specific */
#define STB_GNU_UNIQUE 10 /* Unique symbol. */
#define STB_HIOS 12 /* End of OS-specific */
#define STB_LOPROC 13 /* Start of processor-specific */
#define STB_HIPROC 15 /* End of processor-specific */
st_other 无用
st_shndx 符号所在段
略…