linux 解析elf文件格式,Linux下ELF文件解析

1. windows PE文件与Linux ELF文件概述

在windows中可执行文件是pe文件格式,Linux中可执行文件是ELF文件,其文件格式是ELF文件格式,在Linux下的ELF文件除了可执行文件(Excutable File),可重定位目标文件(RellocatableObject File)、共享目标文件(SharedObject File)、核心转储文件(CoreDump File)也都是ELF格式文件。

2.ELF文件结构分析

2.1 ELF文件查看

使用Linux下专用工具——readelf来查看elf文件信息

745491b04daa87040d8e5641b15e1758.png

查看readelf中的源码

bd185ba19ee23c08723f7db97cb5a96c.png

一个典型的ELF文件大致的结构如下:

ELF文件大致的结构文件头(ELF Header)

程序头表(Program Header Table)

代码段(.text)

数据段(.data)

bss段(.bss)

段表字符串表(.shstrtab)

段表(Section Header Table)

符号表(.symtab)

字符串表(.strtab)

重定位表(.rel.text)

重定位表(.rel.data)

2.2 FLF文件组成

2.2.1文件头

用于记录一个ELF文件的信息(多少位?能够运行的CPU平台是什么?程序的入口点在哪里)

查看ELF头

806b53e0e08431e58fe6da0b96ba3c70.png

在readelf的源码中变量类型Elf_Internal_Ehdr_,定义在internal头文件中

#define

EI_NIDENT        16                /* Size of e_ident[] */

typedef

struct elf_internal_ehdr {

unsigned char                e_ident[EI_NIDENT];   /* ELF "magic

number" */

bfd_vma                      e_entry;              /* Entry point virtual address */

bfd_size_type                e_phoff;              /* Program header table file offset */

bfd_size_type                e_shoff;              /* Section header table file offset */

unsigned long                e_version;            /* Identifies object file version */

unsigned long                e_flags;              /* Processor-specific flags */

unsigned short               e_type;               /* Identifies object file type */

unsigned short               e_machine;            /* Specifies required architecture */

unsigned int                 e_ehsize;             /* ELF header size in bytes */

unsigned int                 e_phentsize;          /* Program header table entry size */

unsigned int                 e_phnum;

/* Program header table

entry count */

unsigned int                 e_shentsize;           /* Section header table entry size */

unsigned int                 e_shnum;

/* Section header table

entry count */

unsigned int                 e_shstrndx;            /* Section header string table index */

}

Elf_Internal_Ehdr;

在Linux自带的头文件中查看,源码文件头的结构中一共有14个字段,对应到文件16进制中。

42822465e0c06c0ec2f1ef00dc27e7d7.png#define EI_NIDENT (16) 

typedef struct

{

unsigned char     e_ident[EI_NIDENT];        /* Magic number and other info */

Elf32_Half        e_type;                    /* Object file type */

Elf32_Half        e_machine;                 /* Architecture */

Elf32_Word        e_version;                 /* Object file version */

Elf32_Addr        e_entry;                   /* Entry point virtual address */

Elf32_Off         e_phoff;                   /* Program header table file offset */

Elf32_Off         e_shoff;                   /* Section header table file offset */

Elf32_Word        e_flags;                   /* Processor-specific flags */

Elf32_Half        e_ehsize;                  /* ELF header size in bytes */

Elf32_Half        e_phentsize;               /* Program header table entry size */

Elf32_Half        e_phnum;                   /* Program header table entry count */

Elf32_Half        e_shentsize;               /* Section header table entry size */

Elf32_Half        e_shnum;                   /* Section header table entry count */

Elf32_Half        e_shstrndx;                /* Section header string table index */

} Elf32_Ehdr;

2.2.2 程序头表

记录了每个Segment的相关信息,比如类型、对应文件的偏移、大小、属性等。

程序头表和段头表相对独立,它们是由ELF文件头统一管理,程序头表管理ELF文件加载后,ELF文件内可加载段到内存映像的映射关系,一般只有可执行文件中,包含程序头表。程序头表包含多个程序头表项,程序头表描述的对象称为“Segment”,Segment描述的是ELF文件加载后的数据块,段(Section)描述的是ELF文件加载前的数据块。一般来说,来说两者会存在一定的对应关系,比如代码段.text的加载信息保存在程序头表项对应存放代码的Segment中,数据段.data的加载信息保存在程序头表项对应存放数据的Segment中。有时候为了简化程序头表项的个数,会把同类型的多个段,设置整个ELF文件作为一个Segment。

f30a304feac32ff4fea33ed04e67c081.png

程序头表的数据结构/* Program segment header.  */

typedef struct

{

Elf32_Word        p_type;                 /* Segment type */

Elf32_Off         p_offset;               /* Segment file offset  Segment对应的内容在文件的偏移*/

Elf32_Addr        p_vaddr;                /* Segment virtual address Segment在内存中的线性地址*/

Elf32_Addr        p_paddr;                /* Segment physical address */

Elf32_Word        p_filesz;               /* Segment size in file */

Elf32_Word        p_memsz;                /* Segment size in memory */

Elf32_Word        p_flags;                /* Segment flags */

Elf32_Word        p_align;                /* Segment alignment */

} Elf32_Phdr;

#define        PT_NULL           0                /* Program header table entry unused */

#define        PT_LOAD           1                /* Loadable program segment */

#define        PT_DYNAMIC        2                /* Dynamic linking information */

#define        PT_INTERP         3                /* Program interpreter */

#define        PT_NOTE           4                /* Auxiliary information */

#define        PT_SHLIB          5                /* Reserved */

#define        PT_PHDR           6                /* Entry for header table itself */

#define        PT_TLS            7                /* Thread-local storage segment */

#define        PT_NUM            8                /* Number of defined types */

p_flag权限属性标志

值 说明 宏1 可执行 PE_X

2 可写 PE_W

3 可读 PE_R

2.2.3 区段头表

用于记录ELF文件的主要的数据

查看区段

2bdda38a3de993e776c5692283289362.png

区段表头的数据结构:

typedef struct

{

Elf32_Word sh_name; /* Section name (string tbl index) */

Elf32_Word sh_type; /* Section type */

Elf32_Word sh_flags; /* Section flags */

Elf32_Addr sh_addr; /* Section virtual addr at execution */

Elf32_Off sh_offset; /* Section file offset */

Elf32_Word sh_size; /* Section size in bytes */

Elf32_Word sh_link; /* Link to another section */

Elf32_Word sh_info; /* Additional section information */

Elf32_Word sh_addralign; /* Section alignment */

Elf32_Word sh_entsize; /* Entry size if section holds table */

} Elf32_Shdr;

区段头表一共有10个字段,含义如下:

(1)sh_name段名,是一个是一个4字节的偏移,记录了段名字符串在段表字符串表(“.shstrtab”段)内的偏移。段表字符串并非表的形式,而是一个文件块,保存了所有的段表字符串内容,存储在“.shstrtab”的段中,根据“.shstrtab”的偏移,加上sh_name便可以访问到每个段对应的段名字符串。

56ba12df45fcb95f13edb7bf08aa082e.png

04644caa60e3cfe0cc319fdba3ada402.png

起始地址是000017ac,第一个段表项全0,sh_name在段表项中的偏移是001b,由上图可以得到“.shstrtab”段的偏移是0016ae,

所以,计算段名的偏移应该是0x0000001b+ 0x000016ae = 0x000016c9

根据计算的结果,查看0x000016c9处:

c8b51948461f8178eb161b9079397ff2.png

(2)sh_type,表示段的类型。段的类型有很多,常见的有SHT_PROGBITS,表示程序数据,SHT_SYMTAB表示符号表,SHT_STRTAB表示字符串表,还有专门存放构造函数数组段SHT_INIT_ARRAY,析构函数数组段SHT_FINI_ARRAY。

a   .txt 代码段

b   .data 数据段

c   .radata记录常量数据

d   .symtab记录符号表(相当于PE文件的导出表)的数据

e   .strtab 串表段

f   .shstrtab 有段表 字符串表段

g   .rel .plt记录某个区段的重定位内容(相当于PE文件的导入表)

对应的宏如下:

/* Legal values for sh_type (section type).  */

#define SHT_NULL              0       /* Section header table entry unused */

#define SHT_PROGBITS          1       /* Program data */

#define SHT_SYMTAB            2       /* Symbol table */

#define SHT_STRTAB            3       /* String table */

#define SHT_RELA              4       /* Relocation entries with addends */

#define SHT_HASH              5       /* Symbol hash table */

#define SHT_DYNAMIC           6       /* Dynamic linking information */

#define SHT_NOTE              7       /* Notes */

#define SHT_NOBITS            8       /* Program space with no data (bss) */

#define SHT_REL               9       /* Relocation entries, no addends */

#define SHT_SHLIB             10      /* Reserved */

#define SHT_DYNSYM            11      /* Dynamic linker symbol table */

#define SHT_INIT_ARRAY        14      /* Array of constructors */

#define SHT_FINI_ARRAY        15      /* Array of destructors */

#define SHT_PREINIT_ARRAY     16      /* Array of pre-constructors */

(3)sh_flags,表示段标志,记录段的属性。其中0表示默认属性,1表示段可写,取值位SHF_WRITE。2表示段加载后需要为之分配内存空间,取值为SHF_ALLOC。4表示可执行,取值为SHF_EXECINSTR,段标志属性可以叠加。

(4)sh_addr,表示段加载后的线性地址

(5)sh_offset,表示段在文件内的偏移,根据此偏移可确定段的位置,读取段的内容。

(6)sh_size,表示段的大小,单位为字节。需要注意的是,如果段类型为SHT_NOBITS,段内没有数据,那么段的大小并非指文件块的大小,而是指段加载后占用内存的大小。

27419630f1fdd45c602e1d92184c21d6.png

(7~8)sh_link和sh_info表示段的链接信息,一般用于描述符号表段和重定位表段的链接信息。对于符号表段(SHT_SYMTAB),sh_link记录的是符号表使用的串表所在段(一般是,.strtab)对应段表项在段表内的索引。

5461ebe004834732c52c8d19349399f2.png

sh_info记录的是符号表最后一个局部符号的符号表项在符号表内的索引加1,一般恰好是第一个全局符号的符号表项索引,这样可以帮助连接器更快的地定位到第一个全局符号。如下图:段中符号表段的信息sh_info,刚好是局部符号+1的索引。

c8bd678e49824e43c3cbc6d1e4907894.png

对于重定位表表段(段类型是SHT_REL),sh_link记录重定位所作用的符号表段表项早段内的索引,而sh_info记录重定位所作用的段对应的段表项在段表中的索引。

6e624e51c03d1018ae59f052a960833a.png

sh_type sh_link sh_infoSHT_DYNAMIC 此表项中条目所用到的字符串表在段表中的索引

SHT_HASH 此哈希表所适用的符号表的段表索引

SHT_REL 相关符号表的段表索引 重定位所使用的段的段表索引

SHT_RELA 相关联的字符串表的段表索引 最后一个局部符号的符号表索引值+1

其它 SHN_UNDEF 0

(9)sh_addralign,表示段的对齐方式,对齐规则为 sh_offset %sh_addralign = 0,即段的文件偏移必须是sh_addralign的整数倍,sh_addralign的取值必须是2的整数倍,入1、2、4、8等。

对齐值 对齐方式 说明0 无对齐要求

1 无对齐要求

4 对齐4 满足sh_iffset % 4 = 0

16 对齐16 满足sh_iffset % 16 = 0

32 对齐32 满足sh_iffset % 32 = 0

3559489fa2c54d20b767b210903a7ab1.png

(10) sh_entsize,一般用于保存注入符号表段,重定位表段时,表示段内保存表的表项大小。例如符号表段“.symtab”内保存的符号表的表项大小为sizeof(Elf32_sym)= 16字节,重定位表段“.rel.plt”内保存的重定位表的表项大小为sizeof(Elf32_rel)= 8字节。

2.2.4 ELF符号表(Symbol Table)

ELF文件的符号表保存了程序中的符号信息,包括程序中的文件名、函数名、全局变量名等,符号表一般保存在名为“.strtab”的段内,该段对应段表项的类型为SHT_SYMTAB。符号表包含多个符号表项,每个符号表项记录了符号的名称、位置、类型等信息。

符号表象的数据结构:typedef struct

{

Elf32_Word st_name; /* Symbol name (string tbl index) */

Elf32_Addr st_value; /* Symbol value */

Elf32_Word st_size; /* Symbol size */

unsigned char st_info; /* Symbol type and binding */

unsigned char st_other; /* Symbol visibility */

Elf32_Section st_shndx; /* Section index */

} Elf32_Sym;

0200226d57198c995d2e10487b011f13.png

2.2.5 ELF重定位表(Reloc Table)

重定位表常见于可重定位目标文件内,对于静态链接生成的可可执行文件,一般不包括重定位表,动态链接生成的可执行文件暂时不讨论。重定位表一般保存在以名为“.rel”开头的段内,该段对应段表项的类型为SHT_REL,ELF文件需要重定位的段,一般都对应一个重定位表,比如代码段“.txt”的重定位表保持在“.rel.text”内,数据段“.data”的重定位表保持在“.rel.data”内。

重定位表包含多个重定位表项,每个重定位表项记录一条重定位信息,包括重定位的符号、位置、类型等。

/* Relocation table entry without addend (in section of type SHT_REL). */

typedef struct

{

Elf32_Addr r_offset; /* Address */

Elf32_Word r_info; /* Relocation type and symbol index */

} Elf32_Rel;

b73e5dcfc32be1bd83e67dfc8f52cc70.png

2.2.6 ELF串表(String Table)

ELF文件内的段表和符号表需要记录段名和符号名,这些名称都是字符串。然而,段表项和符号表项都是固定长度的数据结构,无法存储不定长的字符串。因此FLE文件将名称字符串内容集中存放在一个段内,称为串表。这些段表项和符号表项只需记录段名字符串或符号名字符串在对应串表项的位置即可。

虽然存储的字符串表的内容称为串表,但是并非表的形式,而是一个文件区域。

645294af3af7fcfa983ba4bbcb2f79fa.png

-End-

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/396173.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

富爸爸穷爸爸害了我_这是我必须告诉爸爸的-在我们的时间用完之前

富爸爸穷爸爸害了我by Bram Bos通过Bram Bos 这是我必须告诉爸爸的-在我们的时间用完之前 (This is what I must tell my dad — before our time runs out) I was a young boy in the 1980s. Like the typical Generation-X kid, I grew up in the days of the home computer…

应用容器公共免费部署平台

从网上信息,发现了一个公共的容器部署平台 openshift.com,可以将我们封装好的docker镜像部署到平台上, 这样就不需要拥有一台云服务器了。对于测试环境非常有用。 首先当然是需要注册。这里全英文 第二,注册之后需要选择你想要的套…

西门子rwd68温控器说明书_西门子RWD68说明书

西门子RWD68说明书显示第一界面Y1XX模拟量输出电压值YIXX传感器此时实际温度;同时按—键五秒显示第二界面PS4主控制回路参数;按—键显示第三界面PS3辅助回路参数但仅在室外补偿时出现;按—键显示第四界面PS2按—键显示第五界面PS1控制曲线运用…

linux 内存管理优化,Linux性能优化实战 内存篇 阅读笔记

第十五讲 基础篇:Linux内存是怎么工作的(2020.6.8)这一讲相关的内容正好之前看csapp的时候总结了一下,可以直接贴出来作为总结了。Linux的内存工作原理,这又是一个特别大的话题。一切向着尽量利用物理资源的方向在发展,在没有虚拟…

傅里叶变换与大数乘法

我们知道,两个 N 位数字的整数的乘法,如果使用常规的算法,时间复杂度是 O(N2)。然而,使用快速傅里叶变换,时间复杂度可以降低到 O(N logN loglogN)。 假设我们要计算以下两个 N 位数字的乘积: a (aN-1aN-2…

idea 启动界面导入项目_如何为您的项目启动有效的登录页面

idea 启动界面导入项目by Christian-Peter Heimbach通过克里斯蒂安彼得海姆巴赫 如何为您的项目启动有效的登录页面 (How to launch an effective landing page for your project) I began my career 10 years ago doing online marketing and advertising for video games. S…

linux程序已经在后台运行冻结了_如何使程序在Linux后台运行

经常在Linux上面运行程序都有这样的体验:某个程序运行的时候,会产生大量的log(提示)信息,但实际上我们只想让它跑一下而已,log暂时不需要或者后面才有需要。同时run多个相同或者不同程序的时候,占了好多的命令行界面&a…

数字时代的精益组织

精益IT应该帮助简化和改善我们为客户创造价值的方式,并提出面向未来的更好的解决方案。未来的组织将专注于同行业的产品或业务流——其他的一切,包括专家和管理者在内,都是为了让一线工作人员可以第一时间就做好,而又不会遇到任何…

9th week blog

1、第一个计算机语言:Fortran Fortran I诞生于1957年,由IBM设计,是世界上第一个真正的计算机语言。 Fortran I运行于IBM 704计算机上。 Fortran I支持:变量(变量名最多6个字符)、If和do语句、格式化IO。 不…

cas单点登录系统:客户端(client)详细配置(包含统一单点注销配置)

最近一直在研究cas登录中心这一块的应用,分享一下记录的一些笔记和心得。后面会把cas-server端的配置和重构,另外还有这几天再搞nginxcas的https反向代理配置,以及cas的证书相关的知识分享出来。 Cas由两部分组成,Cas Server和Cas…

open 端口打开Linux,linux – nmap显示打开的端口,但netstat没有

如果我使用nmap扫描我的服务器,则表明已打开21端口.但是当我登录到这个服务器并运行netstat时,我什么也看不见.$nmap -sT serverStarting Nmap 4.76 ( http://nmap.org ) at 2009-06-24 11:54 MSDInteresting ports on server (x.x.x.x):Not shown: 994 filtered portsPORT STA…

技能学习重要的古语_学习方法:最重要的开发人员技能

技能学习重要的古语by Preethi Kasireddy通过Preethi Kasireddy 学习方法:最重要的开发人员技能 (Learning How to Learn: The Most Important Developer Skill) Being an efficient learner is at least as important as being an efficient coder.成为高效的学习…

Git submodule 的笔记

这次不造轮子,写写文章。file最近想把自己的 blog 整理到 github 上,但由于其中依赖了一些主题以及插件,这时候 git submodule 就能很好的处理这种情况了。 submodule 是什么? submodule 顾名思义,子模块。在一个项目依…

Leetcode690.Employee Importance员工的重要性

给定一个保存员工信息的数据结构,它包含了员工唯一的id,重要度 和 直系下属的id。 比如,员工1是员工2的领导,员工2是员工3的领导。他们相应的重要度为15, 10, 5。那么员工1的数据结构是[1, 15, [2]],员工2的数据结构是…

linux进程管理类命令大全,Linux进程管理类命令

一、htop命令选项-d #:指定延迟时间间隔;-u UserName:仅显示指定用户的进程;-s COLUME:以指定字段进行排序;子命令:l:显示选定的进程打开的文件列表;s:跟踪选…

C#操作Excel

1.System.Data.DataTable连接数据库 ExcelFile 是excel的完整路径//OleDbConnection conExcel new OleDbConnection("ProviderMicrosoft.Jet.OLEDB.4.0;Data Source" ExcelFile ";Extended PropertiesExcel 8.0");//if (conExcel.State ConnectionStat…

android抓包工具——使用fiddler4在安卓手机抓包

Fiddler是一款非常流行并且实用的http抓包工具,它的原理是在本机开启了一个http的代理服务器,然后它会转发所有的http请求和响应,因此,它比一般的firebug或者是chrome自带的抓包工具要好用的多。不仅如此,它还可以支持…

rust风化速度_反驳《Golang、Rust的执行速度的对照,让人大吃一惊。》——不会别瞎说...

首先我无意引战,但是今天看到某位同学的文章里有某些错误,不得不指正一下。1. 测量时间的时候我们使用 std::time::SystemTime::elapsed 即可,不必蹩脚的使用两个 system 输出出来还得手动算一下。(当然你说对Rust不熟也就无所谓了)2. 最重要…

css flexbox模型_代码简介:CSS Flexbox有点像旅行

css flexbox模型Here are three stories we published this week that are worth your time:这是我们本周发布的三个值得您关注的故事: CSS Flexbox explained by road tripping across the country: 7 minute read CSS Flexbox在全国各地的旅途中进行了解释&#…

Sharepoint 2013设置customErrors

原文地址:http://www.cnblogs.com/renzh/archive/2013/03/05/2944309.html#3407239 一、首先设置IIS中的Web.config文件 找到对应的IIS应用程序目录,如:C:\inetpub\wwwroot\wss\VirtualDirectories\3000 在此文件夹下包含一个web.config文件&…