ELF文件格式

ELF文件格式

ELF文件(Executable Linkable Format)是一种文件存储格式。Linux下的目标文件和可执行文件都按照该格式进行存储,有必要做个总结。

概要

本文主要记录总结32位的Intel x86平台下的ELF文件结构。ELF文件以Section的形式进行存储。代码编译后的指令放在代码段(Code Section),全局变量和局部静态变量放到数据段(Data Section)。文件以一个“文件头”开始,记录了整个文件的属性信息。

未链接的目标文件结构

SimpleSection.c

int printf(const char* format, ...);int global_init_var = 84;
int global_uniit_var;void func1(int i)
{printf("%d\n", i);
}int main(void)
{static int static_var = 85;static int static_var2;int a = 1;int b;func1(static_var + static_var2 + a + b);return a;
}

对于上面的一段c代码将其编译但是不链接。gcc -c -m32 SimpleSection.c( -c表示只编译不链接,-m32表示生成32位的汇编)得到SimpleSection.o。可以用objdump或readelf命令查看目标文件的结构和内容。

ELF文件头

可以用readelf -h查看文件头信息。执行readelf -h SimpleSection.o后:

root@DESKTOP-2A432QS:~/c# readelf -h SimpleSection.o 
ELF Header:Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class:                             ELF32Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              REL (Relocatable file)Machine:                           Intel 80386Version:                           0x1Entry point address:               0x0Start of program headers:          0 (bytes into file)Start of section headers:          832 (bytes into file)Flags:                             0x0Size of this header:               52 (bytes)Size of program headers:           0 (bytes)Number of program headers:         0Size of section headers:           40 (bytes)Number of section headers:         13Section header string table index: 10

程序头包含了很多重要的信息,每个字段的含义可参考ELF结构文档。主要看下:

  1. Entry point address:程序的入口地址,这是没有链接的目标文件所以值是0x00
  2. Start of section headers:段表开始位置的首字节
  3. Size of section headers:段表的长度(字节为单位)
  4. Number of section headers:段表中项数,也就是有多少段
  5. Start of program headers:程序头的其实位置(对于可执行文件重要,现在为0)
  6. Size of program headers:程序头大小(对于可执行文件重要,现在为0)
  7. Number of program headers:程序头中的项数,也就是多少Segment(和Section有区别,后面介绍)
  8. Size of this header:当前ELF文件头的大小,这里是52字节

段表及段(Section)

段表

ELF文件由各种各样的段组成,段表就是保存各个段信息的结构,以数组形式存放。段表的起始位置,长度,项数分别由ELF文件头中的Start of section headers,Size of section headers,Number of section headers指出。使用readelf -S SimpleSection.o查看SimpleSection.o的段表如下:

There are 13 section headers, starting at offset 0x340:Section Headers:[Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al[ 0]                   NULL            00000000 000000 000000 00      0   0  0[ 1] .text             PROGBITS        00000000 000034 000062 00  AX  0   0  1[ 2] .rel.text         REL             00000000 0002a8 000028 08   I 11   1  4[ 3] .data             PROGBITS        00000000 000098 000008 00  WA  0   0  4[ 4] .bss              NOBITS          00000000 0000a0 000004 00  WA  0   0  4[ 5] .rodata           PROGBITS        00000000 0000a0 000004 00   A  0   0  1[ 6] .comment          PROGBITS        00000000 0000a4 000036 01  MS  0   0  1[ 7] .note.GNU-stack   PROGBITS        00000000 0000da 000000 00      0   0  1[ 8] .eh_frame         PROGBITS        00000000 0000dc 000064 00   A  0   0  4[ 9] .rel.eh_frame     REL             00000000 0002d0 000010 08   I 11   8  4[10] .shstrtab         STRTAB          00000000 0002e0 00005f 00      0   0  1[11] .symtab           SYMTAB          00000000 000140 000100 10     12  11  4[12] .strtab           STRTAB          00000000 000240 000065 00      0   0  1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings)I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)

总共有13个Section,重点关注.text, .data, .rodata, .symtab, .rel.text段。

代码段

.text段保存代码编译后的指令,可以用objdump -s -d SimpleSection.o查看SimpleSection.o代码段的内容。

SimpleSection.o:     file format elf32-i386Contents of section .text:0000 5589e583 ec0883ec 08ff7508 68000000  U.........u.h...0010 00e8fcff ffff83c4 1090c9c3 8d4c2404  .............L$.0020 83e4f0ff 71fc5589 e55183ec 14c745f0  ....q.U..Q....E.0030 01000000 8b150400 0000a100 00000001  ................0040 c28b45f0 01c28b45 f401d083 ec0c50e8  ..E....E......P.0050 fcffffff 83c4108b 45f08b4d fcc98d61  ........E..M...a0060 fcc3                                 ..              
...省略          Disassembly of section .text:00000000 <func1>:0:   55                      push   %ebp1:   89 e5                   mov    %esp,%ebp3:   83 ec 08                sub    $0x8,%esp6:   83 ec 08                sub    $0x8,%esp9:   ff 75 08                pushl  0x8(%ebp)c:   68 00 00 00 00          push   $0x011:   e8 fc ff ff ff          call   12 <func1+0x12>16:   83 c4 10                add    $0x10,%esp19:   90                      nop1a:   c9                      leave  1b:   c3                      ret    0000001c <main>:1c:   8d 4c 24 04             lea    0x4(%esp),%ecx20:   83 e4 f0                and    $0xfffffff0,%esp23:   ff 71 fc                pushl  -0x4(%ecx)26:   55                      push   %ebp27:   89 e5                   mov    %esp,%ebp29:   51                      push   %ecx2a:   83 ec 14                sub    $0x14,%esp2d:   c7 45 f0 01 00 00 00    movl   $0x1,-0x10(%ebp)34:   8b 15 04 00 00 00       mov    0x4,%edx3a:   a1 00 00 00 00          mov    0x0,%eax3f:   01 c2                   add    %eax,%edx41:   8b 45 f0                mov    -0x10(%ebp),%eax44:   01 c2                   add    %eax,%edx46:   8b 45 f4                mov    -0xc(%ebp),%eax49:   01 d0                   add    %edx,%eax4b:   83 ec 0c                sub    $0xc,%esp4e:   50                      push   %eax4f:   e8 fc ff ff ff          call   50 <main+0x34>54:   83 c4 10                add    $0x10,%esp57:   8b 45 f0                mov    -0x10(%ebp),%eax5a:   8b 4d fc                mov    -0x4(%ebp),%ecx5d:   c9                      leave  5e:   8d 61 fc                lea    -0x4(%ecx),%esp61:   c3                      ret

可以看到.text段里保存的正是func1()和main()的指令。

数据段和只读数据段

.data段保存的是已经初始化了的全局静态变量和局部静态变量。前面SimpleSection.c中的global_init_varabal和static_var正是这样的变量。使用objdump -x -s -d SimpleSection.o查看:

Contents of section .data:0000 54000000 55000000                    T...U...        
Contents of section .rodata:0000 25640a00                             %d..            

最左边的0000是偏移,不用看,后面跟着的0x00000054和0x00000055正是global_init_varabal和static_var的初始值。
.rodata段存放的是只读数据,包括只读变量(const修饰的变量和字符串常量),这个例子中保存了"%d\n"正是调用printf的时候使用的字符常量。

符号表段

符号表段一般叫做.symtab,以数组结构保存符号信息(函数和变量),对于函数和变量符号值就是它们的地址。主要关注两类符号:

  1. 定义在目标文件中的全局符号,可以被其他目标文件引用,比如SimpleSction.o里面的func1, main和global_init_var。
  2. 在本目标文件中引用的全局符号,却没有定义在本目标文件,比如pritnf。

可以用readelf -s SimpleSection.o查看SimpleSection.o的符号:

Symbol table '.symtab' contains 16 entries:Num:    Value  Size Type    Bind   Vis      Ndx Name0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 1: 00000000     0 FILE    LOCAL  DEFAULT  ABS SimpleSection.c2: 00000000     0 SECTION LOCAL  DEFAULT    1 3: 00000000     0 SECTION LOCAL  DEFAULT    3 4: 00000000     0 SECTION LOCAL  DEFAULT    4 5: 00000000     0 SECTION LOCAL  DEFAULT    5 6: 00000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.14887: 00000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.14898: 00000000     0 SECTION LOCAL  DEFAULT    7 9: 00000000     0 SECTION LOCAL  DEFAULT    8 10: 00000000     0 SECTION LOCAL  DEFAULT    6 11: 00000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var12: 00000004     4 OBJECT  GLOBAL DEFAULT  COM global_uniit_var13: 00000000    28 FUNC    GLOBAL DEFAULT    1 func114: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf15: 0000001c    70 FUNC    GLOBAL DEFAULT    1 main

可以看到:

  1. func1和main的Ndx对应的值是1,表示在.text段(.text段在段表中的索引是1),类型是FUNC,value分别是0x00000000和0x0000001c,表明这两个函数指令字节码的首字节分别在.text段的0x00000000和0x0000001c偏移处。
  2. printf的Ndx是UND,表明这个符号没有在SimpleSection.o中定义,仅仅是被引用。
  3. global_init_var和static_var.1488两个符号的Ndx都是3,说明他们被定义在数据段,value分别是0x00000000和0x00000004,表示这个符号的位置在数据段的0x00000000和0x00000004偏移处,翻看上一节
Contents of section .data:0000 54000000 55000000                    T...U... 

数据段0x00000000和0x00000004偏移处保存的正是global_init_var和static_var这两个变量。

重定位表段

重定位表也是一个段,用于描述在重定位时链接器如何修改相应段里的内容。对于.text段,对应的重定位表是.rel.text表。使用objdump -r SimpleSection.o查看重定位表。

SimpleSection.o:     file format elf32-i386RELOCATION RECORDS FOR [.text]:
OFFSET   TYPE              VALUE 
0000000d R_386_32          .rodata
00000012 R_386_PC32        printf
00000036 R_386_32          .data
0000003b R_386_32          .bss
00000050 R_386_PC32        func1

printf对应的那行的OFFSET为0x00000012,表明.text段的0x00000012偏移处需要修改。我们objdump -s -d SimpleSection.o查看代码段的0x00000012偏移,发现是”fc ff ff ff“是call指令的操作数。

00000000 <func1>:0:   55                      push   %ebp1:   89 e5                   mov    %esp,%ebp3:   83 ec 08                sub    $0x8,%esp6:   83 ec 08                sub    $0x8,%esp9:   ff 75 08                pushl  0x8(%ebp)c:   68 00 00 00 00          push   $0x011:   e8 fc ff ff ff          call   12 <func1+0x12>16:   83 c4 10                add    $0x10,%esp19:   90                      nop1a:   c9                      leave  1b:   c3                      ret 

也就是说,在没有重定位前call指令的操作”fc ff ff ff“是无效的,需要在重定位过程中进行修正。func1那行也同理。

总结

ELF文件结构可以用下面的图表示:

可执行程序结构

和未链接的ELF文件结构一样,只不过引入了Segment的概念(注意和Section进行区分)。Segment本质上是从装载的角度重新划分了ELF的各个段。目标文件链接成可执行文件时,链接器会尽可能把相同权限属性的段(Section)分配到同一Segment。Segment结构的起始位置,项数,大小分别由ELF头中的Size of program headers,Number of program headers, Size of this header字段指定。

参考资料:

  1. 《程序员的自我修养》第3,6章
  2. ELF结构文档

posted @ 2018-10-07 15:12  gatsby123  阅读(...)  评论(...)  编辑  收藏

刷新评论刷新页面返回顶部

 

 

 

 

 

 

Copyright © 2020 gatsby123
Powered by .NET Core on Kubernetes

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

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

相关文章

TCP协议的三次握手与四次挥手

tcp协议的三次握手和四次挥手 三次握手&#xff1a; 第一次握手&#xff1a; 客户端发起一个链接&#xff08;SYN&#xff09; 第二次握手&#xff1a; 服务端就会返回一条&#xff08;ACK&#xff09;确认信息&#xff0c;同时服务端也会向客户端发起一个链接请求&#xff08;…

晚上美容护肤10要诀 - 健康程序员,至尚生活!

夜晚美容护肤十要诀  如何护肤&#xff0c;我想谁也知道晚上是给予皮肤滋养的最佳时期&#xff0c;别以为早晚搽日霜、晚霜就能够令你的皮肤变得白雪雪、滑嘟嘟&#xff0c;其实这只是表面的滋润。要彻底护理&#xff0c;就要依赖你睡前的准备功夫&#xff0c;才能使肌肤得到…

明白了为什么java方法上面为什么要加个@符号

原来是java的特性“注解&#xff08;Annotation&#xff09;” 注解&#xff08;Annotation&#xff09; 为我们在代码中天界信息提供了一种形式化的方法&#xff0c;是我们可以在稍后某个时刻方便地使用这些数据&#xff08;通过 解析注解 来使用这些数据&#xff09; 注解的语…

PG内核分析 QA

今天查找postgreSQL相关的资料时&#xff0c;查找到该文章&#xff0c;觉得写得比较好 https://www.cnblogs.com/liuhaifeng/p/12184902.html

STM32的PA15、PB3、 PB4管脚作普通管脚的解决办法

最近做了一个板子&#xff0c;使用的是SWD方式进行下载程序&#xff0c;仅仅使用到SWDIO&#xff08;PA13&#xff09; 和SWCLK&#xff08;PA14&#xff09;两个管脚。我将PA15&#xff08;JTDI&#xff09;和PB3&#xff08;JTDO&#xff09;管脚用于他用&#xff08;用于点L…

Google不要走

这两天Google离开中国的新闻不断&#xff0c;如果他真的要离开&#xff0c;我会觉得非常遗憾。我想他已经是大部分程序员的精神领袖&#xff0c;技术牛人的代表&#xff0c;现在居然会被逼走&#xff1f;&#xff1f; 与百度的搜索相比&#xff0c;Google也有广告&#xff0c;但…

AIX errdemon 命令

errdemon 进程持续检查 /dev/error 文件,收集error信息。 命令格式&#xff1a; Usage: errdemon -i filename -s value -B value [ -d | -D -m maxdups -t time ] | -l -i filename Uses the error log file specified by the filename parameter. If this f…

C#集合。

集合命名空间&#xff1a; using system.collections. 非泛型集合 using system.collections.Generic. 泛型集合 为什么要用集合&#xff1a; 1、数组一旦声明长度就固定了。 2、集合有很多方法可以用 等 常用集合&#xff1a; 类似数组集合&#xff1a;ArrayList List<&g…

对std::listT的封装

由于工作的原因&#xff0c;需要在线程安全的情况下对std::list<T>进行该list进行访问&#xff0c;因此就简单的封装了下&#xff0c;代码如下&#xff1a; template<typename T> class List { public:List () {}~List () {}void PushBack(const T& value) {b…

code

// 控制字体系列。"editor.fontFamily": "Droid Sans Mono, monospace, monospace, Droid Sans Fallback",// 覆盖当前所选颜色主题中的编辑器颜色和字体样式。"editor.tokenColorCustomizations": {},// 控制差异编辑器是否将对前导空格或尾随空…

SWISHMAX2脚本整理及Swishmax使用技巧

wish Max让你更快速更简单地在你的网页中加入Flash动画,超过230种可选择的预设效果.SWiSH是一个快速、简单且经济的方案,让你可以在你的网页 中加入Flash动画.只要点几下鼠标,你就可以加入让你的网页在众多网站中令人注目的酷炫动画效果.你可以创造形状、文字、按钮以及移动路径…

MAC机路由管理

On the Mac the command is similar, but a bit different Just as a note to myself and anyone else interested:add network:1sudo route add -net 10.67.0.0/16 192.168.120.254Add hostsudo route add -host 10.67.0.0/16 192.168.120.254转载于:https://blog.51cto.com…

Redis的持久化机制

Redis 的数据全部在内存里&#xff0c;如果突然宕机&#xff0c;数据就会全部丢失&#xff0c;因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失&#xff0c;这种机制就是 Redis 的持久化机制。 Redis 的持久化机制有两种&#xff0c;第一种是RDB快照&#xff0c;第二…

今天用python的turtle简单画了一副眼镜

画的不太好看&#xff0c;下次要继续努力鸭!!! 这个是代码~ 1 from turtle import*2 pencolor("blue")3 fillcolor("white")4 setup(500,1000,100,10)5 speed(2)6 pensize(4)7 begin_fill()8 circle(40,450)9 goto(70,40) 10 circle(-40,360) 11 penup() 1…

怎么样能找到国外的群?

怎么样能找到国外的群和老外聊天,学习呢&#xff1f;给你条途径。 先申请个MSN&#xff0c;作为固定的聊天基地。 在去WWW.OICQ.COM。 这是个外国的聊天网址&#xff0c;去那边的聊天室&#xff08;临时聊天基地&#xff0c;用来找新网友&#xff09;聊天。若是有个谈的来的&am…

redis RDB持久化中save和bgsave区别

SAVE 和 BGSAVE 两个命令都会调用 rdbSave 函数&#xff0c;但它们调用的方式各有不同&#xff1a; SAVE 直接调用 rdbSave &#xff0c;阻塞 Redis 主进程&#xff0c;直到保存完成为止。在主进程阻塞期间&#xff0c;服务器不能处理客户端的任何请求。BGSAVE 则 fork 出一个…

OA系统

employee对象为空&#xff0c;所以mybatis无法获取employeeId的值 getProperty(null, "employeeId") null是employee对象&#xff0c;employeeId是employee对象的属性 Struts Problem Report Struts has detected an unhandled exception: Messages: source is null …

合并所有文档

合并所有文档type *.txt >index.txt #将所有.txt文件内容合并到index.txt文件中 转载于:https://www.cnblogs.com/juan-F/p/10532455.html

memmove()/mmecpy()

今天用到了memcpy()库函数&#xff0c;然后自己实现了一个结合源码应该是这样的 ​ void *memmove(void *dest, const void *src, size_t count) {assert((NULL ! dest) && (NULL ! src) && (count > 0));char *pdest (char *)dest;const char *psrc (co…

ADHD-注意力缺陷多动症

【以下内容转自Wiki】 ADHD&#xff1a;Attention deficit-hyperactivity disorder ADHD的主要病征是&#xff1a; 注意力散涣&#xff08;inattentive&#xff09;或 集中困难&#xff08;Attention-deficit&#xff09;活动量过多&#xff08;hyperactive或hyperkinetic&…