ELF二进制目标文件详解
以下内容为<<linux内核编程>>笔记
链接程序
找出所有引用的外部模块并链接起来,这些外部模块或函数库一般来自于开发者,操作系统和C运行库。
链接程序取出这些函数库,修订指针位置(重定位),并交叉引用模块中的符号解析,最终产生一个可执行模块。符号可以是全局的也可以是局部的。全局符号可以在模块内部定义,或由另一模块外部引用。
静态库是在链接时被找到并复制的,而动态库和共享库是在运行时才装载的,并让所有的进程共享。linux提供的系统调用dlopen(),dlsym(),dlclose(),用于加载/打开共享库,查找库中的符号,然后关闭共享库
ELF二进制目标文件
可执行ELF目标文件包括:ELF头,程序头表(用于加载的节),第1节,第2节。。。。节头表(可选)
1.ELF文件头
typedef struct elf32_hdr{ unsigned char e_ident[EI_NIDENT]; //标识该文件是否为ELF文件 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; //字段保存ELF头的大小 Elf32_Half e_phentsize; //保存程序头表中的每一项的大小 Elf32_Half e_phnum; //程序头中表项的个数 Elf32_Half e_shentsize; //节头表中每一项的大小 Elf32_Half e_shnum; //保存节头中项的数量,表明该文件中有多少节 Elf32_Half e_shstrndx; //保存节头中节字符串的索引 } Elf32_Ehdr;
2 节头表
typedef struct elf32_shdr { Elf32_Word sh_name; //包含节名 Elf32_Word sh_type; //包含节的内容 Elf32_Word sh_flags; //各种属性的内容 Elf32_Addr sh_addr; //节在内存映像中的地址 Elf32_Off sh_offset; //保存ELF文件中这一节中初始字节的偏移量 Elf32_Word sh_size; //包含节的大小 Elf32_Word sh_link; //表链接的索引 Elf32_Word sh_info; //包含附加信息 Elf32_Word sh_addralign; //包含地址对其的约束 Elf32_Word sh_entsize; //节中每项的大小 } Elf32_Shdr;
3 非可执行ELF文件的节
bss 为初始化的数据
.data 已初始化的数据
.hash 符号散列表
.init 初始化代码
.symtab 符号表
.text 可执行的指令
.plt 过程链接表
.rodata 只读数据
.dynamic 动态链接信息
等等
4 程序头表
typedef struct elf64_phdr {
Elf64_Word p_type; //描述该段的类型
Elf64_Word p_flags; //以p_type而定
Elf64_Off p_offset; //该段的开始相对于文件开始的偏移量
Elf64_Addr p_vaddr; //段虚拟地址
Elf64_Addr p_paddr; //段的虚拟地址
Elf64_Xword p_filesz; //文件映像中该段的字节数
Elf64_Xword p_memsz; //内存映像中该段的字节数
Elf64_Xword p_align; //描述要对齐的段在内存中如何对齐,该值是2的整数次幂
} Elf64_Phdr;
1. 将可执行文件的段加入内存
2. 加载所有需要的共享库
3. 需要时重定向可执行文件及其共享对象
4. 将控制权交给程序