Linux 可执行文件 ELF结构 及程序加载运行

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,具体原因以后再分析。

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

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

整体的结构如下图:

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

时间: 2024-10-11 15:24:30

Linux 可执行文件 ELF结构 及程序加载运行的相关文章

32位汇编第二讲,编写窗口程序,加载资源,响应消息,以及调用C库函数

32位汇编第二讲,编写窗口程序,加载资源,响应消息,以及调用C库函数 (如果想看所有代码,请下载课堂资料,里面有所有代码,这里会讲解怎么生成一个窗口程序) 一丶32位汇编编写Windows窗口程序 首先我们知道32位汇编是可以调用Windows API的,那么今天我们就调用windowsAPI来写一个窗口程序 如果你有windows开发知识,那么就很理解了,如果没有,那么跟着我写,跟着步骤去写,那么也可以写出来 首先我们要编写一个窗口程序(使用SDKAPI编写)有几个步骤 1.设计窗口类 2.注

[翻译]-Linux上C++类的动态加载

摘要:本文是翻译文章,主要介绍了运行时重载C++类的技术,包括了Linux上的动态加载接口.C++类的动态加载技术点及实现.自动加载技术等.最后给出了两个应用案例及相关的源代码.   关键字:动态加载,C++类,Linux 原文链接:http://porky.linuxjournal.com:8080/LJ/073/3687.html   推荐: (原文)http://www.tldp.org/HOWTO/text/C++-dlopen (翻译)http://hi.baidu.com/clive

Linux下c函数dlopen实现加载动态库so文件代码举例

dlopen()是一个强大的库函数.该函数将打开一个新库,并把它装入内存.该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的.这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了.可以在自己的程序中使用 dlopen().dlopen() 在 dlfcn.h 中定义,并在 dl 库中实现.它需要两个参数:一个文件名和一个标志.文件名就是一个动态库so文件,标志指明是否立刻计算库的依赖性.如果设置为 RTLD_NOW 的话,则立刻计算:如果设置的是 RTLD_LAZY,则在需要

小白学phoneGap《构建跨平台APP:phoneGap移动应用实战》连载四(使用程序加载事件)

在了解了PhoneGap中都有哪些事件之后,本节将开始对这些事件的用法进行详细地介绍.本节要介绍的是程序加载事件,也就是deviceready.pause和resume这3个事件. [范例4-2 程序加载事件的使用] 01 <!DOCTYPE html> 02 <html> 03 <head> 04 <meta charset="utf-8"> 05 <title>程序加载事件的使用</title> 06 <

MFC程序加载DLL文件时运行到Loadlibray()语句就无法继续下去

我在学习Detour时,为了进行测试,就建立了一个DLL文件和一个MFC工程. 但是我的MFC程序加载DLL文件时运行到Loadlibray("XXX.dll")这条语句就无法继续下去,没有报错,VS2010也没有停止,还是处于编译运行状态. 可是我之前加载的别的DLL文件都可以运行,这应该是很简单的加载DLL文件的过程呀,就想是dll文件的代码错误?或是MFC工程错误?还是Dll工程错误? 于是 我就将dll内的代码 复制到原来可以运行的DLL文件中发现可以运行,那就是代码没错, 接

使用SplashScreenManager控件定制程序加载页面

需要devexpress版本在12.0及以上才支持 https://www.cnblogs.com/wuhuacong/p/6112461.html 在DevExpress程序中使用SplashScreenManager控件实现启动闪屏和等待信息窗口 http://blog.csdn.net/archielau/article/details/37401443 ProgressPanel.WaitForm.SplashScreenManager http://blog.csdn.net/qq99

刚部署的程序加载不出来css,js以及图片

刚部署的程序加载不出来css,js以及图片,解决方式 需要在配置中加入静态资源 方法一: controller.xml中加入 <mvc:annotation-driven/> <mvc:resources location="/css/" mapping="/css/**"/> <mvc:resources location="/js/" mapping="/js/**"/> <mvc

无法为具有固定名称“System.Data.SqlClient”的 ADO.NET 提供程序加载在应用程序配置文件中注册的实体框架提供程序类型“System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer”。请确保使用限定程序集的名称且该程序集对运行的应用程序可用。有关详细信息,请参阅 http://go.m

Windows服务中程序发布之后会如下错误: 无法为具有固定名称"System.Data.SqlClient"的 ADO.NET 提供程序加载在应用程序配置文件中注册的实体框架提供程序类型"System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer".请确保使用限定程序集的名称且该程序集对运行的应用程序可用.有关详细信息,请参阅 http://go.microsoft.com

部署到iis后,发现无法加载运行CSS文件

解决方法: 打开或关闭window功能中的Internet信息服务里的万维网服务=>常见HTTP功能=>静态内容 部署到iis后,发现无法加载运行CSS文件,码迷,mamicode.com