精华内容
下载资源
问答
  • ELF

    2020-04-07 11:43:54
    xxd + ....o 52byte ELFheader http://www.skyfree.org/linux/references/ELF_Format.pdf https://zhuanlan.zhihu.com/p/36887189
    展开全文
  • ELF format / ELF文件格式

    2021-03-16 13:48:38
    2003/09/25 11:50 152,347 ELF_Format.pdf 2003/09/25 11:49 190,796 ELF_Programmer.pdf 2004/10/30 10:37 20,198 ELF动态解析符号过程(修订版).txt 2003/06/11 15:14 28,626 ELF文件格式(中文)(一) .txt 2003/09/...
  • ELF视频教程

    2021-02-21 16:49:21
    本视频视频教程主要讲的是ELF文件格式相关,内容涉及到ELF文件格式解析,ELF文件注入与hook,ELF文件注入shellcode,ELF文件加固加壳,内容丰富。
  • elf101(ELF格式)

    2018-09-23 14:48:44
    elf101,资料收集自互联网,通过一张图展示ELF格式,是学习ELF格式的好工具
  • Understanding ELF

    2015-08-18 10:23:55
    Understanding ELF 介绍ELF格式
  • elf_parse解析工具

    2017-07-26 21:06:32
    elf
  • ELF中文手册: 可执行连接格式(Executable and Linking Format)最初是作为应用程序二进制接口(Application Binary Interface(ABI)的一部分被UNIX系统实验室(USL)开发和发布。工具接口标准委员会(TIS)将还在发展的ELF...
  • elf文件加载

    2018-07-18 10:42:12
    elf文件加载 elf文件加载 elf文件加载 elf文件加载 elf文件加载
  • Elf文件格式

    2020-09-24 10:01:13
    Elf文件最详细的介绍。也是最好的一个介绍elf文件格式的资料。非常棒 Elf文件最详细的介绍。也是最好的一个介绍elf文件格式的资料。非常棒
  • ELF,ELFELFELFELF

    2018-12-19 15:12:48
    ELF详解
  • ELF文件类型 首先ELF文件可以被标记为以下几个类型: ET_NONE:未知类型。 ET_REL:重定位文件,类型标记为relocatable意为着该文件被标记为了一段可重定位的代码段,有时也称为目标文件。可重定位目标文件通常是还...

    ELF文件类型

    首先ELF文件可以被标记为以下几个类型:

    • ET_NONE:未知类型。
    • ET_REL:重定位文件,类型标记为relocatable意为着该文件被标记为了一段可重定位的代码段,有时也称为目标文件。可重定位目标文件通常是还未被链接到可执行程序的一段位置独立的代码。编译完是.o的格式。
    • ET_EXEC:可执行文件,类型为executable,表明这个文件被标记为可执行文件。这种类型被称为程序,是一个进程开始的入口。
    • ET_DYN:共享目标文件。类型为dynamic,以为着该文件被标记为了一个动态的可链接的目标文件,也称为共享库。这类共享库会在程序运行时被装载链接到程序的进程镜像中。
    • ET_CORE:核心文件。载程序崩溃或者进程传递了一个SIGSEGV信号(分段违规)时,会在核心文件中记录整个进程的镜像信息。可以使用GDB读取这类文件来辅助调试并查找程序崩溃的原因。

    用到的一些命令

    readelf -h 查看ELF文件,可以看到原始的ELF文件头。如下所示
    readelf -h
    ELF文件头从文件0偏移量开始,除了文件头之后剩余部分的一个映射。记录了ELF类型,结构,和程序开始执行的入口地址并提供其他ELF(节头和程序头偏移量)。

    程序头

    ELF程序头是对二进制文件中段的描述,是程序装载必需的部分。段载内核装载时被解析。描述了磁盘上可执行文件的内存布局,以及如何映射到内存中,可以通过引用原始ELF头中名为e_phoff(程序头表偏移量)的偏移量来得到程序头表。

    常见有5种程序头类型:

    PT_LOAD,一个可执行文件只是一个PT_LOAD类型的段。这类程序头描述的时可装载的段。一般一个需要动态链接的ELF可执行文件通常由两个可装载的段:存放程序代码的text段,存放全局变量和动态链接的data段。
    上面两个段会被映射到内存,根据p_align中存放的值在内存中对齐。程序头主要描述程序执行时在内存中的布局。
    PT_DYNAMIC,动态段时动态链接可执行文件持有的,包含动态链接器所必有的一些信息,包含了一些标记值和指针,比如运行是需要链接的共享库列表,全局偏移表,重定位条目的相关信息。
    PT_NOTE,保存了与特定供应商或者系统相关的附加信息。
    PT_INTERP,将信息二号位置存放在一个NULL为终止符的字符串中,对程序解释器位置的描述。
    PT_PHDR,段保存了程序头标本身的位置和大小。Phdr表保存了所有的Phdr对文件(以及内存镜像)中段的描述信息。

    用到的一些命令

    readelf -l <文件名>命令查看文件的Phdr表:

    readelf -l

    ELF节头

    段是程序执行的必要组成部分,每个段中,会有代码或者数据被划分为不同的节,节头表是对这些节位置和大小的描述。用于链接和调试。没有节头表程序仍可以政策执行。因为节头没有对程序的内存布局进行描述,这是程序头表的任务。用readelf可以看到节和段的关系。

    • .text节是保存了程序代码指令的代码节。一段可执行程序,存在Phdr,.text就会存在于text段中。由于.text节保存了程序代码,因此节的类型为SHT_PROGBITS。
    • .rodata 保存只读数据。类型SHT_PROGBITS。
    • .plt 过程链接表(Procedure Linkage Table),包含动态链接器调用从共享库导入的函数所必须的相关代码。存在于text段中,类型SHT_PROGBITS。
    • .bss节保存未初始化全局数据,是data的一部分。程序加载时数据被初始化成0,在程序执行期间可以赋值,未保存实际数据,类型SHT_NOBITS。
    • .got节保存全局偏移表。它和.plt节一起提供了对导入的共享库函数访问的入口。由动态链接器在运行时进行修改。如果攻击者获得堆或者.bss漏洞的一个指针大小写原语,就可以对该节任意修改。类型SHT_PROGBITS。
    • .dynsym节保存共享库导入的动态符号信息,该节在text段中,类型SHT_DYNSYM。
    • .dynstr保存动态符号字符串表,存放一系列字符串,代表了符号的名称,以空字符作为终止符。
    • .rel节保存重定位信息,类型SHT_REL。
    • .hash节,也称为.gnu.hash,保存一个查找符号散列表。
    • .symtab节,保存了ElfN_Sym类型的符号信息,类型SHT_SYMTAB。
    • strtab节,保存符号字符串表,表中内容被.symtab的ElfN_Sym结构中的st_name条目引用。类型SHT_SYMTAB。
    • .shstrtab节,保存节头字符串表,以空字符终止的字符串集合,保存了每个节节名,如.text,.data等。有个e_shsrndx的ELF文件头条目会指向.shstrtab节,e_shstrndx中保存了.shstrtab的偏移量。这节的类型是SHT_SYMTAB。
    • .ctors和.dtors节,前者构造器,后者析构器,指向构造函数和析构函数的函数指针,构造函数是在main函数执行前需要执行的代码,析构是main函数之后需要执行的代码。
      可以使用 readelf -S 查看ET_REL文件的节头。
    root@ubuntu:~/Desktop# readelf -S UDP_25000
    There are 28 section headers, starting at offset 0xebb64:
    
    Section Headers:
      [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
      [ 0]                   NULL            00000000 000000 000000 00      0   0  0
      [ 1] .note.ABI-tag     NOTE            080480d4 0000d4 000020 00   A  0   0  4
      [ 2] .init             PROGBITS        080480f4 0000f4 000017 00  AX  0   0  4
      [ 3] .text             PROGBITS        08048120 000120 0b5cc0 00  AX  0   0 32
      [ 4] __libc_thread_fre PROGBITS        080fdde0 0b5de0 0000e2 00  AX  0   0  4
      [ 5] __libc_freeres_fn PROGBITS        080fdec4 0b5ec4 000f6e 00  AX  0   0  4
      [ 6] .fini             PROGBITS        080fee34 0b6e34 00001a 00  AX  0   0  4
      [ 7] .rodata           PROGBITS        080fee60 0b6e60 01d89a 00   A  0   0 32
      [ 8] __libc_atexit     PROGBITS        0811c6fc 0d46fc 000004 00   A  0   0  4
      [ 9] __libc_subfreeres PROGBITS        0811c700 0d4700 00003c 00   A  0   0  4
      [10] __libc_thread_sub PROGBITS        0811c73c 0d473c 000004 00   A  0   0  4
      [11] .eh_frame         PROGBITS        0811c740 0d4740 00fc30 00   A  0   0  4
      [12] .gcc_except_table PROGBITS        0812c370 0e4370 00439f 00   A  0   0  4
      [13] .tdata            PROGBITS        08131000 0e9000 000014 00 WAT  0   0  4
      [14] .tbss             NOBITS          08131014 0e9014 000018 00 WAT  0   0  4
      [15] .ctors            PROGBITS        08131014 0e9014 000028 00  WA  0   0  4
      [16] .dtors            PROGBITS        0813103c 0e903c 00000c 00  WA  0   0  4
      [17] .jcr              PROGBITS        08131048 0e9048 000004 00  WA  0   0  4
      [18] .data.rel.ro      PROGBITS        08131060 0e9060 00063c 00  WA  0   0 32
      [19] .got              PROGBITS        0813169c 0e969c 00005c 04  WA  0   0  4
      [20] .got.plt          PROGBITS        081316f8 0e96f8 00000c 04  WA  0   0  4
      [21] .data             PROGBITS        08131720 0e9720 0014b4 00  WA  0   0 32
      [22] .bss              NOBITS          08132be0 0eabd4 008314 00  WA  0   0 32
      [23] __libc_freeres_pt NOBITS          0813aef4 0eabd4 000018 00  WA  0   0  4
      [24] .comment          PROGBITS        00000000 0eabd4 000e6a 00      0   0  1
      [25] .shstrtab         STRTAB          00000000 0eba3e 000126 00      0   0  1
      [26] .symtab           SYMTAB          00000000 0ebfc4 0156b0 10     27 1276  4
      [27] .strtab           STRTAB          00000000 101674 02935f 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)
    

    因为可重定位文件不会存在程序头,.o类型文件会被链接到可执行文件中,但是不会
    直接加载到内存,所以readelf -l xxx.o 不会得到想要的结果。不过Linux中可加载内核模块(LKM)是个例外,LKM是ET_REL类型的文件,它会被直接加载进内核的内存中,并自动进行重定位。

    ELF符号

    符号是对某些类型的数据或者代码的符号引用。在大多数共享库和动态链接可执行文件中,存在两个符号表。.dynsym和.symtab。
    .dynsym保存了引用来自外部文件符号的全局符号,是.symtab的子集,.symtab保存了所有符号。.dynsym只保存了动态/全局符号。

    用两个符号表的原因

    使用readelf -S 看到了一部分节被标记了A(ALLOC),WA(WRITE,ALLOC)或者AX(ALLOC/EXEC)。.dynsym被标记了ALLOC的,.symtab则没有标记。ALLOC表示有该标记的节会在运行时分配并装载进入内存,而.symtab不是运行是必须的,不会被装载到内存中。.dynsym保存的符号只能在运行时被解析,所以时运行时动态连接器所需要的唯一符号。.dynsym符号表对于动态链接可执行文件的执行来说必需的,而.symtab符号表只是用来调试和链接的。

    参考文献

    [1] 《Linux二进制分析》Ryan O’Neill

    展开全文
  • 前面章节我们了解了ELF文件的头部结构,这次我们深入了解另一个非常重要的数据结构,那就是程序表头。操作系统严重依赖该结构来加载ELF文件或是实现动态链接。程序表头反映的是当ELF加载到内存后所形成的“视图”或...

    前面章节我们了解了ELF文件的头部结构,这次我们深入了解另一个非常重要的数据结构,那就是程序表头。操作系统严重依赖该结构来加载ELF文件或是实现动态链接。程序表头反映的是当ELF加载到内存后所形成的“视图”或结构,也就是说ELF文件存在硬盘上或者被加载到内存,它展现出来的形态不一致。

    我们先看程序表头的数据结构:

    typedef  struct {
        unit32_t  p_type;    #数据类型
        uint332_t  p_flags; #标志位
        uint64_t  p_offset; #在ELF文件中的偏移
        uint64_t  p_vaddr;  #虚拟地址
        uint64_t  p_paddr;  #物理地址
        uint64_t  p_fllesz;  #在硬盘上的大小
        uint64_t  p_memsz;  #在内存中大小
        uint64_t  p_align;  #内存对齐方式
    } Elf64_Phdr;
    

    使用命令 readelf --wide --segments a.out可以读取程序表头内容信息:

    在这里插入图片描述
    这里需要注意的是,程序表头其实没有什么新意,它其实应前面说过的若干个段所形成的集合。接下来我们看每个字段的含义。

    p_type对应表头的类型,常用的数值有PT_LOAD, PT_DYNAMIC, PT_INTER。如果取值PT_LOAD,意味着表头对应的段需要加装到内存中;从上图看到有两个表头的类型为PT_LOAD,分别为第3和4,而第3个表头对应段的集合为.init_array .fini_array等,第4个表头对应段集合为.dynamic,着意味着这些段需要加载到内存中,同时每个表头对应的段都要合成一个整体加载到表头中所指定的位置。

    PT_FLAGS对应段加载到内存后的读写权限,常用的值有PF_X,PF_W,PF_R。PF_X表示表头对应的段可以被执行,PF_W对应加载的那些段可以被修改,PT_R表示加载的段可以被读取。p_offset表示表头对应那些段的起始地址,p_vaddr表示表头对应段该加载的虚拟位置,p_filesz表示表头对应段在硬盘上的大小,p_memsz表示表头对应段在加载到内存后的大小。

    你可能会困惑,为何p_filesz和p_memsz的值不一样。这是因为有些段在硬盘上不占据容量,只有加载到内存时才分配容量。最后p_align表示内存对齐方式,它的取值为2的指数,同时p_vaddr必须等于(p_offset % p_align)

    了解了ELF二进制内部原理后,我们需要实现手动加载ELF文件,实现这个目标,我们需要依赖一个库叫libbfd,这个库提供很多功能让调用者能解读X86架构下的通用二进制可执行文件。其安装可以使用如下命令:

    sudo apt-get install -y libbfd-dev

    基本上所有版本的Linux都会附带这个代码库,该代码库提供了一个类叫Binary,用于对可执行二进制文件的抽象,同时还有Section类,它是对前面我们提到的段数据结构的抽象;同时它还提供Symbol类,这是对符号表的抽象,接下来我们先看看其基本使用方法:

    #include <stdio.h>
    #include <stdint.h>
    #include <string>
    #include "../inc/loader.h"
    
    
    int  main(int argc, char *argv[]) {
        
        size_t i;
        Binary  bin;  //represent elf file 
        Section  *sec;
        Symbol  *sym;
        std::string  fname;
    
        if  (argc < 2) {
            printf("need to set binary file name");
            return 1;
        }
    
        fname.assign(argv[1]);
        if (load_binary(fname, &bin, Binary::BIN_TYPE_AUTO) < 0) {
            printf("load binary fail!");
            return 1;
        }
        
    
        printf("loaded binary file name:%s", bin.filename.c_str());
        printf("loaded binary file type: %s", bin.type_str.c_str());
        printf("loaded binary file entry@0X%016jx\n: ", bin.entry);
        printf("loaded binary file bits: %u", bin.bits);
    
        for(i = 0; i < bin.sections.size(); i++) {
            sec = &bin.sections[i];
            printf("   0x%16jx  %-8ju  %-20s  %s\n", sec->vma, sec->size, sec->name.c_str(),
            sec->type == Section::SEC_TYPE_CODE?"CODE":"DATA");    
        }
      
        if (bin.symbols.size() > 0) {
            printf("scanned symbol tables:\n");
            for (i = 0; i < bin.symbols.size(); i++) {
                sym = &bin.symbols[i];
                printf("   %-40s  0x%016jx  %s\n", sym->name.c_str(), sym->addr, 
                (sym->type & Symbol::SYM_TYPE_FUNC)? "FUNC":"");
            }
        }
    
        unload_binary(&bin);
      
        return 0;
    }
    
    

    代码中需要注意的是,loader.h是来自libbfd库的头文件,读者需要修改代码中该文件的路径以对应你电脑上libbfd的安装路径。Binary类用于对整个elf文件的抽象,通过它可以访问ELF文件相关信息,Section是对前面章节描述的段对象的抽象,Symbol是对前面章节符号表对象的抽象。

    load_binary是来自libbfd库提供的函数,它将elf文件加载到内存中。上面代码编译时对应的Makefile内容为:

    CXX=g++
    OBJ=my_loader
    
    .PHONY: all clean
    
    all: $(OBJ)
    
    loader.o: ../inc/loader.cc
    	$(CXX) -std=c++11 -c ../inc/loader.cc
    
    my_loader: loader.o my_loader.cc
    	$(CXX) -std=c++11 -o my_loader my_loader.cc loader.o -lbfd
    
    clean:
    	rm -f $(OBJ) *.o
    

    执行make命令编译后,在本地目录会有my_loader可执行文件,使用命令./my_load a.out即可让程序加载a.out文件并输出一系列信息:
    在这里插入图片描述
    对于libbfd更加详细的使用方法,我们在后续章节会详细介绍。

    展开全文
  • elf64

    2019-08-21 10:55:39
    ELF头部结构定义为: typedef struct { unsigned char e_ident[EI_NIDENT]; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff;...
    • ELF头部结构定义为:

    typedef struct {

        unsigned char e_ident[EI_NIDENT];

        Elf32_Half  e_type;

        Elf32_Half  e_machine;

        Elf32_Word  e_version;

        Elf32_Addr  e_entry;

        Elf32_Off   e_phoff;

        Elf32_Off   e_shoff;

        Elf32_Word  e_flags;

        Elf32_Half  e_ehsize;

        Elf32_Half  e_phentsize;

        Elf32_Half  e_phnum;

        Elf32_Half  e_shentsize;

        Elf32_Half  e_shnum;

        Elf32_Half  e_shstrndx;

    } Elf32_Ehdr;

     

    typedef struct elf64_hdr {内核

      unsigned char        e_ident[EI_NIDENT];        16字节/* ELF "magic number" */

      Elf64_Half e_type;

      Elf64_Half e_machine;

      Elf64_Word e_version;

      Elf64_Addr e_entry;                /* Entry point virtual address */

      Elf64_Off e_phoff;                /* Program header table file offset */

      Elf64_Off e_shoff;                /* Section header table file offset */

      Elf64_Word e_flags;

      Elf64_Half e_ehsize;

      Elf64_Half e_phentsize;

      Elf64_Half e_phnum;

      Elf64_Half e_shentsize;

      Elf64_Half e_shnum;

      Elf64_Half e_shstrndx;

    } Elf64_Ehdr;

     

     

     

    ?        Section头部结构定义为:

    typedef struct {

        Elf32_Word  sh_name;

        Elf32_Word  sh_type;

        Elf32_Word  sh_flags;

        Elf32_Addr  sh_addr;

        Elf32_Off   sh_offset;

        Elf32_Word  sh_size;

        Elf32_Word  sh_link;

        Elf32_Word  sh_info;

        Elf32_Word  sh_addralign;

        Elf32_Word  sh_entsize;

    } Elf32_Shdr;

    typedef struct {

        Elf32_Addr r_offset;

        Elf32_Word r_info;

    } Elf32_Rel;

     

     

     

     

    • ELF符号表Section .symtab的表项结构定义为:

    typedef struct {

        Elf32_Word st_name;

        Elf32_Addr st_value;

        Elf32_Word st_size;

        unsigned char st_info;

        unsigned char st_other;

        Elf32_Half st_shndx;

    } Elf32_sym;

    BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。

    BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。

     

    bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小。

    data(已手动初始化的数据)段则为数据分配空间,数据保存在目标文件中。

     

    来自 <http://www.cppblog.com/prayer/archive/2009/08/17/93594.html>

    在于思考

    博客园首页新随笔订阅 管理

    随笔 - 132  文章 - 33  评论 - 138

    地址空间分布

     

      最近看了本书,突然对于地址空间有些疑惑。在深入理解linux内核中把地址分为三类:逻辑地址(汇编语言中操作数地址或指令的地址,对于80x86的cup,逻辑地址是段+段内偏移地址)、线性地址(也叫虚拟地址)和物理地址。但在Stott Maxwell的《Linux Core Kernel Commentrary》中确是这样分的:逻辑地址(也叫虚拟地址)、线性地址和物理地址。按照386 CPU总设计师 John Crowford的解释,虚拟地址是保护模式下段和段内偏移量组成的地址,而逻辑地址就是代码段内偏移量,或称进程的逻辑地址。其实对于linux来说,这三种说法都没错,由于linux下并不主张将程序分段,而是主张分页,所以即使是在80x86的体系结构下,段的基地址也是0。因此逻辑地址、线性地址、虚拟地址在linux中其实是相同的。所以对于linux下的elf可执行文件来说,代码段的起始地址0x08048000既是逻辑地址,也是线性地址也是虚拟地址。

     

      1 x86的物理地址空间布局:

     

     

     

     

     

      物理地址空间的顶部以下一段空间,被PCI设备的I/O内存映射占据,它们的大小和布局由PCI规范所决定。640K~1M这段地址空间被BIOS和VGA适配器所占据。

     

      Linux系统在初始化时,会根据实际的物理内存的大小,为每个物理页面创建一个page对象,所有的page对象构成一个mem_map数组。

     

    进一步,针对不同的用途,Linux内核将所有的物理页面划分到3类内存管理区中,如图,分别为ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。

     

      ZONE_DMA的范围是0~16M,该区域的物理页面专门供I/O设备的DMA使用。之所以需要单独管理DMA的物理页面,是因为DMA使用物理地址访问内存,不经过MMU,并且需要连续的缓冲区,所以为了能够提供物理上连续的缓冲区,必须从物理地址空间专门划分一段区域用于DMA。

     

      ZONE_NORMAL的范围是16M~896M,该区域的物理页面是内核能够直接使用的。

     

      ZONE_HIGHMEM的范围是896M~结束,该区域即为高端内存,内核不能直接使用。

     

    2 linux虚拟地址内核空间分布

     

     

     

      在kernel image下面有16M的内核空间用于DMA操作。位于内核空间高端的128M地址主要由3部分组成,分别为vmalloc area,持久化内核映射区,临时内核映射区。

     

      由于ZONE_NORMAL和内核线性空间存在直接映射关系,所以内核会将频繁使用的数据如kernel代码、GDT、IDT、PGD、mem_map数组等放在ZONE_NORMAL里。而将用户数据、页表(PT)等不常用数据放在ZONE_ HIGHMEM里,只在要访问这些数据时才建立映射关系(kmap())。比如,当内核要访问I/O设备存储空间时,就使用ioremap()将位于物理地址高端的mmio区内存映射到内核空间的vmalloc area中,在使用完之后便断开映射关系。 上面描述默认都是32位的机器,对于64位的机器,PAGE_OFFSET为0x0xffff880000000000,用户地址空间范围:0x0000000000000000 - 0x00007fffffffffff,内核代码地址空间:0xffffffff80000000 - 0xffffffffa0000000。

     

    3 linux虚拟地址用户空间分布

     

     

     

      用户进程的代码区一般从虚拟地址空间的0x08048000开始,这是为了便于检查空指针。代码区之上便是数据区,未初始化数据区,堆区,栈区,以及参数、全局环境变量。

     

    4 linux虚拟地址与物理地址映射的关系

     

     

     

      Linux将4G的线性地址空间分为2部分,0~3G为user space,3G~4G为kernel space。

     

      由于开启了分页机制,内核想要访问物理地址空间的话,必须先建立映射关系,然后通过虚拟地址来访问。为了能够访问所有的物理地址空间,就要将全部物理地址空间映射到1G的内核线性空间中,这显然不可能。于是,内核将0~896M的物理地址空间一对一映射到自己的线性地址空间中,这样它便可以随时访问ZONE_DMA和ZONE_NORMAL里的物理页面;此时内核剩下的128M线性地址空间不足以完全映射所有的ZONE_HIGHMEM,Linux采取了动态映射的方法,即按需的将ZONE_HIGHMEM里的物理页面映射到kernel space的最后128M线性地址空间里,使用完之后释放映射关系,以供其它物理页面映射。虽然这样存在效率的问题,但是内核毕竟可以正常的访问所有的物理地址空间了。

     

    5 linux中可执行程序与虚拟地址空间的映射关系

     

      虚拟内存区域(VMA,Virtual Memory Area)是Linux中进程虚拟地址空间中的一个段,在Windows里面叫虚拟段。当操作系统创建线程后,会在进程相应的数据结构中设置一个.text段的VMA,它在虚拟空间中的地址为0x08048000~0x08049000,它对应ELF文件中的偏移为0的.text。可以查看操作系统为运行的进程维护的信息:

     

     

     

     

     

    从上面的图可以看出,虚拟空间地址为0x08048000~0x08049000的VMA映射为elf文件中的一个段(segment),并且是按整页进行映射的。

     

      由于linux下的ELF可执行文件会有很多个段(section),所以如果把每个section都映射为一个VMA,那么没有一个页大小的段(section)也会被映射为一个页的VMA,这样就浪费了物理空间,由于不足会用0补充。故elf有一个装载的段(segment),与前面的段(section)不同,前面的段(section)主要用于链接,而段(segment)主要用于装载进内存。

     

     

     

     

     

      可以看出段(segment)02包含了很多的段(section),那链接器怎样将段(section)合并到一个段(segment)中的呢?可以通过段(section)的权限来合并,如以代码段为代表的权限为可读可执行权限;以数据段和BSS段为代表的权限为可读可写的段;以只读数据为代表的权限为只读权限。

     

      ELF与Linux进程虚拟空间映射关系如下图所示:

     

     

     

     

     

      即使把多个段(section)合并到几个段(segment),每个段(segment)还是又很能产生较大的页内碎片,怎样解决这个问题呢?Unix巧妙的通过各个段(segment)接壤部分共享一个物理页来解决这个问题。

     

    参考:http://www.cnblogs.com/zszmhd/archive/2012/08/29/2661461.html、深入理解linux内核、Linux Core Kernel Commentrary、程序员的自我修养。

     

    分类: linux内核

    展开全文
  • ELF文件格式

    2018-11-09 15:36:30
    ELF”的全称是:Executable and Linking Format. 大意为可执行,可关联的文件格式,扩展名为elf .因此把这一类型的文件简称为“ELF”。
  • ELF reader

    2013-01-13 16:08:59
    The software is used for parsing ELF file fomrat of linux ELF object file
  • ELF文件

    2020-09-06 20:53:15
    elf文件可以分为三种类型可重定位的目标文件(Relocatable File,或者Object File)、可执行(Executable)、共享库(Shared Object,或者Shared Library)。 elf文件物理上可以分为elf header、Program Header Table、...
  • Core Flight System : Framework : Tool : ELF to cFE Table Converter This repository contains NASA's ELF to cFE Table Converter Tool (elf2cfetbl), which is a framework component of the Core Flight ...
  • ELF 文件格式

    2019-02-22 10:27:22
    Elf文件最详细的介绍。也是最好的一个介绍elf文件格式的资料。 看过都说好。
  • ELF介绍

    千次阅读 2019-05-11 11:59:25
    ELF是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储的标准文件格式。 ELF标准的目的是为软件开发人员提供一组二进制接口定义,这些接口可延伸到多种操作系统中,从而减少重新编码、编译程序的需要。...
  • elf注入的功能是在elf文件执行前,生成helloworld文件并写入内容:helloworld,之后再执行原elf文件功能 gcc main.c -o main # 生成注入函数 # 测试文件功能:打印 This is the program, whick will be injected.\...
  • ELF Workbench是一个旨在分析/检查ELF对象文件的程序,该程序也可以对其进行修补。 它提供了类似于GDB所使用的交互式用户界面。 有关更多详细信息,请参见下面的功能列表。
  • elf 控制台解析

    2018-12-30 23:51:57
    代码使用VS2015编写的,里面有解析elf的文件头、节头、程序头、以及通过函数名查找函数代码所在的偏移和代码字节数,支持32位和64位,育德函数没有调用,可查看相应的ElfFile32.h和ElfFile64.h
  • ELF Parse

    2019-02-04 22:12:51
    #ELF Format
  • ELF文件格式分析.pdf》文档,非常不错的elf格式参考文档,参考elf解析过程,能很快掌握elf文件格式
  • ELF入门

    2019-11-06 12:55:57
    Executable and Linkable Format ...ELF有两种视图,对应不同的目标文件 ELF的链接视图 对应可重定位的目标文件 ELF的执行视图对应可执行的目标文件 main.c程序 在这里插入代码片 ...
  • elf的头文件

    2017-11-30 16:06:36
    elf文件中的结构体描述的头文件,对解析elf文件很有用的.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 21,448
精华内容 8,579
关键字:

elf