zoukankan      html  css  js  c++  java
  • ELF文件格式

    目标代码(Object Code)指编译器和汇编器处理源代码后所生成的机器语言目标代码
    目标文件(Object File)指包含目标代码的文件
    最早的目标文件格式是自有格式,非标准的。标准的几种目标文件格式:
    – DOS操作系统(最简单) :COM格式,文件中仅包含代码和数据,且被加载到固定位置
    – System V UNIX早期版本:COFF格式,文件中不仅包含代码和数据,还包含重定位信息、调试信息、符号表等其他信息,由一组严格定义的数据结构序列组成
    – Windows:PE格式(COFF的变种),称为可移植可执行(Portable Executable,简称PE)
    – Linux等类UNIX:ELF格式(COFF的变种),称为可执行可链接(Executable and Linkable Format,简称ELF)
     
    ELF文件格式提供了两种视图,分别是:
    链接视图(被链接):可重定位目标文件 (Relocatable object files) 
    执行视图(被执行):可执行目标文件(Executable object files)
    (1)链接视图——可重定位目标文件
    扩展名为.o(相当于Windows中的 .obj文件),包含代码、数据、定位信息(指出哪些符号引用处需要重定位)等
    可被链接合并,生成可执行文件或共享目标文件(.so文件);若干个可重定位目标文件组成静态链接库文件(.a文件)。
    节(section)是ELF 文件中具有相同特征的最小可处理单位:
    • ELF 头:
    分32位系统对应结构和64位系统对应结构(32位版本、64位版本)。32位系统中:
    ELF头位于ELF文件开始,共52字节,包括16字节标识信息、文件类型 (.o, exec, .so)、机器类型(如 IA-32)、节头表的偏移、节头表的表项大小以及表项个数等
    #define EI_NIDENT 16
    typedef struct{
        unsigned char e_ident[EI_NIDENT]; //16个字节的数组,定义了一些标识信息。最开头4B称为魔数,标识目标文件的类型或者格式。加载或读取文件时,可用魔数确认文件类型是否正确。再后面12字节,标识32位还是64位、大端还是小端、ELF头的版本号。
        Elf32_Half e_type;     //目标文件类型,是可重定位文件、可执行文件、共享库文件还是其它
        Elf32_Half e_machine;  //机器结构类型,是IA32、AMD64、SPARC V9还是其它
        Elf32_Word e_version; //目标文件版本
        Elf32_Addr e_entry;  //程序执行的入口地址,可重定位目标文件应该为0,且没有程序头表(段头表)等执行视图
        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;  //字符串表在节头表中的索引,即节头表中第多少项是字符串表(String Table)。
    }Elf32_Ehdr;
    ELF头是二进制存储的,需要使用命令查看内容
    $ readelf -h main.o
    Magic:   7f 45 4c 4601 01 01 00 00 00 00 00 00 00 00 00    //ELF文件的魔数是7f 45 4c 46。
    Class:    ELF32   
    Data:     2's complement, little endian    //负数用2进制补码形式表示、小端方式存放
    Version: 1 (current)
    OS/ABI:  UNIX - System V  
    ABI Version:   0
    Type:    REL (Relocatable file) 
    Machine:   Intel 80386 
    Version:    0x1
    Entry point address:  0x0     //可重定位文件程序执行入口地址为0
    Start of program headers:  0 (bytes into file)    //没有程序头表
    Start of section headers:   516 (bytes into file) //节头表起始位置
    Flags:    0x0
    Size of this header:   52 (bytes)          
    Size of section headers:    40 (bytes)  //节头表每项40B
    Number of section headers:  15       //节头表共15项
    Section header string table index: 12        //.strtab在节头表中的索引
    • .text 节
    编译后的代码部分
    • .rodata节
    只读数据,如 printf 格式串、switch跳转表等
    • .data 节
    已初始化全局变量和静态成员变量,存放具体的初始值,需要占磁盘空间。
    区分初始化和非初始化是为了空间效率
    • .bss 节
    未初始化全局变量和局部静态变量,默认初始值为0,.bss节中无需存放初始值,只要说明.bss中的每个变量将来在执行时占用几个字节即可,因此,.bss节实际上不占用磁盘空间,
    通过专门的节头表(Section header table)来说明应该为.bss节预留多大的空间
    • .symtab 节
    存放函数和全局变量的 (符号表)信息 ,它不包括局部变量
    • .rel.text 节
    .text节的重定位信息,用于重新修改代码段的指令中的地址信息
    • .rel.data 节
    .data节的重定位信息,用于对被模块使用或定义的全局变量进行重定位的信息
    • .debug 节
    调试用符号表
    • strtab 节
    字符串表,包括.symtab节和.debug节中的符号以及节头表中的节名。
    字符串表就是以null结尾的字符串序列。
    • Section header table(节头表)
    节头表的起始位置、表项数目、长度在ELF头中给出。
    以下是32位系统对应的节头表数据结构(每个表项占40B),说明了每个节的节名、在文件中的偏移、大小、访问属性、对齐方式等
    typedef struct {
        Elf32_Word sh_name;   //节名字符串在.strtab节(字符串表)中的偏移
        Elf32_Word sh_type;   //节类型:无效/代码或数据/符号/字符串/...
        Elf32_Word sh_flags;  //节标志:该节在虚拟空间中的访问属性
        Elf32_Addr sh_addr;   //虚拟地址:若可被加载,则对应虚拟地址
        Elf32_Off  sh_offset; //在文件中的偏移地址,对.bss节而言则无意义
        Elf32_Word sh_size;   //节在文件中所占的长度
        Elf32_Word sh_link;   //sh_link和sh_info用于与链接相关的节(如 .rel.text节、.rel.data节、.symtab节等)
        Elf32_Word sh_info;
        Elf32_Word sh_addralign; //节的对齐要求
        Elf32_Word sh_entsize;   //节中每个表项的长度,0表示无固定长度表项
    } Elf32_Shdr;
    使用readelf命令命令查看节头表内容
    $ readelf -S test.o
    可重定位目标文件中,每个可装入节的起始地址总是0
    .bss节应占00000c大小,但只有装入内存时才会分配。
    这11个节在装入内存时,只有.text节、.data节、.bss节和.rodata节会分配存储空间
     
    (2)执行视图——可执行目标文件
    与可重定位文件的不同
    1. ELF头中字段e_entry给出执行程序时第一条指令的地址,而在可重定位文件中,此字段为0
    2. 多一个程序头表,也称段头表(segment header table) ,是一个结构数组
    3. 多一个.init节,用于定义_init函数,该函数用来进行可执行目标文件开始执行时的初始化工作
    4. 少两个.rel节(无需重定位)
    使用readelf命令查看ELF头的内容:
    $ readelf -h main.o
    Magic:   7f 45 4c 4601 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:    EXEC (Executable file)  
    Machine:   Intel 80386 
    Version:    0x1
    Entry point address:  x8048580
    Start of program headers:  52 (bytes into file)   //程序头表在ELF头后
    Start of section headers:  3232 (bytes into file)
    Flags:    0x0
    Size of this header:   52 (bytes)  
    Size of program headers:    32 (bytes)   //程序头表每项32B
    Number of program headers:   8          //程序头表共8项
    Size of section headers:    40 (bytes)       
    Number of section headers:  29       
    Section header string table index: 26        //.strtab在节头表中的索引
    装入内存时,ELF头、程序头表、.init节、.rodata节会被装入只读代码段
    .data节和.bss节会被装入读写数据段
    描述可执行文件中的节与虚拟空间中的存储段之间的映射关系。
    一个表项32B,说明虚拟地址空间中一个连续的段或一个特殊的节。
    以下是32位系统对应的程序头表数据结构:
    typedef struct {
        Elf32_Word p_type;   //此数组元素描述的段的类型,或者如何解释此数组元素的信息。
        Elf32_Off p_offset;  //此成员给出从文件头到该段第一个字节的偏移
        Elf32_Addr p_vaddr;  //此成员给出段的第一个字节将被放到内存中的虚拟地址
        Elf32_Addr p_paddr;  //此成员仅用于与物理地址相关的系统中。System V忽略所有应用程序的物理地址信息。
        Elf32_Word p_filesz; //此成员给出段在文件映像中所占的字节数。可以为0。
        Elf32_Word p_memsz;  //此成员给出段在内存映像中占用的字节数。可以为0。
        Elf32_Word p_flags;  //此成员给出与段相关的标志。
        Elf32_Word p_align;  //此成员给出段在文件中和内存中如何对齐。
    } Elf32_phdr;
    使用readelf命令某可执行目标文件的程序头表
    $ readelf –l main
    程序头表信息有8个表项,其中两个为可装入段(即Type=LOAD):
    第一可装入段:第0x00000~0x004d3的长度为0x4d4字节的ELF头、程序头表、.init、.text和.rodata节,映射到虚拟地址0x8048000开始长度为0x4d4字节的区域 ,按0x1000=2^12=4KB对齐,具有只读/执行权限(Flg=RE),是只读代码段。
    第二可装入段:第0x00f0c~0x01014的长度为0x108字节的.data节和磁盘中不占存储空间的.bss节,映射到虚拟地址0x8049f0c开始长度为0x110字节的存储区域,在0x110=272B存储区中,前0x108=264B用.data节内容初始化,后面272-264=8B对应.bss节,初始化为0 ,按0x1000=4KB对齐,具有可读可写权限(Flg=RW),是可读写数据段。
    由此看出.bss节在文件中不占用磁盘空间,但在存储器中需要给它分配相应大小的空间。
  • 相关阅读:
    URAL——DFS找规律——Nudnik Photographer
    URAL1353——DP——Milliard Vasya's Function
    URAL1203——DPor贪心——Scientific Conference
    递推DP HDOJ 5389 Zero Escape
    区间DP UVA 1351 String Compression
    树形DP UVA 1292 Strategic game
    Manacher HDOJ 5371 Hotaru's problem
    同余模定理 HDOJ 5373 The shortest problem
    递推DP HDOJ 5375 Gray code
    最大子序列和 HDOJ 1003 Max Sum
  • 原文地址:https://www.cnblogs.com/yangyuliufeng/p/9207769.html
Copyright © 2011-2022 走看看