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

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

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

整体的结构如下图:

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

原文:https://blog.csdn.net/jltxgcy/article/details/39233689

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

  1. linux运行vb程序,Linux可执行文件ELF结构及程序加载运行

    Linux下ELF文件类型分为以下几种: 1.可重定位文件,例如SimpleSection.o: 2.可执行文件,例如/bin/bash: 3.共享目标文件,例如/lib/libc.so. 在Linu ...

  2. Linux下编译、链接、加载运行C++ OpenCV的两种方式及常见问题的解决

    Linux下编译.链接.加载运行C++ OpenCV的两种方式及常见问题的解决 在Linux下安装完OpenCV C++之后(还没有安装的读者请参考Ubuntu 18.04 安装OpenCV C++) ...

  3. Linux用户程序的编译链接与加载启动过程

    Linux用户程序的编译链接与加载启动过程 rtoax 2021年3月 1. 程序的编译链接 1.1. 介绍 如果我们打开维基百科的 链接器 页,我们将会看到如下定义: 在计算机科学中,链接器(英文: ...

  4. contiki学习笔记(六)contiki程序加载器和多线程库

    六.contiki程序加载器 contiki程序加载器是一个用于加载和启动程序的抽象接口. Data Structures struct dsc//DSC程序描述结构. ModulesThe Cont ...

  5. 【学习分享】2、创龙 TMS320C6748开发板程序加载和烧写(四)

    此部分由于篇幅过长,将分为五个小点进行叙述,此处主讲基于SD卡烧写程序到NAND FLASH,所用器件为创龙TMS320C6748开发板.需要注意的点已在备注中进行说明. 如果需要从头开始进行本使用手 ...

  6. 借由ARM CORTEX-M芯片分析C程序加载和存储模型

    https://zhuanlan.zhihu.com/p/22048373 写文章 借由ARM CORTEX-M芯片分析C程序加载和存储模型 王小军 1 年前 阿军最近在忙着血氧手环嵌入式系统的技术预 ...

  7. 【OS学习笔记】十 实模式:实现一个程序加载器-程序加载器如何将用户程序加载到内存并执行

    上一篇文章学习了以下内容: 用一种不同的分段方法,从另一个不同的的角度理解处理器的分段内存访问机制 使用循环和条件转移指令来优化主引导扇区代码 点击链接查看上一篇文章:点击链接查看 对于主引导扇区部分 ...

  8. linux内核启动文件系统,Linux启动过程中文件系统的加载

    前言:我觉得我的文章相对来说都是比较浅显的.一些初学者可以看看,这也是我不在嵌入式那个版上发文的原因.对于高手来说,如果你们不吝啬时间的话,希望也能帮我看看,指点一下其中的错误.这也是我到这里来和大家 ...

  9. Linux笔记整理(1)系统的加载和main函数执行准备

    <Linux内核设计的艺术>笔记 基于linux0.11,主要研究原理,对自己不清楚的地方会有一点个人补充,偶尔会穿插其他版本的对比. 内核版本和发行版本 linux内核和发行版不是一个概 ...

最新文章

  1. python赋值语句的一般格式为_Python 基础语法
  2. 计算机英语protocols,计算机网络协议词汇Protocols
  3. VC++实现Turbo码
  4. loj10165. 「一本通 5.3 例 3」Windy 数
  5. 检测客户pc电脑端VC++环境并安装
  6. AgileEAS.NET SOA 中间件平台.Net Socket通信框架-简单例子-实现简单的服务端客户端消息应答...
  7. win定时关机_电脑定时关机,你造吗?
  8. Chapter 5 : 索引和算法
  9. 计算机二级机试题型,计算机二级机试题库
  10. 数学建模 —— 自回归模型
  11. 偏差、方差、标准差、协方差
  12. MIPI CSI、DSI、UFS、C-PHY、D-PHY、M-PHY概念理解
  13. SQP(序列二次规划中的Marotos效应)
  14. java170道面试题汇总+详细解析
  15. 个体工商户营业执照在网上如何年检?
  16. C#软件绑定QQ群验证代码
  17. h5微信本地调试 vue_UniApp本地调试H5(谷歌chrome浏览器)跨域问题的解决方法,亲测可用...
  18. 央视解说之韩乔生巅峰之作--夏普
  19. linux如何删除ntfs分区,Ubuntu中增添NTFS分区的方法
  20. 论程序员怎么拥有咪蒙的文笔

热门文章

  1. JS ES6中的箭头函数(Arrow Functions)使用
  2. CSS块元素水平垂直居中的实现技巧
  3. laravel中的自定义函数的加载和第三方扩展库加载
  4. js 计算对象数组中某个属性值重复出现的个数
  5. 为什么dubbo的调用重试不建议设置成超过1
  6. oracle安装中桌面模式与服务器模式的去别
  7. serialVersionUID的作用以及如何用idea自动生成实体类的serialVersionUID
  8. python学习笔记(python介绍)
  9. Javascript常用的设计模式详解
  10. easyui plugin——etreegrid:CRUD Treegrid