Linux下ELF文件类型分为以下几种:

1、可重定位文件,比如SimpleSection.o;

2、可运行文件,比如/bin/bash。

3、共享目标文件,比如/lib/libc.so。

Linux 可重定位文件 ELF结构一文中,我们已经分析了可重定位文件ELF结构。

本文分析可运行文件的ELF结构。

首先附上源码:

SectionMapping.c

#include <stdlib.h>

int main()
{
while(1)
{
sleep(1000);
}
return 0;
}

使用命令gcc -static SectionMapping.c -o SectionMapping.elf。静态链接为可运行文件。

接着使用命令readelf -S SectionMapping.elf得到Section Table。例如以下:

There are 33 section headers, starting at offset 0xc3878:

Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.ABI-tag NOTE 0000000000400190 00000190
0000000000000020 0000000000000000 A 0 0 4
[ 2] .note.gnu.build-i NOTE 00000000004001b0 000001b0
0000000000000024 0000000000000000 A 0 0 4
[ 3] .rela.plt RELA 00000000004001d8 000001d8
0000000000000120 0000000000000018 A 0 5 8
[ 4] .init PROGBITS 00000000004002f8 000002f8
0000000000000018 0000000000000000 AX 0 0 4
[ 5] .plt PROGBITS 0000000000400310 00000310
00000000000000c0 0000000000000000 AX 0 0 16
[ 6] .text PROGBITS 00000000004003d0 000003d0
0000000000094988 0000000000000000 AX 0 0 16
[ 7] __libc_thread_fre PROGBITS 0000000000494d60 00094d60
00000000000000a8 0000000000000000 AX 0 0 16
[ 8] __libc_freeres_fn PROGBITS 0000000000494e10 00094e10
000000000000181c 0000000000000000 AX 0 0 16
[ 9] .fini PROGBITS 000000000049662c 0009662c
000000000000000e 0000000000000000 AX 0 0 4
[10] .rodata PROGBITS 0000000000496640 00096640
000000000001d344 0000000000000000 A 0 0 32
[11] __libc_thread_sub PROGBITS 00000000004b3988 000b3988
0000000000000008 0000000000000000 A 0 0 8
[12] __libc_subfreeres PROGBITS 00000000004b3990 000b3990
0000000000000058 0000000000000000 A 0 0 8
[13] __libc_atexit PROGBITS 00000000004b39e8 000b39e8
0000000000000008 0000000000000000 A 0 0 8
[14] .eh_frame PROGBITS 00000000004b39f0 000b39f0
000000000000d4c4 0000000000000000 A 0 0 8
[15] .gcc_except_table PROGBITS 00000000004c0eb4 000c0eb4
0000000000000172 0000000000000000 A 0 0 1
[16] .tdata PROGBITS 00000000006c1ef0 000c1ef0
0000000000000020 0000000000000000 WAT 0 0 16
[17] .tbss NOBITS 00000000006c1f10 000c1f10
0000000000000038 0000000000000000 WAT 0 0 16
[18] .init_array INIT_ARRAY 00000000006c1f10 000c1f10
0000000000000008 0000000000000000 WA 0 0 8
[19] .fini_array FINI_ARRAY 00000000006c1f18 000c1f18
0000000000000008 0000000000000000 WA 0 0 8
[20] .ctors PROGBITS 00000000006c1f20 000c1f20
0000000000000010 0000000000000000 WA 0 0 8
[21] .dtors PROGBITS 00000000006c1f30 000c1f30
0000000000000010 0000000000000000 WA 0 0 8
[22] .jcr PROGBITS 00000000006c1f40 000c1f40
0000000000000008 0000000000000000 WA 0 0 8
[23] .data.rel.ro PROGBITS 00000000006c1f50 000c1f50
0000000000000080 0000000000000000 WA 0 0 16
[24] .got PROGBITS 00000000006c1fd0 000c1fd0
0000000000000010 0000000000000008 WA 0 0 8
[25] .got.plt PROGBITS 00000000006c1fe8 000c1fe8
0000000000000078 0000000000000008 WA 0 0 8
[26] .data PROGBITS 00000000006c2060 000c2060
0000000000001690 0000000000000000 WA 0 0 32
[27] .bss NOBITS 00000000006c3700 000c36f0
0000000000002ba8 0000000000000000 WA 0 0 32
[28] __libc_freeres_pt NOBITS 00000000006c62b0 000c36f0
0000000000000048 0000000000000000 WA 0 0 16
[29] .comment PROGBITS 0000000000000000 000c36f0
000000000000002a 0000000000000001 MS 0 0 1
[30] .shstrtab STRTAB 0000000000000000 000c371a
000000000000015b 0000000000000000 0 0 1
[31] .symtab SYMTAB 0000000000000000 000c40b8
000000000000c168 0000000000000018 32 870 8
[32] .strtab STRTAB 0000000000000000 000d0220
0000000000007a26 0000000000000000 0 0 1

表 1

这个可运行文件共同拥有33个Section。

接着我们使用readelf -h SectionMapping.elf。读取elf可运行文件头部信息。

例如以下图:

图 1

能够对照,Linux 可重定位文件 ELF结构,这里多了program header。

Entry point address:程序的入口地址是0x401058,使用objdump -d SectionMapping.elf | less,能够查看到程序的入口地址是<_start>。

例如以下图:

图 2

Start of program headers:program headers的偏移。由于头文件大小为64,所以program headers紧挨着头文件存放。

Size of program headers:program headers的大小。为56个字节。

Number of section headers:program headers的数量。

为6个。

在表1中。第一个section在文件里的偏移是0x190。头文件大小为64 + program header大小为56 * program header数量6 = 400 = 0x190。

然后,我们使用命令readelf -l SectionMapping.elf。我们会得到program header部分。例如以下图:

图  3

从图中可见,分为6个Segment。

注意表1中每一个段叫Section。

Offset:这个Segment在文件里偏移。

VirtAddr:这个Segment在虚拟地址的偏移。

FileSiz:在ELF文件里所占的长度。

MemSiz:在进程虚拟空间所占的长度。

我们发现第二个Segment,MemSiz > FileSiz,表示在内存中分配的空间大小超过文件实际大小。

超过的部分所有初始化为0。作为BSS段。由于数据段和BSS段的唯一差别是,数据段从文件里初始化内容,BSS段内容所有初始化为0。

我们主要关心前两个Segment。第一个是代码段,虚拟地址从0x00400000到0x004c1026。文件偏移从0x00000000到0x000c1026。

第二个是数据段。虚拟地址为从0x006c1ef0到0x006c1ef0+0x4408=0x6c62f8。

文件偏移从0x000c1ef0到0x000c1ef0+0x1800=0x000C36f0。

结合表1和两个Segment的文件偏移。能够得出:

第一个Segment从第0个Section到第15个Section。(0x00000000-0x000c1026)

  [Nr] Name              Type             Address           Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .note.ABI-tag NOTE 0000000000400190 00000190
0000000000000020 0000000000000000 A 0 0 4
[ 2] .note.gnu.build-i NOTE 00000000004001b0 000001b0
0000000000000024 0000000000000000 A 0 0 4
[ 3] .rela.plt RELA 00000000004001d8 000001d8
0000000000000120 0000000000000018 A 0 5 8
[ 4] .init PROGBITS 00000000004002f8 000002f8
0000000000000018 0000000000000000 AX 0 0 4
[ 5] .plt PROGBITS 0000000000400310 00000310
00000000000000c0 0000000000000000 AX 0 0 16
[ 6] .text PROGBITS 00000000004003d0 000003d0
0000000000094988 0000000000000000 AX 0 0 16
[ 7] __libc_thread_fre PROGBITS 0000000000494d60 00094d60
00000000000000a8 0000000000000000 AX 0 0 16
[ 8] __libc_freeres_fn PROGBITS 0000000000494e10 00094e10
000000000000181c 0000000000000000 AX 0 0 16
[ 9] .fini PROGBITS 000000000049662c 0009662c
000000000000000e 0000000000000000 AX 0 0 4
[10] .rodata PROGBITS 0000000000496640 00096640
000000000001d344 0000000000000000 A 0 0 32
[11] __libc_thread_sub PROGBITS 00000000004b3988 000b3988
0000000000000008 0000000000000000 A 0 0 8
[12] __libc_subfreeres PROGBITS 00000000004b3990 000b3990
0000000000000058 0000000000000000 A 0 0 8
[13] __libc_atexit PROGBITS 00000000004b39e8 000b39e8
0000000000000008 0000000000000000 A 0 0 8
[14] .eh_frame PROGBITS 00000000004b39f0 000b39f0
000000000000d4c4 0000000000000000 A 0 0 8
[15] .gcc_except_table PROGBITS 00000000004c0eb4 000c0eb4
0000000000000172 0000000000000000 A 0 0 1

第二个Segment从第16个Section到26个Section。

(0x000c1ef0-0x000C36f0)

  [16] .tdata            PROGBITS         00000000006c1ef0  000c1ef0
0000000000000020 0000000000000000 WAT 0 0 16
[17] .tbss NOBITS 00000000006c1f10 000c1f10
0000000000000038 0000000000000000 WAT 0 0 16
[18] .init_array INIT_ARRAY 00000000006c1f10 000c1f10
0000000000000008 0000000000000000 WA 0 0 8
[19] .fini_array FINI_ARRAY 00000000006c1f18 000c1f18
0000000000000008 0000000000000000 WA 0 0 8
[20] .ctors PROGBITS 00000000006c1f20 000c1f20
0000000000000010 0000000000000000 WA 0 0 8
[21] .dtors PROGBITS 00000000006c1f30 000c1f30
0000000000000010 0000000000000000 WA 0 0 8
[22] .jcr PROGBITS 00000000006c1f40 000c1f40
0000000000000008 0000000000000000 WA 0 0 8
[23] .data.rel.ro PROGBITS 00000000006c1f50 000c1f50
0000000000000080 0000000000000000 WA 0 0 16
[24] .got PROGBITS 00000000006c1fd0 000c1fd0
0000000000000010 0000000000000008 WA 0 0 8
[25] .got.plt PROGBITS 00000000006c1fe8 000c1fe8
0000000000000078 0000000000000008 WA 0 0 8
[26] .data PROGBITS 00000000006c2060 000c2060
0000000000001690 0000000000000000 WA 0 0 32

以上分析的都是静态状态下的程序,以下我们看看动态下的进程的空间是怎么分配的。

首先使用命令, ./SectionMapping.elf &,输出例如以下:

然后使用命令:cat /proc/2184/maps,输出例如以下:

图 4

静态时。我们计算出的两个Segment的虚拟空间的偏移分别为:

第一个是代码段。虚拟地址从0x00400000到0x004c1026。

在图4中,由于要页面对齐,所以分配了0x400000到0x4c2000。

第二个是数据段,虚拟地址为从0x006c1ef0到0x006c1ef0+0x4408=0x6c62f8。在图4中。由于要页面对齐,所以分配了0x6c1000到0x6c4000。注意。0x6c62f8大于0x6c4000。详细原因以后再分析。

第三个紧接着是堆。用于动态分配内存。

第四个是栈。用于存放局部变量。

总体的结构例如以下图:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamx0eGdjeQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

程序运行的过程:建立虚拟空间(分配一个页文件夹)-> 建立虚拟空间与可运行文件映射(页文件夹项指向磁盘的程序) -> 跳到程序入口 -> 缺页异常-> 在内存中寻找空暇页。将相应的页换入 -> 建立映射 -> 開始运行。

Linux 可执行文件 ELF结构 及程序载入执行的更多相关文章

  1. Android开发第一讲之目录结构和程序的执行流程

    1.如何在eclipse当中,修改字体 下面的这种办法,可以更改xml的字体 窗口--首选项--常规--外观--颜色和字体--基本--文本字体--编辑Window --> Preferences ...

  2. Linux操作系统中/sbin/init程序的执行过程

    当init启动后,它通过执行各种启动事务来继续引导进程(检查并监视文件系统,启动后台程序daemons,等等),直至完成用户所有操作环境的设置工作.这里主要涉及4个程序:init.getty(aget ...

  3. Linux shell编程02 shell程序的执行 及文件权限

    第一个shell脚本 1.       shell编程的方式 交互式shell编程 非交互式shell编程:执行的语句存放到一个文件 shell脚本:可以任意文件名,建议扩展名为sh 2.       ...

  4. Linux可执行文件格式-ELF结构详解

    表1. ELF文件类型分类 ELF文件类型 说明 实例 Relocatable File 可重定位文件 未链接之前的ELF文件,可用于链接可执行文件或静态链接库 Linux下的".o&quo ...

  5. [转]linux,windows 可执行文件(ELF、PE)

    ELF (Executable Linkable Format)UNIX类操作系统中普遍采用的目标文件格式 . 首先要知道它有什么作用:工具接口标准委员会TIS已经将ELF作为运行在Intel32位架 ...

  6. linux上应用程序的执行机制

    linux上应用程序的执行机制 执行文件是如何在shell中被"执行"的.本文中尽可能少用一些源码,免得太过于无 聊,主要讲清这个过程,感兴趣的同学可以去查看相应的源码了解更多的信 ...

  7. linux,windows 可执行文件(ELF、PE)

    现在PC平台流行的可执行文件格式(Executable)主要是Windows下的PE(Portable Executable)和Linux的ELF(Executable Linkable Format ...

  8. 可执行文件(ELF)格式之讲解

    ELF(Executable and Linking Format)是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中都放了什么东西.以及都以什么样的格式去放这些东西.它自 ...

  9. Linux之ELF文件初探

    对比windowsPE文件与概述 在windows中可执行文件是pe文件格式,Linux中可执行文件是ELF文件,其文件格式是ELF文件格式,在Linux下的ELF文件除了可执行文件(Excutabl ...

随机推荐

  1. C++模板详解

    参考:C++ 模板详解(一) 模板:对类型进行参数化的工具:通常有两种形式: 函数模板:仅参数类型不同: 类模板:   仅数据成员和成员函数类型不同. 目的:让程序员编写与类型无关的代码. 注意:模板 ...

  2. 前端架构:Angular与requirejs集成实践

    这几天angular与requirejs.browserify的集成弄的博主头好晕,今天终于成功集成了requirejs,现写些心得体会在这里. 核心思想:angular加载时有一定的顺序,必须依次加 ...

  3. 事务处理: databse jdbc mybatis spring

    事务的认识需要一个相当漫长的流程,慢慢在实践中理解,然后在强化相关理论基础. 数据库中的事务: 传统的本地事务处理都是依靠数据库自身事务处理能力,而事务本身是传统关系型数据库的基石.简单来说事务就是一 ...

  4. Python:数字

    一.数字简介 数字可以直接访问,是不可更改并且不可分割的原子类型,这些在标准类型的分类中都谈到了.不可更改意味着变更数字值的实质是新对象的创建.当然,这些对于程序员来说都是透明的,不需过多考虑. 1. ...

  5. 【数据结构与算法分析——C语言描述】第二章总结 算法分析

    算法 算法(algorithm)是为求解一个问题需要遵循的.被清楚地指定的简单指令的集合. 数学基础 四个定义: 1.大O表示法: 如果存在正常数 c 和 n0 使得当 N ≥ n0时,T(N) ≤ ...

  6. 怎么监视跟踪一个进程(Process)中的MS Unit Test DLL的详细性能(performance)【asp.net C#】

    Sample This tutorial will show how to instrument a unit test DLL for performance profiling. Visual S ...

  7. 第二百二十六天 how can I 坚持

    今天弟弟生日,只是简单的说了句生日快乐,幸亏看了下日历,要不又忘了. 在家待了一天. 明天还想去爬山,八大处太远了,该去哪呢. 不想在家待着. 日复一日,啊,年复一年啊.想想好可怕,人生,太快.该如何 ...

  8. 使用CXF发布WebService服务简单实例

    一.说明: 前面介绍了使用axis2来发布Webservice服务,现在介绍一种更popular,更高效的Webservice服务发布技术:CXF Apache CXF = Celtix + XFir ...

  9. STM32中的位带(bit-band)操作

    转:http://blog.csdn.net/gaojinshan/article/details/11479929 //位带操作,实现51类似的GPIO控制功能 //具体实现思想,参考<< ...

  10. TPARAMS和OLEVARIANT相互转换

    所谓的“真3层”有时候是需要客户端上传数据集的TPARAMS到中间件的. 现在,高版本的DATASNAP的远程方法其实也是直接可以传输TPARAMS类型的变量,但是DELPHI7(七爷).六爷它们是不 ...