Linux ELF文件学习

代码重定位——ELF分段、API/ABI、指定函数(变量)地址、分散加载、函数调用过程、动态加载 ELF目标文件分段结构 小狼@http://blog.youkuaiyun.com/xiaolangyangyang 一、采用分段结构的优点 1、分段易于管理,对于代码段,是只读的,而数据段可读写; 2、提高cache命中率,Icache与Dcache分开,分别读取代码段和数据段的数据; 3、未初始化数据放在.bss段,生成bin文件时不包含该部分,只在startup.s的指令中保存.bss段的__bss_start和__bss_end地址,减小bi... 阅读详情
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:

#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


转自:http://blog.chinaunix.net/uid-21273878-id-1828736.html

第二十一篇: `man`, `tldr`, `explain` - 授人以渔的“终极指令” 我们已经一起学习了19个(组)关键命令,从nvidia-smi到kubectl。但这只是GPU运维武器库中的一小部分。技术日新月异,新的工具、新的参数会不断涌现。这个系列的真正目的,不仅是教会你使用这19件“兵器”,更是希望你掌握学习任何一件新兵器的方法论。这套方法论就是你的“终极指令”。授人以鱼不如授人以渔。至此,《GPU运维指令集:20个关键命令的战地手册》系列已全部完结。我们从nvidia-smi出发,一路探索了监控、诊断、容器、集群、网络、性能等各个维度的命令,最后用自动化脚本将它们串联,又用。 阅读详情

相关推荐

Pytorch的一小步,昇腾芯片的一大步

相信在AI圈的人多多少少都看到了最近的信息:PyTorch最新2.1版本宣布支持华为昇腾芯片!

机器学习-深度学习-图像处理-opencv-段子 5240

S5PV210重定位引入和链接脚本

参考朱老师教程视频讲解,视频链接地址:http://edu.51cto.com/lecturer/user_id-9584512.html 1、重定位相关概念位置无关码(PIC,position independent code):汇编源文件被编译成二进制可执行文件时编码方式与位置(内存地址)无关。 位置有关码:汇编源文件被编译成二进制可执行程序后和位置(内存地址)有关。 链接地址:链接时指定的

1257

A Mathematical Framework for Transformer Circuits—(一)

transformer数学理解理论

qq_42208244的博客 2660

计算机系统篇之链接(5):静态链接(下)——重定位

本文描述了 Linux 系统中重定位的工作原理,并介绍了 X86_64 中常见的重定位类型。

woquhan的博客 2964

论文学习:Towards Automated Infographic Design:Deep Learning-based Auto-generation of Extensible Timeline

title: “Towards Automated Infographic Design:Deep Learning-based Auto-generation of Extensible Timeline” tags: [“论文评述”, “报告”] date: 2019-08-22 author: 叶慧 mathjax: true 论文:Towards Automated Infograph...

xiaoyehahaha的博客 731

手把手教你用U盘制作Win11启动盘:从PE工具到系统安装全流程

本文提供了一份详尽的Windows 11启动盘制作与系统安装全流程指南。从选择合适的U盘和PE工具开始,重点讲解了如何正确制作包含EFI引导分区的启动盘,并详细演示了在PE环境下进行GPT分区、使用CGI工具安装系统镜像,以及首次启动后的关键设置步骤,帮助用户轻松完成从零到一的系统安装。

weixin_28475533的博客 1286

Linux ELF文件学习(1)

(转载)http://blog.chinaunix.net/uid-21273878-id-1828736.html ELF头文件学习 ELF文件原名Executable and Linking Format,译为“可执行可连接格式”。 ELF规范中把ELF文件宽泛的称为“目标文件”,这与我们平时的理解不同。一般的,我们把编译但没有链接的文件(比如Linux下的.o文件)称为目标文件。而EL...

weixin_30444105的博客 228

第076篇:大涡模拟进阶

大涡模拟(Large Eddy Simulation, LES)是介于直接数值模拟(DNS)和雷诺平均方法(RANS)之间的重要湍流模拟方法。本文深入介绍动态Smagorinsky模型、尺度相似模型、WALE模型和隐式LES等高级亚格子模型。通过Python实现槽道湍流和圆柱绕流的LES求解器,详细展示近壁面处理、动态系数计算和亚格子应力建模技术。大涡模拟,亚格子模型,动态模型,Smagorinsky,WALE模型,尺度相似模型,近壁面处理,槽道湍流LES的核心思想是对Navier-Stokes方程进行空间

2401_87715305的博客 736

4. ELF可重定位目标文件

ELF可重定位目标文件格式

qq_61300276的博客 1051

无人船 | 基于ROS的轻量级多无人艇自主导航仿真框架

无人水面艇USV是海上自主无人系统的关键组成部分,在民用、军用领域应用广泛,例如海上救援、水质测量、扫雷反潜等,因此对USV导航算法的研究具有重要意义。为了便于进行USV相关算法的验证与部署,本文介绍的这套基于ROS的多无人艇的自主导航仿真平台,轻量化地实现了建图、规划、控制等多种功能,同时自研框架保证了功能的高度可拓展和可定制

FRIGIDWINTER的博客 8000

如何免费快速的部署一个自用免VIP的自动追剧工具!NAS新玩法!豆瓣与迅雷结合,轻松实现自动下载电影自动追剧功能(支持Windows,群晖nas,飞牛nas,软路由,服务器等Docker快速部署)

追剧党狂喜!豆瓣追剧神器,自动添加云盘,自动下载,微信通知,一键搞定!

XiaoqiangClub的博客 4582

linux ELF文件学习

学习计划

wangyuling1234567890的专栏 801

2025年GESP12月认证C++一级真题解析

摘要 本文包含13道C++编程选择题,每题2分,涉及语法、运算符、循环、条件判断等知识点。题目考察了变量命名规则、运算符优先级、赋值语句、循环控制、格式化输出等内容。部分题目解析与标答存在差异,例如第4题逗号表达式输出应为5但标答为4,第6题赋值运算结果应为4 4但标答为3 4等。这些题目适合用于检验基础编程能力,但需注意部分标答可能存在争议,建议结合具体编译环境验证。最后一题考察"漂亮数"判断,要求填写数字某位能被3整除的条件表达式。

lan_in的博客 742

u32 头文件 linux,Linux ELF文件学习(1)

ELF头文件学习ELF文件原名Executable and Linking Format,译为“可执行可连接格式”。ELF规范中把ELF文件宽泛的称为“目标文件”,这与我们平时的理解不同。一般的,我们把编译但没有链接的文件(比如Linux下的.o文件)称为目标文件。而ELF文件仅指链接好的可执行文件。在ELF规范中,所用符合ELF规范的文件都成为ELF文件,也成为目标文件,这两个名字意义相同。经过...

weixin_39708502的博客 693

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

学习linux下elf文件的一些整理

ELF文件格式分析  http://os.pku.edu.cn:8080/gaikuang/submission/TN05.ELF.Format.Summary.pdf 后面了解了一些got和plt表在动态链接的相关: http://www.codeproject.com/Articles/30824/PLT-redirection-through-shared-object-inj

runksun的专栏 461

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Linux下的ELF可执行文件学习总结

Linux下的ELF可执行文件的格式解析 http://blog.youkuaiyun.com/xuchao1229/article/details/8915831 目录(?)[+] ELF(Executable and Linking Format)是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中都放了什么东西、以及都以什么样的格式去放这些东西。它自最早在 Sys

bcbobo21cn的专栏 4188

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

【嵌入式知识03】Linux下 GCC编译及ELF文件学习

本文主要介绍gcc编译过程中的常用命令及工具,简要介绍ELF文件格式

weixin_52241626的博客 844

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

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

weixin_58201211的博客 1789

Linux GCC常用命令及ELF文件格式基础学习

gcc常用命令及ELF基础学习

qq_53088119的博客 436

Linux基础学习笔记01--编译、链接的基石-ELF文件详解

ELF文件介绍

zhouyuming_hbxt的博客 430

Linux系统下ELF可执行文件的装载与执行

本文内容参考自《程序员的自我修养——链接、装载与库》

你是谁的博客 1068

1——man elf的翻译——ELF header (Ehdr)

学习ptrace,学习linux本质,elf是无法绕开的一个基础知识。所有的网上知识都没有man elf来的准确和优秀,唯一艰难的一点就是英文。这么多年,我也没在网上找到一个翻译版本,所以我觉得好好阅读一下,并用自己的话翻译出来于大家分享是一件不错的事情。抽空我会进行翻译和更新。在翻译结束之前就暂时不排版了,等都结束了,我会再汇总,认真设计一下结构。 名字 elf——可执行和链接格式文件...

We do not claim to have achieved that goal in every aspect of the software, but we strive for it. 864
上一篇: ld中文使用手册完全版
下一篇: cmake与autoconf+automake的对比
coo1fish
博客等级 码龄14年 22粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值