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

GXHT40温湿度传感器:I2C通信与CRC校验实战解析 本文详细解析GXHT40温湿度传感器的I2C通信协议与CRC校验机制,涵盖硬件连接、时序控制、数据读取及加热功能应用。通过实战案例分享优化技巧与异常处理,帮助开发者提升嵌入式系统中传感器数据的准确性与可靠性。 阅读详情

相关推荐

RFSOC实战解析(一):深入RF Data Converter的时钟与数据接口设计

本文深入解析RFSOC中RF Data Converter的时钟与数据接口设计,涵盖GHz采样、数据流处理、时钟树同步及AXI-Stream接口实战。通过详细案例和调试技巧,帮助工程师高效配置IP核,优化ADC/DAC性能,解决跨时钟域和信号完整性等关键问题。

weixin_30955617的博客 473

S5PV210重定位引入和链接脚本

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

1257

基于FPGA的图像预处理系统(图)

 北京机械工业学院 韩娟娟 邓文怡 娄小平 由于获取图像的工具或手段的影响,使获取图像无法完全体现原始图像的全部信息。因此,对图像进行预处理就显得非常重要。预处理的目的是改善图像数据,抑制不需要的变形或者增强某些对于后续处理来说比较重要的图像特征。通常根据预处理目的的不同,把预处理技术归为图像增强技术和图像分割技术两类。图像对比度处理是空间域图像增强的一种方法。由于图像灰度范围狭窄会使

lyzhangxianglove的专栏 1779

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

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

woquhan的博客 2964

西门子PLC与汇川SV660P伺服速度模式配置实战指南

本文详细介绍了西门子S7-1200 PLC与汇川SV660P伺服驱动器在速度模式下的配置实战指南。从硬件接线、伺服参数配置到TIA Portal软件组态和PLC程序编写,提供了全面的操作步骤和实用技巧,帮助工程师快速解决伺服系统调试中的常见问题,确保系统稳定运行。

weixin_30316097的博客 498

PX4固件下VTOL固定翼的3种起飞方式对比:手抛vs弹射vs跑道

本文深度解析PX4固件下VTOL固定翼的三种起飞方式:手抛、弹射与跑道起飞,对比其技术差异与适用场景。通过详细的参数配置和工程实践指南,帮助开发者优化起飞性能,提升无人机在不同环境下的适应性和可靠性。文章特别针对PX4飞控系统的最新特性进行了技术探讨。

weixin_30296405的博客 87

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

白话机器学习算法理论+实战番外篇之Xgboost

1. 写在前面 如果想从事数据挖掘或者机器学习的工作,掌握常用的机器学习算法是非常有必要的,在这简单的先捋一捋, 常见的机器学习算法: 监督学习算法:逻辑回归,线性回归,决策树,朴素贝叶斯,K近邻,支持向量机,集成算法Adaboost等 无监督算法:聚类,降维,关联规则, PageRank等 我前面已经尝试用最白话的语言完成了一个白话机器学习算法理论+实战系列,这个系列已经基本包含了上面这些算...

Miracle8070 1万+

4. ELF可重定位目标文件

ELF可重定位目标文件格式

qq_61300276的博客 1051

AUTOSAR CP R18-10 架构总览与导航

AUTOSAR CP R18-10 架构总览 本文档系统整理了AUTOSAR CP R18-10版本的199篇技术规范,涵盖7大架构层级: 应用层(15篇):包含车身舒适、底盘控制、HMI、安全系统、动力系统等AI应用 RTE(1篇):虚拟功能总线规范 服务层(159篇):细分为系统服务(OS/ECU管理/加密)、存储服务、通信服务(CAN/LIN/Ethernet) ECU抽象层(20篇):I/O与通信硬件抽象 MCAL(41篇):微控制器驱动层 复杂驱动(14篇):CDD设计与SPAL支撑 规范模板(2

一名留德华的,康德的信徒的,专注于汽车电子领域的博客 718

工业相机的检测原理课件(建议收藏)

本课件为各位工程师详细介绍了工业相机的视觉检测原理 ,旨在为工程师调试工业相机的视觉检测原理时提供理论性指导。

Bamboo265925的博客 498

linux ELF文件学习

学习计划

wangyuling1234567890的专栏 801

GEE:哨兵时间序列遥感数据和动态阈值方法计算物候时期EOS/SOS(2)

本文将分享和解释论文《Improved Estimates of Arctic Land Surface Phenology Using Sentinel-2 Time Series》中使用到的基于阈值估算北极地区的植被物候,特别是北极地区的植被季节开始和结束的日期(SoS和EoS)的方法和代码。该方法使用的是使用哨兵2数据,并实现了两种阈值方法( 无平滑的阈值方法、平滑后的阈值方法)。

养乐多的博客 2971

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

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

weixin_39708502的博客 693

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

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

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

学习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

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

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

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

bcbobo21cn的专栏 4188

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

【嵌入式知识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、付费专栏及课程。

余额充值