zoukankan      html  css  js  c++  java
  • Linux ELF文件学习(1)

    (转载)http://blog.chinaunix.net/uid-21273878-id-1828736.html
    ELF头文件学习
    ELF文件原名Executable and Linking Format,译为“可执行可连接格式”。
    ELF规范中把ELF文件宽泛的称为“目标文件”,这与我们平时的理解不同。一般的,我们把编译但没有链接的文件(比如Linux下的.o文件)称为目标文件。而ELF文件仅指链接好的可执行文件。在ELF规范中,所用符合ELF规范的文件都成为ELF文件,也成为目标文件,这两个名字意义相同。
    经过编译但没有连接的文件则称为“可重定位文件 (relocatable file)”或“待重定位文件 (relocatable file)”。本文采用与ELF规范相同的命名方式,所以当提到可重定位文件时,一般可以理解为惯常所说的目标文件;而提到目标文件时,即指各种类型的 ELF文件。
     
    ELF文件定义如下表格所示:

    ELF文件

    可重定位文件(relocatable file)

    可重定位文件(relocatable file),用于与其它目标文件进行连接以构建可执行文件或动态链接库。可重定位文件就是常说的目标文件,由源文件编译而成,但还没有连接成可执行文件。在 UNIX系统下,一般有扩展名”.o”。之所以称其为“可重定位”,是因为在这些文件中,如果引用到其它目标文件或库文件中定义的符号(变量或者函数)的话,只是给出一个名字,这里还并不知道这个符号在哪里,其具体的地址是什么。需要在连接的过程中,把对这些外部符号的引用重新定位到其真正定义的位置上,所以称目标文件为“可重定位”或者“待重定位”的。

    共享目标文件(shared object file)

    即动态连接库文件。它在以下两种情况下被使用:第一,在连接过程中与其它动态链接库或可重定位文件一起构建新的目标文件;第二,在可执行文件被加载的过程中,被动态链接到新的进程中,成为运行代码的一部分。

    可执行文件(executable file )

    经过连接的,可以执行的程序文件。

     
    ELF文件的作用有两个:一是用于构建程序,构建动态链接库或可执行程序等,主要体现在链接过程。二是用于运行程序。在这两种情况下,我们可以从不同的视角看待同一个目标文件。
    从连接的角度和运行的角度,可以分别把目标文件的组成部分做以下划分:
     
     
    1. ELF头文件
        位于文件最开始处,包含整个文件的结构信息。
    2. 节(section)
        是专门用于连接过程而言的,在每个节中包含指令数据、符号数据、重定位数据等等。
    3. 程序头表
        在运行过程中是必须的,在链接过程中是可选的,因为它的作用是告诉系统如何创建进程的映像。
    4. 节头表
        包含文件中所用节的信息。
    下面看一下Linux内核对ELF头文件的定义。

    /* 32-bit ELF base types. */
    typedef __u32 Elf32_Addr;
    typedef __u16 Elf32_Half;
    typedef __u32 Elf32_Off;
    typedef __s32 Elf32_Sword;
    typedef __u32 Elf32_Word;

     #define EI_NIDENT 16

     typedef struct elf32_hdr{
      unsigned char e_ident[EI_NIDENT]; //16字节的信息,下文详细解释
      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;  //此结构体一共52个字节

    我使用Ultraedit打开一个ELF文件,来分析它的前52个字节:

    e_ident[0]

    7F

    目标文件最开头的前四个字节。

    文件标志,表示是ELF文件

    EI_MAG0 = 0, EI_MAG1 = 1, EI_MAG2 = 2, EI_MAG3 = 3

    e_ident[1]

    45 == ‘E’

    e_ident[2]

    4C == ’L’

    e_ident[3]

    46 == ‘F’

    e_ident[4]

    01

    文件类别,取值如下:(EI_CLASS = 4)

    ELFCLASSNONE

    0

    非法目标文件

    ELFCLASS32

    1

    32为目标文件

    ELFCLASS64

    2

    64为目标文件

    e_ident[5]

    01

    编码格式,取值如下:(EI_DATA = 5)

    ELFDATANONE

    0

    非法编码格式

    ELFDATA2LSB

    1

    小端编码格式

    ELFDATA2MSB

    2

    大端编码格式

    e_ident[6]

    01

    文件版本,(EI_VERSION),为1表明是目前版本

    e_ident[7] 

    to

    e_ident[15]

    00 00 00 00 00 00 00 00 00

    这就个字节暂且不使用,留在以后扩展。

    e_type

    00 01

    此字段表明文件属于那种类型:

    ET_NONE

    0

    未知文件类型

    ET_REL

    1

    可重定位文件

    ET_EXEC

    2

    可执行文件

    ET_DYN

    3

    动态链接库文件

    ET_CORE

    4

    CORE文件

    ET_LOPROC

    0xff00

    特定处理器文件扩展下边界

    ET_HIPROC

    0xffff

    特定处理器文件扩展上边界

    e_machine

    00 03

    体系结构类型 : Intel 80386

    e_version

    00 00 00 01

     目前版本

    e_entry

    00 00 00 00

     

    e_phoff

    00 00 00 00

    没有程序头部表。

    e_shoff

    00 00 00 FC

    e_ident[0]是文件的第一个字节,从它开始FC(252)字节后就是节的数据信息。

    e_flags

    00 00 00 00

     

    e_ehsize

    00 34

    52字节的ELF头部结构体

    e_phentsize

    00 00

    因为没有程序头部表,所以大小为0

    e_phnum

    00 00

    同上

    e_shentsize

    00 28

    40字节的节区头部表

    e_shnum

    00 0B

    节区头部表的表项有B(11)

    e_shstrndx

    00 08

     节头表中与节名字表相对应的表项的索引。

     下面的一段程序用来读ELF文件的头部:readelf_h.c:

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <sys/types.h>
      4 #include <sys/stat.h>
      5 #include <fcntl.h>
      6 #include <unistd.h>
      7 #include <errno.h>
      8 
      9 #define FALSE 0
     10 #define TURE 1
     11 #define MAX_SIZE 52
     12 
     13 typedef short int     Elf32_Half;
     14 typedef int         Elf32_Word;
     15 typedef Elf32_Word     Elf32_Addr;
     16 typedef Elf32_Word     Elf32_Off;
     17 /*Elf头部文件部分重要数据*/
     18 typedef struct{
     19         Elf32_Half e_type;
     20         Elf32_Half e_machine;
     21         Elf32_Word e_version;
     22         Elf32_Addr e_entry; //程序入口的虚拟地址,如果目标文件没有程序入口,为0 
     23 
     24         Elf32_Off e_phoff; //程序头部表格的偏移量(按字节),如果文件中没有,为0
     25 
     26         Elf32_Off e_shoff; //节区头部表格的偏移量(按字节),如果文件中没有,为0    
     27 
     28         Elf32_Word e_flags; //
     29 
     30         Elf32_Half e_ehsize; //ELF头部的大小
     31 
     32         Elf32_Half e_phentsize; //程序头部表格的表项大小。
     33 
     34         Elf32_Half e_phnum; //程序头部表格的表项数目。
     35 
     36         Elf32_Half e_shentsize; //节区头部表格的表项大小。
     37 
     38         Elf32_Half e_shnum; //节区头部表格的表项数目。
     39 
     40         Elf32_Half e_shstrndx;
     41 }Elf_lan;
     42 static Elf_lan lan_elf;
     43 int OpenElf(char *filename)
     44 {
     45     int fd;
     46     fd = open(filename, O_RDONLY);
     47     if(fd == -1){
     48           printf("Open %s Error!\n", filename);
     49           return FALSE; 
     50     }
     51     return fd; 
     52 }
     53 //读取Elf头部表函数 :int ReadElf(int fd);
     54 
     55 int ReadElf(int fd)
     56 {
     57     char str[MAX_SIZE];
     58     int num;
     59     memset(str, 0, MAX_SIZE);
     60     if((num = read(fd, str, 52)) != 52){
     61             perror("File NO ELF!\n");
     62             return FALSE; 
     63     }
     64     if((str[0] == 0x7f) && (str[1] == 'E') && (str[2] == 'L') && (str[3] == 'F')){
     65           printf("This is ELF file.\n");
     66           printf("文件类别: "); 
     67           switch(str[4]){
     68                         case 0:
     69                             printf("非法目标文件\n");
     70                             break;
     71                         case 1:
     72                             printf("32位目标文件\n");
     73                             break;
     74                         case 2:
     75                             printf("64位目标文件\n");
     76                             break;
     77                         default:
     78                             break;             
     79           }
     80           printf("编码格式: ");
     81           switch(str[5]){
     82                   case 0:
     83                       printf("非法编码格式\n");
     84                       break;
     85                   case 1:
     86                       printf("小端编码格式\n");
     87                       break;
     88                   case 2:
     89                       printf("大端编码格式\n");    
     90                       break;
     91                         default:
     92                             break;    
     93           }
     94           printf("文件版本: ");
     95           if(str[6] == 1){
     96                   printf("当前版本\n");
     97           }else{
     98                   printf("NULL\n");
     99           }
    100           printf("目标文件类型: ");
    101           lan_elf.e_type = *((Elf32_Half *)&str[16]);
    102           printf("e_type = %d\t", lan_elf.e_type); 
    103           switch(lan_elf.e_type){
    104                   case 0:
    105                       printf("未知文件类型\n");
    106                       break;
    107                   case 1:
    108                       printf("可重定位文件类型\n");
    109                       break;
    110                   case 2:
    111                       printf("可执行文件\n");
    112                       break;
    113                   case 3:
    114                       printf("动态链接库文件\n");
    115                       break;
    116                   case 4:
    117                       printf("CORE文件\n");
    118                       break;
    119                   default:
    120                       break;                        
    121           }
    122           printf("体系结构为:");
    123           lan_elf.e_machine = *((Elf32_Half *)&str[18]);
    124           printf("e_machine = %d\n", lan_elf.e_machine);
    125           switch(lan_elf.e_machine){
    126                  case 0:
    127                       printf("未知体系结构");
    128                       break;
    129                  case 3:
    130                       printf("Intel 8086"); 
    131           }
    132           printf("版本信息: ");
    133           lan_elf.e_version = *((Elf32_Word *)&str[20]);
    134           if(lan_elf.e_version == 1){
    135                   printf("当前版本\n");
    136           }else{
    137                   printf("NULL\n");
    138           }
    139           printf("程序入口的虚拟地址:");
    140           lan_elf.e_entry = *((Elf32_Word *)&str[24]);
    141           printf("0x%x\n", lan_elf.e_entry);
    142           
    143           printf("程序头部表格的偏移量(按字节): ");
    144           lan_elf.e_phoff = *((Elf32_Off *)&str[28]);
    145           printf("0x%x, %d\n", lan_elf.e_phoff, lan_elf.e_phoff);
    146           
    147           printf("节区头部表格的偏移量(按字节): ");
    148           lan_elf.e_shoff = *((Elf32_Off *)&str[32]);
    149           printf("0x%x, %d\n", lan_elf.e_shoff, lan_elf.e_shoff);
    150           
    151           printf("处理器标志位: ");
    152           lan_elf.e_flags = *((Elf32_Off *)&str[36]);
    153           printf("%d\n", lan_elf.e_flags);
    154           
    155           printf("ELF头文件大小: ");
    156           lan_elf.e_ehsize = *((Elf32_Half *)&str[40]);
    157           printf("0x%x, %d\n", lan_elf.e_ehsize, lan_elf.e_ehsize);
    158           
    159           printf("程序头部表大小: ");
    160           lan_elf.e_phentsize = *((Elf32_Half *)&str[42]);
    161           printf("0x%x, %d\n", lan_elf.e_phentsize, lan_elf.e_phentsize);
    162           
    163           printf("程序头部表的数目:");
    164           lan_elf.e_phnum = *((Elf32_Half *)&str[44]);
    165           printf("0x%x, %d\n", lan_elf.e_phnum, lan_elf.e_phnum);
    166           
    167           printf("节区头部表大小: ");
    168           lan_elf.e_shentsize = *((Elf32_Half *)&str[46]);
    169           printf("0x%x, %d\n", lan_elf.e_shentsize, lan_elf.e_shentsize);
    170           
    171           printf("节区头部表数目: ");
    172           lan_elf.e_shnum = *((Elf32_Half *)&str[48]);
    173           printf("0x%x, %d\n", lan_elf.e_shnum, lan_elf.e_shnum);
    174           
    175           printf("节头表与节名字相对应的表项的索引: ");
    176           lan_elf.e_shstrndx = *((Elf32_Half *)&str[50]);
    177           printf("0x%x, %d\n", lan_elf.e_shstrndx, lan_elf.e_shstrndx);
    178           return TURE; 
    179     }else{
    180           perror("File NO ELF!\n");
    181           return FALSE; 
    182     } 
    183 }
    184 
    185 int main(int argc, char *argv[])
    186 {
    187     int boolen;
    188     if(argc == 2){
    189             boolen = OpenElf(argv[1]);
    190             if(boolen == FALSE){
    191                       return -1; 
    192             }
    193             ReadElf(boolen); 
    194     }
    195 
    196     return 0; 
    197 }


    $./readelf_lan readelf_lan.o
    This is ELF file.
    文件类别: 32位目标文件
    编码格式:  小端编码格式
    文件版本:  当前版本
    目标文件类型: e_type = 1       可重定位文件类型
    体系结构为:e_machine = 3
    Intel 8086版本信息: 当前版本
    程序入口的虚拟地址:0x0
    程序头部表格的偏移量(按字节): 0x0, 0
    节区头部表格的偏移量(按字节): 0xc00, 3072
    处理器标志位: 0
    ELF头文件大小: 0x34, 52
    程序头部表大小: 0x0, 0
    程序头部表的数目:0x0, 0
    节区头部表大小: 0x28, 40
    节区头部表数目: 0xc, 12
    节头表与节名字相对应的表项的索引: 0x9, 9

  • 相关阅读:
    c# PrintDocument 设置自定义纸张大小的示例
    C#获取本地打印机列表,并将指定打印机设置为默认打印机
    水晶报表自定义纸张大小打印 (Crystal Report Print with custom paper size)
    c#打印机设置,取得打印机列表及相应打印机的所有纸张格式
    在C#中设置打印机纸张大小
    打印grid
    获取List集合中最大值的方法
    mysql使用索引优化查询效率
    mysql数据库中标的key的含义
    mysql数据库添加索引优化查询效率
  • 原文地址:https://www.cnblogs.com/Robotke1/p/3045393.html
Copyright © 2011-2022 走看看