zoukankan      html  css  js  c++  java
  • 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。详细原因以后再分析。

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

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

        总体的结构例如以下图:



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

  • 相关阅读:
    asp.net core 不依赖autofac实现aop
    C# 获取调用者信息
    IIS 命令学习
    Jenkins + PowerShell + .net core 自动打包
    gogs 自定义配置
    搜索文件内容神器:SearchMyFiles
    非常强大的磁盘计算大小工具:TreeSizeFree
    rancher入门教程
    IQueryable 表达式解析学习
    Sql server 入门权威指南
  • 原文地址:https://www.cnblogs.com/blfshiye/p/5275294.html
Copyright © 2011-2022 走看看