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头文件的定义。
linux+v2.6.36/include/linux/elf.h:

/* 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:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define FALSE 0
#define TURE 1
#define MAX_SIZE 52

typedef short int     Elf32_Half;
typedef int         Elf32_Word;
typedef Elf32_Word     Elf32_Addr;
typedef Elf32_Word     Elf32_Off;
/*Elf头部文件部分重要数据*/
typedef struct{
        Elf32_Half e_type;
        Elf32_Half e_machine;
        Elf32_Word e_version;
        Elf32_Addr e_entry; //程序入口的虚拟地址,如果目标文件没有程序入口,为0

Elf32_Off e_phoff; //程序头部表格的偏移量(按字节),如果文件中没有,为0

Elf32_Off e_shoff; //节区头部表格的偏移量(按字节),如果文件中没有,为0

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;
}Elf_lan;
static Elf_lan lan_elf;
int OpenElf(char *filename)
{
    int fd;
    fd = open(filename, O_RDONLY);
    if(fd == -1){
          printf("Open %s Error!\n", filename);
          return FALSE; 
    }
    return fd; 
}
//读取Elf头部表函数 :int ReadElf(int fd);

int ReadElf(int fd)
{
    char str[MAX_SIZE];
    int num;
    memset(str, 0, MAX_SIZE);
    if((num = read(fd, str, 52)) != 52){
            perror("File NO ELF!\n");
            return FALSE; 
    }
    if((str[0] == 0x7f) && (str[1] == 'E') && (str[2] == 'L') && (str[3] == 'F')){
          printf("This is ELF file.\n");
          printf("文件类别: "); 
          switch(str[4]){
                        case 0:
                            printf("非法目标文件\n");
                            break;
                        case 1:
                            printf("32位目标文件\n");
                            break;
                        case 2:
                            printf("64位目标文件\n");
                            break;
                        default:
                            break;             
          }
          printf("编码格式: ");
          switch(str[5]){
                  case 0:
                      printf("非法编码格式\n");
                      break;
                  case 1:
                      printf("小端编码格式\n");
                      break;
                  case 2:
                      printf("大端编码格式\n");    
                      break;
                        default:
                            break;    
          }
          printf("文件版本: ");
          if(str[6] == 1){
                  printf("当前版本\n");
          }else{
                  printf("NULL\n");
          }
          printf("目标文件类型: ");
          lan_elf.e_type = *((Elf32_Half *)&str[16]);
          printf("e_type = %d\t", lan_elf.e_type); 
          switch(lan_elf.e_type){
                  case 0:
                      printf("未知文件类型\n");
                      break;
                  case 1:
                      printf("可重定位文件类型\n");
                      break;
                  case 2:
                      printf("可执行文件\n");
                      break;
                  case 3:
                      printf("动态链接库文件\n");
                      break;
                  case 4:
                      printf("CORE文件\n");
                      break;
                  default:
                      break;                        
          }
          printf("体系结构为:");
          lan_elf.e_machine = *((Elf32_Half *)&str[18]);
          printf("e_machine = %d\n", lan_elf.e_machine);
          switch(lan_elf.e_machine){
                 case 0:
                      printf("未知体系结构");
                      break;
                 case 3:
                      printf("Intel 8086"); 
          }
          printf("版本信息: ");
          lan_elf.e_version = *((Elf32_Word *)&str[20]);
          if(lan_elf.e_version == 1){
                  printf("当前版本\n");
          }else{
                  printf("NULL\n");
          }
          printf("程序入口的虚拟地址:");
          lan_elf.e_entry = *((Elf32_Word *)&str[24]);
          printf("0x%x\n", lan_elf.e_entry);
          
          printf("程序头部表格的偏移量(按字节): ");
          lan_elf.e_phoff = *((Elf32_Off *)&str[28]);
          printf("0x%x, %d\n", lan_elf.e_phoff, lan_elf.e_phoff);
          
          printf("节区头部表格的偏移量(按字节): ");
          lan_elf.e_shoff = *((Elf32_Off *)&str[32]);
          printf("0x%x, %d\n", lan_elf.e_shoff, lan_elf.e_shoff);
          
          printf("处理器标志位: ");
          lan_elf.e_flags = *((Elf32_Off *)&str[36]);
          printf("%d\n", lan_elf.e_flags);
          
          printf("ELF头文件大小: ");
          lan_elf.e_ehsize = *((Elf32_Half *)&str[40]);
          printf("0x%x, %d\n", lan_elf.e_ehsize, lan_elf.e_ehsize);
          
          printf("程序头部表大小: ");
          lan_elf.e_phentsize = *((Elf32_Half *)&str[42]);
          printf("0x%x, %d\n", lan_elf.e_phentsize, lan_elf.e_phentsize);
          
          printf("程序头部表的数目:");
          lan_elf.e_phnum = *((Elf32_Half *)&str[44]);
          printf("0x%x, %d\n", lan_elf.e_phnum, lan_elf.e_phnum);
          
          printf("节区头部表大小: ");
          lan_elf.e_shentsize = *((Elf32_Half *)&str[46]);
          printf("0x%x, %d\n", lan_elf.e_shentsize, lan_elf.e_shentsize);
          
          printf("节区头部表数目: ");
          lan_elf.e_shnum = *((Elf32_Half *)&str[48]);
          printf("0x%x, %d\n", lan_elf.e_shnum, lan_elf.e_shnum);
          
          printf("节头表与节名字相对应的表项的索引: ");
          lan_elf.e_shstrndx = *((Elf32_Half *)&str[50]);
          printf("0x%x, %d\n", lan_elf.e_shstrndx, lan_elf.e_shstrndx);
          return TURE; 
    }else{
          perror("File NO ELF!\n");
          return FALSE; 
    } 
}

int main(int argc, char *argv[])
{
    int boolen;
    if(argc == 2){
            boolen = OpenElf(argv[1]);
            if(boolen == FALSE){
                      return -1; 
            }
            ReadElf(boolen); 
    }

return 0; 
}

$./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

Linux ELF文件学习(1)相关推荐

  1. linux elf 文件理解与分析

    https://linux-audit.com/elf-binaries-on-linux-understanding-and-analysis/ 我们理所当然的使用一些工具.其中一部分就是 linu ...

  2. linux elf 文件查看工具 readelf

    Android在NDK开发工具中提供了readelf,用来帮助开发者查看编译后目标文件的组成结构和具体内容. 常用的有以下几个功能选项: 1)-h或者--file-header 显示在ELF文件头里包 ...

  3. Linux如何找到所有elf文件,linux – ELF文件中的导入表在哪里?

    But you can see in the attached picture,that on the offset 464 there are only zeros. 错误:上次我检查时,01,20 ...

  4. Linux ELF文件

    ELF全称:可执行链接格式,是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的.ELF文件有三种不同类型: 可重定位 ...

  5. Linux repo 文件学习;

    在CentOS下安装gcc:出现一个错误: 看一下里面有repo的字样:下面来学习repo相关内容: 在linux下,repo文件都是存放在/etc/yum.repos.d文件夹之中的.repo文件即 ...

  6. linux elf 文件加密

    ELF头的各个字段如下: #define EI_NIDENT 16 typedef struct{ unsigned char e_ident[EI_NIDENT]; //目标文件标识信息 Elf32 ...

  7. linux 导入函数,共享库 – Linux ELF文件:如何获取属于导入函数的共享对象

    How can I find out from which shared library/shared object the strcp function is obtained? 通常,您不能:该库 ...

  8. Linux里gcc编译过程分析和ELF文件格式学习

    GCC编译器背后的故事及常用命令.了解ELF文件格式 前言 一.GCC简介 二.GCC背后的战友 1.Binutils 2.C运行库 三.GCC编译流程及对应命令 1.编译流程图及命令框图 2.实践操 ...

  9. Ubuntu18.04系统下,gcc编译过程分析、命令参数介绍及ELF文件格式学习

    GCC编译器背后的故事及常用命令.了解ELF文件格式 文章目录 GCC编译器背后的故事及常用命令.了解ELF文件格式 前言 一.GCC简介 二.GCC背后的战友 1.Binutils 2.C运行库 三 ...

  10. Linux ELF文件格式介绍

    文章目录 一.引言 二.介绍 三.ELF目标文件格式 3.1 常见段及对应用途 3.2 目标文件内容解析 3.2.1 代码段.text 3.2.2 只读数据段.rodata 3.2.3 数据段.dat ...

最新文章

  1. 系统服务器端口怎么关闭了,如何关闭云服务器端口号
  2. Ubuntu Linux 安装后,建立嵌入式开发环境
  3. Python pip工具初步学习
  4. k3运行linux,首个Kubernetes操作系统k3OS,附主要功能介绍
  5. 《深入理解Android 卷III》第四章 深入理解WindowManagerService
  6. matplotlib 中文_Python 关于matplotlib无法显示中文字体的解决方法
  7. 自己选择的路,不后悔
  8. 如何做一个让开发人员看得起的软件测试人员
  9. 微信小程序——实现时钟样式
  10. python高维数据存储_Numpy高维数据的理解
  11. Python 2X 版本 痛苦的编码格式,一遍完美解决Unicode、GB2312、GBK编码格式的文章
  12. 数学公式编辑器AxMath
  13. 网站SEO诊断优化有那些不利操作
  14. 基于机智云IoT开发平台的温室番茄远程监控系统
  15. CSS+HTML 顶部导航栏实现
  16. DeprecationWarning: The binary mode of fromstring is deprecated, as it behaves surprisingly on unico
  17. 企业微信和个人微信的区别
  18. java实现奖学金申请,基于ssm+mysql的web助学金申请系统[实现过程记录]
  19. 计算机无线网怎么安装教程,wifi怎么安装,教您电脑如何安装wifi
  20. python表示倍数_倍数 python

热门文章

  1. RailsCasts中文版,#15 Fun with Find Conditions 使用hash为查询条件以便生成正确语法的SQL查询...
  2. 使用Spark core和SparkSQL的窗口函数分别实现分组取topN的操作
  3. crontab 定时执行任务
  4. PostgreSQL 全文检索 - 词频统计
  5. LabVIEW--为控件添加说明信息
  6. Docker-registry + GlusterFS
  7. 十一月份英语学习总结—积累
  8. 关于php开发中用户请求数据的安全问题的一点想法
  9. img的属性alt 与 title的区别
  10. vsftp客户连接常见故障现象