浅谈内存映射

程序启动过程简介

Linux为例,Linux在启动以后为每个终端用户建立一个进程执行shell解释程序,该程序接收病执行命令的过程如下:

  • 读取命令行,分析命令,以命令名作为文件名,相关参数传入execve内部处理,执行execve后,引发int 0x80中断,进入内核态;
  • 终端进程调用fork建立一个子进程,并系统调用wait4等待,子进程根据文件名找到相关文件,将其调入内存,执行这个程序;
  • 若命令末尾有&则终端不需要系统调用wait4等待。

Linux支持不同的可执行文件格式,通过struct linux_binfmt结构体管理不同的可执行文件,这个结构中有对应的可执行文件的处理函数。
总体过程如下:

  • 进入内核态后,do_execve函数读入可执行文件,检查权限,读取可执行文件的相关信息;
  • 根据文件类型,在struct linux_binfmt中找到相应的load_binary()函数(对于ELF格式的文件,处理函数是load_elf_binary),加载可执行文件

ELF文件格式为例,其加载可执行文件的过程如下:

  • 读入ELf文件的头部,根据头部信息读入各种数据,扫描程序段描述符(Program Header Table),找到类型为PT_LOAD的段(即.text, .data, .bss等节区)
  • 将段映射到内存的固定地址上,如果没有动态连接器的描述段,则返回入口地址为应用程序入口;
  • 若应用程序使用了动态链接库,除加载可执行文件外,控制权交给动态链接器(ld-linux.so)。内核搜寻段表(Program Header Table),找到标记为 PT_INTERP 段中所对应的动态连接器的名称,并使用 load_elf_interp 加载其映像,并把返回的入口地址设置成 load_elf_interp 的返回值,即动态链接器的入口;

ELF文件

ELF 文件的大体结构:

----------------------ELF Header            |    #程序头,有该文件的Magic number(参考man magic),类型等----------------------Program Header Table  |    #对可执行文件和共享库有效,它描述下面各个节(section)组成的段----------------------Section1              |----------------------Section2              |----------------------Section3              |----------------------.....                 |----------------------Program Section Table |  #仅对可重定位目标文件和静态库有效,用于描述各个Section的重定位信息等。----------------------

ELF 文件的主要节区(section)有 .data,.text,.bss,.interp 等,而主要段(segment)有 LOAD,INTERP 等。

section解释实例
.data初始化的数据比如 int a=10
.bss未初始化的数据比如 char sum[100];这个在程序执行之前,内核将初始化为0
.text程序代码正文即可执行指令集
.interp描述程序需要的解析器(动态连接和装载程序)存在解释器的全路径,如/lib/ld-linux.so

示例

ELF Header:

ELF Header:  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00  Class:                             ELF64  Data:                              2's complement, little endian  Version:                           1 (current)  OS/ABI:                            UNIX - System V  ABI Version:                       0  Type:                              REL (Relocatable file)  Machine:                           Advanced Micro Devices X86-64  Version:                           0x1  Entry point address:               0x0  Start of program headers:          0 (bytes into file)  Start of section headers:          712 (bytes into file)  Flags:                             0x0  Size of this header:               64 (bytes)  Size of program headers:           0 (bytes)  Number of program headers:         0  Size of section headers:           64 (bytes)  Number of section headers:         13  Section header string table index: 12

Section Headers:
ELF 文件的主要节区(section)有 .data,.text,.bss,.interp 等,而主要段(segment)有 LOAD,INTERP 等。

  [Nr] Name              Type             Address           Offset       Size              EntSize          Flags  Link  Info  Align  [ 0]                   NULL             0000000000000000  00000000       0000000000000000  0000000000000000           0     0     0  [ 1] .text             PROGBITS         0000000000000000  00000040       0000000000000017  0000000000000000  AX       0     0     1  [ 2] .rela.text        RELA             0000000000000000  00000218       0000000000000030  0000000000000018   I      10     1     8  [ 3] .data             PROGBITS         0000000000000000  00000057       0000000000000000  0000000000000000  WA       0     0     1  [ 4] .bss              NOBITS           0000000000000000  00000057       0000000000000000  0000000000000000  WA       0     0     1  [ 5] .rodata           PROGBITS         0000000000000000  00000057       000000000000000d  0000000000000000   A       0     0     1  [ 6] .comment          PROGBITS         0000000000000000  00000064       000000000000002a  0000000000000001  MS       0     0     1  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  0000008e       0000000000000000  0000000000000000           0     0     1  [ 8] .eh_frame         PROGBITS         0000000000000000  00000090       0000000000000038  0000000000000000   A       0     0     8  [ 9] .rela.eh_frame    RELA             0000000000000000  00000248       0000000000000018  0000000000000018   I      10     8     8  [10] .symtab           SYMTAB           0000000000000000  000000c8       0000000000000120  0000000000000018          11     9     8  [11] .strtab           STRTAB           0000000000000000  000001e8       000000000000002e  0000000000000000           0     0     1  [12] .shstrtab         STRTAB           0000000000000000  00000260       0000000000000061  0000000000000000           0     0     1Key to Flags:  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),  L (link order), O (extra OS processing required), G (group), T (TLS),  C (compressed), x (unknown), o (OS specific), E (exclude),  l (large), p (processor specific)
#include #include int main(){    printf("hello world!\n");    return 0;}

对应的汇编代码如下:

        .file   "helloworld.c"        .text        .section        .rodata.LC0:        .string "hello world!"        .text        .globl  main        .type   main, @functionmain:.LFB5:        .cfi_startproc        pushq   %rbp        .cfi_def_cfa_offset 16        .cfi_offset 6, -16        movq    %rsp, %rbp        .cfi_def_cfa_register 6        leaq    .LC0(%rip), %rdi        call    puts@PLT        movl    $0, %eax        popq    %rbp        .cfi_def_cfa 7, 8        ret        .cfi_endproc.LFE5:        .size   main, .-main        .ident  "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"        .section        .note.GNU-stack,"",@progbits
寄存器名称注释
EIP/RAX程序指令指针通常指向下一条指令的位置
ESP/RSP程序堆栈指针通常指向当前堆栈的当前位置
EBP/RBP程序基指针通常指向函数使用的堆栈顶端
 void main() {     __asm__ __volatile__("jmp forward;"        "backward:"        "popl   %esi;"        "movl   $4, %eax;"        "movl   $2, %ebx;"        "movl   %esi, %ecx;"        "movl   $12, %edx;"        "int    $0x80;"        "movl   $1, %eax;"        "movl   $0, %ebx;"        "int    $0x80;"        "forward:"        "call   backward;"        ".string \"Hello World\\n\";");}
  • %rax 作为函数返回值使用。
  • %rsp 栈指针寄存器,指向栈顶
  • %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。。。
  • %rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改
  • %r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值

参考

Was this helpful?

0 / 0

发表回复 0