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节在文件中不占用磁盘空间,但在存储器中需要给它分配相应大小的空间。
  • 相关阅读:
    CentOS中的环境变量配置文件
    SCVMM中Clone虚拟机失败显示Unsupported Cluster Configuration状态
    Windows Server 2012 虚拟化实战:SCVMM的安装和部署
    Windows Server 2012 虚拟化实战:网络(二)
    x86.zip
    音视频处理之PS封装的介绍与使用20180928
    界面编程之QT的数据库操作20180801
    界面编程之QT的线程20180731
    界面编程之QT的Socket通信20180730
    界面编程之QT的文件操作20180729
  • 原文地址:https://www.cnblogs.com/yangyuliufeng/p/9207769.html
Copyright © 2011-2022 走看看