ELF格式解析
- 调试
- 2024-01-13
- 256热度
- 0评论
编译过程
工具链把程序源文件翻译成可执行文件一般经理4个步骤:预处理、编译、汇编、链接。本章节关于静态链接和动态链接的过程主要就是在第4个过程。链接器会将输入目标文件(.o)经过加工后合并成一个输出文件,这里的输出文件以linux的ELF为例。链接器会将相同性质的段合并在一起,如将所有输入文件的\".text\"合并到输出文件的\".text\"段,接着是\".data\"段,\".bss\"段等。
ELF格式
ELF一般有3种类型:
- 可重定位文件(Reloacatable File):xxx.o。
- 可执行文件(Executable File):xx.out。
- 共享目标文件(Sharead object File):xxx.so。
ELF提供了两种视图:链接视图和执行视图。
- 链接视图: 以section为单位,在链接阶段,如将多个模块的.text合并为一个.text。
- 执行视图:以segment为单位,在执行阶段,对应于linux的VMA,.text和.radata为一个整体。
ELF可以组成可以分为四类:ELF header、program header table、固定的sections、sections header table。
ELF header
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; //描述了有多少个sections。
Elf32_Half e_shstrndx;
} Elf32_Ehdr;
这些字段是 ELF 文件头中的一部分,它们包含了 ELF 文件的基本信息,如文件类型、目标机器架构、入口地址、程序头表和节区头表等,为 ELF 文件的加载和执行提供了必要的信息。
使用命令readelf -h xxx可以读取上面的信息。
program header table
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;
ELF 程序头表(Program Header Table)包含了多个程序头(Program Header)的条目,每个程序头描述了一个段(Segment)的信息。段是 ELF 文件中的逻辑组织单元,包含了可执行代码、数据、只读数据等。程序头表通过指定每个段在文件中和内存中的位置、大小、属性等信息,为系统加载器(Loader)提供了必要的信息,以正确加载和执行 ELF 文件。
使用readelf -l 可以查看有哪些segment
上图中Type 为 LOAD的,表示该节区包含可执行文件或共享库的可加载部分。在加载过程中,这些节区的内容将被加载到内存中。
固定的sections
使用readelf -S 可以查看有哪些sections
- .text:包含可执行代码的节区,通常用于存储程序的指令。
- .data:包含已初始化的全局和静态变量的节区,用于存储程序的全局数据。
- .bss:包含未初始化的全局和静态变量的节区,用于存储程序的全局数据,但在文件中不占用实际空间。
- .rodata:包含只读数据的节区,通常用于存储常量、字符串等不可修改的数据。
- .comment:包含编译器生成的注释信息的节区,通常用于存储编译器版本、编译时间等信息。
- .eh_frame:包含异常处理框架信息的节区,用于支持 C++ 异常处理和调用堆栈展开。
- .dynsym:包含动态链接符号表的节区,用于存储动态链接所需的符号信息。
- .shstrtab:包含节区名称字符串的节区,用于存储所有节区的名称。
- .strtab:包含字符串表的节区,用于存储字符串常量。
- .symtab:包含符号表的节区,用于存储链接器符号信息。
- .got:全局偏移表(Global Offset Table),用于存储动态链接所需的全局变量和函数地址。
- .plt:过程链接表(Procedure Linkage Table),用于实现函数调用的延迟绑定。
- .rel.xxx:用于存储重定位信息的节区,其中 xxx 表示需要进行重定位的节区名称,如.rel.text,.rel.data。
- .dynamic:包含动态链接器信息的节区,用于存储动态链接器所需的信息。
.bss 节区存储未初始化的全局和静态变量,这些变量在编译时没有明确赋初始值,因此在 ELF 文件中不需要占用实际空间。相反,当程序被加载到内存中时,操作系统会分配一段 BSS 段的内存,大小由 .bss 节区中所有变量的总大小决定,然后将该内存区域初始化为 0。这种方式可以有效地节省文件大小,节约磁盘空间和加载时间。如果 .bss 节区中的变量在文件中被显式地初始化了,那么它们就会被分配到 .data 节区中,该节区会在文件中占用实际空间。需要注意的是,.bss 节区的存在并不是必须的,编译器可以将未初始化的全局和静态变量直接放入 .data 节区中,但这样会导致 .data 节区的大小增加,从而增加了文件大小和加载时间。因此,使用 .bss 节区可以更好地优化程序的性能和空间占用。
sections header table
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;
ELF文件中section head table (SHT)来描述ELF文件中有哪些具体的sections,每个section描述了这个段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其它属性。
使用命令使用readelf -S 可以查看有section head table信息,与上一节固定的sections对应,实际上section head table就是描述上一节的信息。