哈尔滨工业大学计算机系统大作业

计算机系统

大作业

    目  程序人生-Hellos P2P  

专       业   计算机专业与技术            

学     号    7203610609                  

      2036014                     

学       生    宋浩              

指 导 教 师    刘宏伟                             

计算机科学与技术学院

2022年5月

摘  要

本文介绍了hello程序预处理、编译、汇编、链接生成可执行文件的全过程,并分析了其在运行中与计算机系统进程管理、存储管理和IO管理等部件的相互作用和原理,展现了程序文件和计算机系统密不可分的底层关系。

关键词:hello,预处理,编译,汇编,链接,进程管理,存储管理,IO管理,edb                            

(摘要0分,缺失-1分,根据内容精彩称都酌情加分0-1分

目  录

第1章 概述

1.1 Hello简介

1.2 环境与工具

1.3 中间结果

1.4 本章小结

第2章 预处理

2.1 预处理的概念与作用

2.2在Ubuntu下预处理的命令

2.3 Hello的预处理结果解析

2.4 本章小结

第3章 编译

3.1 编译的概念与作用

3.2 在Ubuntu下编译的命令

3.3 Hello的编译结果解析

3.4 本章小结

第4章 汇编

4.1 汇编的概念与作用

4.2 在Ubuntu下汇编的命令

4.3 可重定位目标elf格式

4.4 Hello.o的结果解析

4.5 本章小结

第5章 链接

5.1 链接的概念与作用

5.2 在Ubuntu下链接的命令

5.3 可执行目标文件hello的格式

5.4 hello的虚拟地址空间

5.5 链接的重定位过程分析

5.6 hello的执行流程

5.7 Hello的动态链接分析

5.8 本章小结

第6章 hello进程管理

6.1 进程的概念与作用

6.2 简述壳Shell-bash的作用与处理流程

6.3 Hello的fork进程创建过程

6.4 Hello的execve过程

6.5 Hello的进程执行

6.6 hello的异常与信号处理

6.7本章小结

第7章 hello的存储管理

7.1 hello的存储器地址空间

7.2 Intel逻辑地址到线性地址的变换-段式管理

7.3 Hello的线性地址到物理地址的变换-页式管理

7.4 TLB与四级页表支持下的VA到PA的变换

7.5 三级Cache支持下的物理内存访问

7.6 hello进程fork时的内存映射

7.7 hello进程execve时的内存映射

7.8 缺页故障与缺页中断处理

7.9动态存储分配管理

7.10本章小结

第8章 hello的IO管理

8.1 Linux的IO设备管理方法

8.2 简述Unix IO接口及其函数

8.3 printf的实现分析

8.4 getchar的实现分析

8.5本章小结

结论

附件

参考文献


第1章 概述

1.1 Hello简介

根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。

P2P:在Linux系统中,hello.c经过cpp的预处理成为hello.i,再由ccl的编译变成汇编文件hello.s,然后汇编其as的汇编成为hello.o,最终经过ld的链接成为可执行目标程序hello,在shell中键入启动命令后,shell为其fork产生一个子进程,hello程序就变为了进程。

020: shell为此子进程execve,映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入主函数执行目标代码,CPU为运行的hello分配时间片执行逻辑控制流。当程序运行结束后,shell父进程负责回收hello进程,内核删除相关数据结构。

1.2 环境与工具

列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。

1.2.1 硬件环境

X64 CPU;2GHz;2G RAM;256GHD Disk 以上

1.2.2 软件环境

Windows10 64位;

Vmware Workstation Pro 15.5

Ubuntu 20.04 LTS 64位;

1.2.3 开发工具

CodeBlocks 64位;gedit+gcc

1.3 中间结果

列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。

中间结果文件名字

文件作用

hello.i

预处理得到的中间结果

hello.s

hello.i编译后得到的汇编语言文本文件

hello.o

hello.s汇编后得到的可重定位目标文件

hello.out

链接后得到的可执行目标文件

hello

可执行文件

helloelf.txt

hello.o的ELF格式文本

helloELF.txt

hello的ELF格式文本

asmhello.txt

hello.o的反汇编文本

asmhelloexe.txt

hello的反汇编文本

1.4 本章小结

•预处理阶段

预处理器(CPP)根据以字符 #开头的命令,修改原始的 C 程序。比如hello.c 中第1行的#include < stdio.h> 命令告诉预处理器读取系统头文件stdio.h 的内容,并把它直接插入程序文本中。结果就得到了另一个 C 程序,通常是以.i 作为文件扩展名。

•编译阶段

编译器(ccl)将文本文件 hello.i 翻译成文本文件 hello.s, 它包含一个汇编语言程序。该程序包含函数 main 的定义

定义中的每条语句都以一种文本格式描述了一条低级机器语言指令。

汇编语言是非常有用的,因为它为不同高级语言的不同编译器提供了通用的输出语言。例如,C 编译器和 Fortran 编译器产生的输出文件用的都是一样的汇编语言。

•汇编阶段

接下来,汇编器(as)将 hello.s 翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序(relocatable object program)的格式,并将结果保存在目标文件 hello.o 中。hello.o文件是一个二进制文件,它包含的 17 个字节是函数 main的指令编码。如果我们在文本编辑器中打开 hello.o文件,将看到一堆乱码[1]

(第1章0.5分)

图1

第2章 预处理

2.1 预处理的概念与作用

2.1.1概念

指在程序源代码被翻译为目标代码的过程中,生成二进制代码之前的过程。典型地,由预处理器(preprocessor) 对程序源代码文本进行处理,得到的结果再由编译器核心进一步编译。

预处理也称为预编译,它为编译做准备工作,主要进行代码文本的替换工作,用于处理#开头的指令,其中预处理产生编译器的输出。下表是一些常见的预处理指令及其功能。[3]

图2

2.1.2作用

C语言的预处理主要有三个方面的内容:宏定义、文件包含、条件编译。预处理命令以符号“#”开头。预处理工作也叫做宏展开,将宏名替换为文本。宏定义只需将符号常量替换成后面对应的文本即可。文件包含可以在一个文件中包含另一个文件的内容,被包含的文件称为头文件。头文件的内容可以有函数原型、宏定义、结构体定义。条件编译是在条件满足时才编译某些语句。使用条件编译可以使目标程序变小,运行时间变短。同时有利于代码的模块化。

2.2在Ubuntu下预处理的命令

命令:

gcc hello.c -o hello.i -E

图3

图4

图5

2.3 Hello的预处理结果解析

经过预处理之后,hello.c文件转化为hello.i文件。原文件中的宏进行了宏展开,头文件中的内容被包含进该文件中。打开该文件可以发现,文件长度变为3057行。文件的内容增加,且仍为可以阅读的C语言程序文本文件。

2.4 本章小结

本阶段完成了对hello.c的预处理工作。使用Ubuntu下的预处理指令可以将其转换为.i文件。

(第2章0.5分)


第3章 编译

3.1 编译的概念与作用

3.1.1概念

将预处理生成的文件,经过词法分析、语法分析、语义分析以及优化后编译成若干个目标模块。可以理解为将高级语言翻译成计算机可以理解的二进制码,即机器语言。

3.1.2作用

编译程序的基本功能是把源程序(高级语言)翻译成目标程序。但是,作为一个具有实际应用价值的编译系统,除了基本功能之外,还应具备语法检查、调试措施、修改手段、覆盖处理、目标程序优化、不同语言合用以及人机联系等重要功能。[4]

 注意:这儿的编译是指从 .i .s 即预处理后的文件到生成汇编语言程序

3.2 在Ubuntu下编译的命令

gcc -S hello.i -o hello.s

图6

图7

图8

图9

3.3 Hello的编译结果解析

C语言代码如下:

  1. #include   
  2. #include    
  3. #include   
  4.   
  5. int main(int argc,char *argv[]){  
  6.     int i;  
  7.   
  8.     if(argc!=4){  
  9.         printf("用法: Hello 7203610609 宋浩 秒数!\n");  
  10.         exit(1);  
  11.     }  
  12.     for(i=0;i<8;i++){  
  13.         printf("Hello %s %s\n",argv[1],argv[2]);  
  14.         sleep(atoi(argv[3]));  
  15.     }  
  16.     getchar();  
  17.     return 0;  
  18. }  

hello.s代码如下:

  1. .file   "hello.c"  
  2.     .text  
  3.     .section    .rodata  
  4.     .align 8  
  5. .LC0:  
  6.     .string "\347\224\250\346\263\225: Hello 7203610609 \345\256\213\346\265\251 \347\247\222\346\225\260\357\274\201"  
  7. .LC1:  
  8.     .string "Hello %s %s\n"  
  9.     .text  
  10.     .globl  main  
  11.     .type   main, @function
  12. main:  
  13. .LFB6:  
  14.     .cfi_startproc             //用于函数开始
  15.     endbr64  
  16.     pushq   %rbp  
  17.     .cfi_def_cfa_offset 16    //表示执行完pushq %rbp后sp与cfa偏移了16字节
  18.     .cfi_offset 6, -16  
  19.     movq    %rsp, %rbp  
  20.     .cfi_def_cfa_register 6  
  21.     subq    $32, %rsp  
  22.     movl    %edi, -20(%rbp)    //%edi代表argc
  23.     movq    %rsi, -32(%rbp)    //%rsi代表argv[]
  24.     cmpl    $4, -20(%rbp)  
  25.     je  .L2                      //if(argc!=4)执行下面操作,否则跳转.L2
  26.     leaq    .LC0(%rip), %rdi  
  27.     call    puts@PLT  
  28.     movl    $1, %edi                
  29.     call    exit@PLT            //exit(1)
  30. .L2:  
  31.     movl    $0, -4(%rbp)  
  32.     jmp .L3  
  33. .L4:  
  34.     movq    -32(%rbp), %rax  
  35.     addq    $16, %rax           //argv[2]
  36.     movq    (%rax), %rdx  
  37.     movq    -32(%rbp), %rax  
  38.     addq    $8, %rax            //argv[1]
  39.     movq    (%rax), %rax  
  40.     movq    %rax, %rsi  
  41.     leaq    .LC1(%rip), %rdi  
  42.     movl    $0, %eax  
  43.     call    printf@PLT  
  44.     movq    -32(%rbp), %rax  
  45.     addq    $24, %rax            //argv[3],sleep的参数
  46.     movq    (%rax), %rax  
  47.     movq    %rax, %rdi  
  48.     call    atoi@PLT     //调用atoi函数
  49.     movl    %eax, %edi  
  50.     call    sleep@PLT    //调用sleep函数
  51.     addl    $1, -4(%rbp)  
  52. .L3:  
  53.     cmpl    $7, -4(%rbp)  //用来判断i是否超过8
  54.     jle .L4                //不超过则执行L4循环体
  55.     call    getchar@PLT  //调用getchar函数
  56.     movl    $0, %eax    
  57.     leave  
  58.     .cfi_def_cfa 7, 8  
  59.     ret                   //return 0
  60.     .cfi_endproc  
  61. .LFE6:  
  62.     .size   main, .-main  
  63.     .ident  "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0"  
  64.     .section    .note.GNU-stack,"",@progbits  
  65.     .section    .note.gnu.property,"a"  
  66.     .align 8  
  67.     .long    1f - 0f  
  68.     .long    4f - 1f  
  69.     .long    5  
  70. 0:  
  71.     .string  "GNU"  
  72. 1:  
  73.     .align 8  
  74.     .long    0xc0000002  
  75.     .long    3f - 2f  
  76. 2:  
  77.     .long    0x3  
  78. 3:  
  79.     .align 8  
  80. 4: 

3.3.0文件结构

.file:声明源文件
.text:声明代码段
.data:声明数据段
.rodata:只读数据
.align:数据或者指令的地址对其方式
.string:声明一个字符串
.globl:声明全局变量
.type:指定类型

  1. .file   "hello.c"  
  2.     .text  
  3.     .section    .rodata  
  4.     .align 8  
  5. .LC0:  
  6.     .string "\347\224\250\346\263\225: Hello 7203610609 \345\256\213\346\265\251 \347\247\222\346\225\260\357\274\201"  
  7. .LC1:  
  8.     .string "Hello %s %s\n"  
  9.     .text  
  10.     .globl  main  
  11.     .type   main, @function

3.3.1数据

①字符串

字符串常量被存在只读数据段(.rodata)

图10

②局部变量

main函数声明了一个局部变量i,编译器进行编译的时候将局部变量i会放在堆栈中。如图所示,局部变量i放在栈上-4(%rbp)的位置且赋值为0。

图11

③常量

程序中的数字常量直接存储在程序段(.text)

图12

④全局变量

全局函数main

图13

3.3.2赋值

赋值主要通过mov指令进行操作,如下:

图14

图15

3.3.3算数操作

加法操作,此处为i=i+1。

图16

减法操作。

图17

3.3.4关系操作

图18

图19

3.3.5数组操作

数组的起始地址存放在栈中-32(%rbp)的位置并对相应位置进行字符串的赋值。如下图:

图20

图21

3.3.6控制转移

主要通过j类命令操作。

图22

图23

3.3.7函数操作

主要通过call命令。

图24

3.3.8类型转换

图25

调用atoi函数将字符型转换成int型

3.4 本章小结

概述了编译的概念和作用,重点详细分析了C程序编译成汇编程序的过程以及不同C语言的操作翻译成汇编语句的表示和处理方法。

(第3章2分)


第4章 汇编

4.1 汇编的概念与作用

4.1.1概念

汇编是指把汇编语言翻译成机器语言的过程,在这里还包括把这些机器语言指令打包成为可重定位目标程序的过程。

4.1.2作用

把汇编语言一一对应地翻译成机器可以直接执行的机器指令。

注意:这儿的汇编是指从 .s 到 .o 即编译后的文件到生成机器语言二进制程序的过程。

4.2 在Ubuntu下汇编的命令

gcc hello.s -c -o hello.o

图26

再用gedit打开可以看到乱码:

图27

4.3 可重定位目标elf格式

分析hello.o的ELF格式,用readelf等列出其各节的基本信息,特别是重定位项目分析。

4.3.1概览

首先ELF文件是一种用于二进制文件、可执行文件、目标代码、共享库和core转存格式文件。是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的,也是Linux的主要可执行文件格式。

使用readelf -l 命令可以查看一个链接后的elf可执行文件,Section to Segment 的映射关系

使用命令readelf -a hello.o > helloelf.txt 查看hello.oELF格式,并将结果重定位为文本文件如下:

图28

图29

其完整代码如下:

  1. ELF Header:                 //ELF头↓
  2.   Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00   
  3.   Class:                             ELF64  
  4.   Data:                              2's complement, little endian         //2补码,小端序
  5.   Version:                           1 (current)  
  6.   OS/ABI:                            UNIX - System V  
  7.   ABI Version:                       0  
  8.   Type:                              REL (Relocatable file)                //可重定位文件
  9.   Machine:                           Advanced Micro Devices X86-64        //系统架构
  10.   Version:                           0x1  
  11.   Entry point address:               0x0                                     //入口点地址
  12.   Start of program headers:          0 (bytes into file)                    //程序头起点
  13.   Start of section headers:          1248 (bytes into file)               //节头起点
  14.   Flags:                             0x0                                    //标志
  15.   Size of this header:               64 (bytes)                            //本头大小64字节
  16.   Size of program headers:           0 (bytes)                             //程序头大小0字节
  17.   Number of program headers:         0  
  18.   Size of section headers:           64 (bytes)                           //节头大小64字节
  19.   Number of section headers:         14                                    //有14个节头
  20.   Section header string table index: 13                                    //字符串表索引节头           
  21.       //ELF头↑
  22. Section Headers:                                                                       //节头表↓     
  23.   [Nr] Name              Type             Address           Offset   //Offset是偏移量
  24.        Size              EntSize          Flags  Link  Info  Align  
  25.   [ 0]                   NULL             0000000000000000  00000000  
  26.        0000000000000000  0000000000000000           0     0     0  
  27.   [ 1] .text             PROGBITS         0000000000000000  00000040  
  28.        0000000000000092  0000000000000000  AX       0     0     1  
  29.   [ 2] .rela.text        RELA             0000000000000000  00000390  
  30.        00000000000000c0  0000000000000018   I      11     1     8  
  31.   [ 3] .data             PROGBITS         0000000000000000  000000d2  
  32.        0000000000000000  0000000000000000  WA       0     0     1  
  33.   [ 4] .bss              NOBITS           0000000000000000  000000d2  
  34.        0000000000000000  0000000000000000  WA       0     0     1  
  35.   [ 5] .rodata           PROGBITS         0000000000000000  000000d8  
  36.        0000000000000037  0000000000000000   A       0     0     8  
  37.   [ 6] .comment          PROGBITS         0000000000000000  0000010f  
  38.        000000000000002c  0000000000000001  MS       0     0     1  
  39.   [ 7] .note.GNU-stack   PROGBITS         0000000000000000  0000013b  
  40.        0000000000000000  0000000000000000           0     0     1  
  41.   [ 8] .note.gnu.propert NOTE             0000000000000000  00000140  
  42.        0000000000000020  0000000000000000   A       0     0     8  
  43.   [ 9] .eh_frame         PROGBITS         0000000000000000  00000160  
  44.        0000000000000038  0000000000000000   A       0     0     8  
  45.   [10] .rela.eh_frame    RELA             0000000000000000  00000450  
  46.        0000000000000018  0000000000000018   I      11     9     8  
  47.   [11] .symtab           SYMTAB           0000000000000000  00000198  
  48.        00000000000001b0  0000000000000018          12    10     8  
  49.   [12] .strtab           STRTAB           0000000000000000  00000348  
  50.        0000000000000048  0000000000000000           0     0     1  
  51.   [13] .shstrtab         STRTAB           0000000000000000  00000468  
  52.        0000000000000074  0000000000000000           0     0     1                    
  53. Key to Flags:  
  54.   W (write), A (alloc), X (execute), M (merge), S (strings), I (info),  
  55.   L (link order), O (extra OS processing required), G (group), T (TLS),  
  56.   C (compressed), x (unknown), o (OS specific), E (exclude),  
  57.   l (large), p (processor specific)  
  58.                                                              //节头表↑
  59. There are no section groups in this file.  
  60.   
  61. There are no program headers in this file.  
  62.   
  63. There is no dynamic section in this file.  
  64.   
  65. Relocation section '.rela.text' at offset 0x390 contains 8 entries:  
  66.   Offset          Info           Type           Sym. Value    Sym. Name + Addend  
  67. 00000000001c  000500000002 R_X86_64_PC32     0000000000000000 .rodata - 4  
  68. 000000000021  000c00000004 R_X86_64_PLT32    0000000000000000 puts - 4  
  69. 00000000002b  000d00000004 R_X86_64_PLT32    0000000000000000 exit - 4  
  70. 000000000054  000500000002 R_X86_64_PC32     0000000000000000 .rodata + 26  
  71. 00000000005e  000e00000004 R_X86_64_PLT32    0000000000000000 printf - 4  
  72. 000000000071  000f00000004 R_X86_64_PLT32    0000000000000000 atoi - 4  
  73. 000000000078  001000000004 R_X86_64_PLT32    0000000000000000 sleep - 4  
  74. 000000000087  001100000004 R_X86_64_PLT32    0000000000000000 getchar - 4  
  75.   
  76. Relocation section '.rela.eh_frame' at offset 0x450 contains 1 entry:  
  77.   Offset          Info           Type           Sym. Value    Sym. Name + Addend  
  78. 000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0  
  79.   
  80. The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.  
  81.   
  82. Symbol table '.symtab' contains 18 entries:                                           //符号表↓
  83.    Num:    Value          Size Type    Bind   Vis      Ndx Name  
  84.      0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND   
  85.      1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c  
  86.      2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1   
  87.      3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3   
  88.      4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4   
  89.      5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5   
  90.      6: 0000000000000000     0 SECTION LOCAL  DEFAULT    7   
  91.      7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8   
  92.      8: 0000000000000000     0 SECTION LOCAL  DEFAULT    9   
  93.      9: 0000000000000000     0 SECTION LOCAL  DEFAULT    6   
  94.     10: 0000000000000000   146 FUNC    GLOBAL DEFAULT    1 main  
  95.     11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_  
  96.     12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts  
  97.     13: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND exit  
  98.     14: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf  
  99.     15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND atoi  
  100.     16: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND sleep  
  101.     17: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND getchar                  //符号表↑
  102.   
  103. No version information found in this file.  
  104.   
  105. Displaying notes found in: .note.gnu.property  
  106.   Owner                Data size    Description  
  107.   GNU                  0x00000010   NT_GNU_PROPERTY_TYPE_0  
  108.       Properties: x86 feature: IBT, SHSTK 

4.3.2分析[6]

ELF头以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含帮助链接器语法分析和解释目标文件的信息。其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或者共享的)、机器类型(如X86-64)、节头部表的文件偏移,以及节头部表中条目的大小和数量。不同节的位置和大小是由节头部表描述的,其中目标文件中每一个节都有一个固定大小的条目

可通过readelf -h hello.o读取ELF头信息,包括文件信息[5]

图30

可见,对应代码1-20行。

其中Magic含义如下图:图来自[7]

图31

程序头表(Program header table)列举了所有有效的段(segments)和他们的属性(执行视图)。程序头是一个结构的数组,每一个结构都表示一个段(segments)。在可执行文件或者共享链接库中所有的节(sections)都被分为不同的几个段(segments)。

可通过readelf -l hello.o读取ELF程序头信息

图32

可见,对应代码61行,为NULL。

节头表(Section header table) - 包含对节(sections)的描述(链接视图)一个ELF文件中到底有哪些具体的 sections,由包含在这个ELF文件中的 section head table(SHT)决定。每个section描述了这个段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限及段的其它属性。

可通过readelf -S hello.o读取sections' header

图33

④系统预定的固定section

以下给出常见section:

图34

  1. .text代码段

以通过objdump -d 反汇编,查看ELF文件代码段内容。

可通过命令readelf -x.text hello.o查看

图35

  1. .strtab 字符串表

在ELF文件中,会用到很多字符串,比如节名,变量名等。所以ELF将所有的字符串集中放到一个表里,每一个字符串以’\0’分隔,然后使用字符串在表中的偏移来引用字符串。这样在ELF中引用字符串只需要给出一个数组下标即可。字符串表在ELF也以段的形式保存, .shstrtab是专供section name的字符串表。可以通过命令readelf -x.strtab hello.o查看:

图36

  1. .symtab符号表

在链接过程中,我们将函数和变量统称为符号,函数名和变量名就是符号名。

每个定义的符号都有一个相应的值,叫做符号值(Symbol Value),对于变量和函数,符号值就是它们的地址。

可以使用下面命令查看:readelf -s hello.o

图37

可见对应代码82-101行。

也可以通过readelf -x.symtab hello.o查看其16进制输出:

图38

  1. .eh_frame / .eh_frame_hdr

在调试程序的时候经常需要进行堆栈回溯,早期使用通用寄存器(ebp)来保存每层函数调用的栈帧地址,但局限性很大。后来现代Linux操作系统在LSB(Linux Standard Base)标准中定义了一个.eh_frame section,用来描述如何去unwind the stackgcc编译器默认打开,如果不想把.eh_frame section编入elf文件,可以通过gcc选项 -fno-asynchronous-unwind-tables 去除。

GAS(GCC Assembler)汇编编译器定义了一组伪指令来协助eh_frame生成调用栈信息CFI(Call Frame Information)

  1. .relname重定位表

链接器在处理目标文件时,需要对目标文件中的某些部位进行重定位,即代码段和数据中中那些绝对地址引用的位置。对于每个需要重定位的代码段或数据段,都会有一个相应的重定位表。比如”.rel.text”就是针对”.text”的重定位表,”.rel.data”就是针对”.data”的重定位表。

可通过命令readelf -r hello.o查看

图39

信息的高4个字节表示该符号在.symtab中的index,低4个字节表示重定位的类型,加数是一个符号常数,一些类型的重定位要使用它对被修改引用的值做偏移调整。

  1. .rodata节

可通过命令readelf -x.rodata hello.o 查看其16进制输出:

图40

  

4.4 Hello.o的结果解析

objdump -d -r hello.o  分析hello.o的反汇编,并请与第3章的 hello.s进行对照分析。说明机器语言的构成,与汇编语言的映射关系。特别是机器语言中的操作数与汇编语言不一致,特别是分支转移函数调用等。

hello.s代码如下:

  1. .file   "hello.c"  
  2.     .text  
  3.     .section    .rodata  
  4.     .align 8  
  5. .LC0:  
  6.     .string "\347\224\250\346\263\225: Hello 7203610609 \345\256\213\346\265\251 \347\247\222\346\225\260\357\274\201"  
  7. .LC1:  
  8.     .string "Hello %s %s\n"  
  9.     .text  
  10.     .globl  main  
  11.     .type   main, @function
  12. main:  
  13. .LFB6:  
  14.     .cfi_startproc             //用于函数开始
  15.     endbr64  
  16.     pushq   %rbp  
  17.     .cfi_def_cfa_offset 16    //表示执行完pushq %rbp后sp与cfa偏移了16字节
  18.     .cfi_offset 6, -16  
  19.     movq    %rsp, %rbp  
  20.     .cfi_def_cfa_register 6  
  21.     subq    $32, %rsp  
  22.     movl    %edi, -20(%rbp)    //%edi代表argc
  23.     movq    %rsi, -32(%rbp)    //%rsi代表argv[]
  24.     cmpl    $4, -20(%rbp)  
  25.     je  .L2                      //if(argc!=4)执行下面操作,否则跳转.L2
  26.     leaq    .LC0(%rip), %rdi  
  27.     call    puts@PLT  
  28.     movl    $1, %edi                
  29.     call    exit@PLT            //exit(1)
  30. .L2:  
  31.     movl    $0, -4(%rbp)  
  32.     jmp .L3  
  33. .L4:  
  34.     movq    -32(%rbp), %rax  
  35.     addq    $16, %rax           //argv[2]
  36.     movq    (%rax), %rdx  
  37.     movq    -32(%rbp), %rax  
  38.     addq    $8, %rax            //argv[1]
  39.     movq    (%rax), %rax  
  40.     movq    %rax, %rsi  
  41.     leaq    .LC1(%rip), %rdi  
  42.     movl    $0, %eax  
  43.     call    printf@PLT  
  44.     movq    -32(%rbp), %rax  
  45.     addq    $24, %rax            //argv[3],sleep的参数
  46.     movq    (%rax), %rax  
  47.     movq    %rax, %rdi  
  48.     call    atoi@PLT     //调用atoi函数
  49.     movl    %eax, %edi  
  50.     call    sleep@PLT    //调用sleep函数
  51.     addl    $1, -4(%rbp)  
  52. .L3:  
  53.     cmpl    $7, -4(%rbp)  //用来判断i是否超过8
  54.     jle .L4                //不超过则执行L4循环体
  55.     call    getchar@PLT  //调用getchar函数
  56.     movl    $0, %eax    
  57.     leave  
  58.     .cfi_def_cfa 7, 8  
  59.     ret                   //return 0
  60.     .cfi_endproc  
  61. .LFE6:  
  62.     .size   main, .-main  
  63.     .ident  "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0"  
  64.     .section    .note.GNU-stack,"",@progbits  
  65.     .section    .note.gnu.property,"a"  
  66.     .align 8  
  67.     .long    1f - 0f  
  68.     .long    4f - 1f  
  69.     .long    5  
  70. 0:  
  71.     .string  "GNU"  
  72. 1:  
  73.     .align 8  
  74.     .long    0xc0000002  
  75.     .long    3f - 2f  
  76. 2:  
  77.     .long    0x3  
  78. 3:  
  79.     .align 8  
  80. 4: 

通过objdump -d -r hello.o > asmhello.txt反汇编hello.o并重定向为asmhello.txt如下:

图41

hello.o反汇编代码如下:

  1. hello.o:     file format elf64-x86-64  
  2.   
  3.   
  4. Disassembly of section .text:  
  5.   
  6. 0000000000000000 
    :  
  7.    0:   f3 0f 1e fa             endbr64   
  8.    4:   55                      push   %rbp  
  9.    5:   48 89 e5                mov    %rsp,%rbp  
  10.    8:   48 83 ec 20             sub    $0x20,%rsp  
  11.    c:   89 7d ec                mov    %edi,-0x14(%rbp)  
  12.    f:   48 89 75 e0             mov    %rsi,-0x20(%rbp)  
  13.   13:   83 7d ec 04             cmpl   $0x4,-0x14(%rbp)  
  14.   17:   74 16                   je     2f   
  15.   19:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # 20   
  16.             1c: R_X86_64_PC32   .rodata-0x4  
  17.   20:   e8 00 00 00 00          callq  25   
  18.             21: R_X86_64_PLT32  puts-0x4  
  19.   25:   bf 01 00 00 00          mov    $0x1,%edi  
  20.   2a:   e8 00 00 00 00          callq  2f   
  21.             2b: R_X86_64_PLT32  exit-0x4  
  22.   2f:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)  
  23.   36:   eb 48                   jmp    80   
  24.   38:   48 8b 45 e0             mov    -0x20(%rbp),%rax  
  25.   3c:   48 83 c0 10             add    $0x10,%rax  
  26.   40:   48 8b 10                mov    (%rax),%rdx  
  27.   43:   48 8b 45 e0             mov    -0x20(%rbp),%rax  
  28.   47:   48 83 c0 08             add    $0x8,%rax  
  29.   4b:   48 8b 00                mov    (%rax),%rax  
  30.   4e:   48 89 c6                mov    %rax,%rsi  
  31.   51:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # 58   
  32.             54: R_X86_64_PC32   .rodata+0x26  
  33.   58:   b8 00 00 00 00          mov    $0x0,%eax  
  34.   5d:   e8 00 00 00 00          callq  62   
  35.             5e: R_X86_64_PLT32  printf-0x4  
  36.   62:   48 8b 45 e0             mov    -0x20(%rbp),%rax  
  37.   66:   48 83 c0 18             add    $0x18,%rax  
  38.   6a:   48 8b 00                mov    (%rax),%rax  
  39.   6d:   48 89 c7                mov    %rax,%rdi  
  40.   70:   e8 00 00 00 00          callq  75   
  41.             71: R_X86_64_PLT32  atoi-0x4  
  42.   75:   89 c7                   mov    %eax,%edi  
  43.   77:   e8 00 00 00 00          callq  7c   
  44.             78: R_X86_64_PLT32  sleep-0x4  
  45.   7c:   83 45 fc 01             addl   $0x1,-0x4(%rbp)  
  46.   80:   83 7d fc 07             cmpl   $0x7,-0x4(%rbp)  
  47.   84:   7e b2                   jle    38   
  48.   86:   e8 00 00 00 00          callq  8b   
  49.             87: R_X86_64_PLT32  getchar-0x4  
  50.   8b:   b8 00 00 00 00          mov    $0x0,%eax  
  51.   90:   c9                      leaveq   
  52.   91:   c3                      retq  

对比发现主要流程并没有不同,存在不同主要有以下几个方面:

相异分析

hello.s

分支转移:

hello.s文件中分支转移是使用段名称进行跳转的,而反汇编hello.o文件中分支转移是通过地址进行跳转的。

数的进制: 

反汇编hello.o中,立即数全部是以16进制表示的,因为16进制与2进制之间的转换比十进制更加方便,所以都转换成了16进制。

函数调用: 

hello.s文件中,函数调用call后跟的是函数名称,而在hello.o文件中,call后跟的是下一条指令。这是因为 hello.c中调用的函数都是共享库中的函数,最终需要通过动态链接器才能确定函数的运行时执行地址,在汇编成为机器语言的时候,对于这些不确定地址的函数调用,在.rela.text 节中为其添加重定位条目,等待静态链接的进一步确定。

机器语言的构成,与汇编语言的映射关系:

机器语言是一种二进制语言,每一条指令、数据都由二进制来表示。汇编语言对于很多指令用一个字符串来表示,更容易读懂。每一条汇编语言操作码都可以用机器二进制数据来表示,进而可以将所有的汇编语言和二进制机器语言建立一一映射的关系,也就是双射。

4.5 本章小结

本章对汇编结果进行了详尽的介绍,介绍了汇编的概念与作用,以及汇编的命令。本章主要部分在于对可重定位目标ELF格式进行了详细的分析,重点在重定位项目上。同时对hello.o文件进行反汇编,将反汇编代码与hello.s文件进行了对比,在分析比较的过程中加深了对二者的理解。

(第4章1分)


5链接

5.1 链接的概念与作用

5.1.1概念

链接是将关联的所有可重定位的目标文件结合到一起,形成一个具有统一地址空间的可执行目标文件的过程。

5.1.2作用

将模块化编写的程序链接起来,成为一个整体,实现程序功能。提高了空间利用率,源程序文件中无需包含共享库的所有代码,直接调用即可。可执行文件运行时的内存中只需要包含所调用的函数的代码。

注意:这儿的链接是指从 hello.o 到hello生成过程。

5.2 在Ubuntu下链接的命令

链接的命令:ld -o hello -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o hello.o /usr/lib/x86_64-linux-gnu/libc.so /usr/lib/x86_64-linux-gnu/crtn.o

链接结果如下图所示:

使用ld的链接命令,应截图,展示汇编过程! 注意不只连接hello.o文件

5.3 可执行目标文件hello的格式

  分析hello的ELF格式,用readelf等列出其各段的基本信息,包括各段的起始地址,大小等信息。

使用命令readelf -a hello > helloELF.txt查看hello的ELF格式。

其完整代码如下:

  1. ELF Header:   //ELF头↓
  2.   Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00   
  3.   Class:                             ELF64  
  4.   Data:                              2's complement, little endian  
  5.   Version:                           1 (current)  
  6.   OS/ABI:                            UNIX - System V  
  7.   ABI Version:                       0  
  8.   Type:                              EXEC (Executable file)  
  9.   Machine:                           Advanced Micro Devices X86-64  
  10.   Version:                           0x1  
  11.   Entry point address:               0x4010f0  
  12.   Start of program headers:          64 (bytes into file)  
  13.   Start of section headers:          14208 (bytes into file)  
  14.   Flags:                             0x0  
  15.   Size of this header:               64 (bytes)  
  16.   Size of program headers:           56 (bytes)  
  17.   Number of program headers:         12  
  18.   Size of section headers:           64 (bytes)  
  19.   Number of section headers:         27  
  20.   Section header string table index: 26   //ELF头↑
  21.   
  22. Section Headers:                                                                             //节头↓
  23.   [Nr] Name              Type             Address           Offset  
  24.        Size              EntSize          Flags  Link  Info  Align  
  25.   [ 0]                   NULL             0000000000000000  00000000  
  26.        0000000000000000  0000000000000000           0     0     0  
  27.   [ 1] .interp           PROGBITS         00000000004002e0  000002e0  
  28.        000000000000001c  0000000000000000   A       0     0     1  
  29.   [ 2] .note.gnu.propert NOTE             0000000000400300  00000300  
  30.        0000000000000020  0000000000000000   A       0     0     8  
  31.   [ 3] .note.ABI-tag     NOTE             0000000000400320  00000320  
  32.        0000000000000020  0000000000000000   A       0     0     4  
  33.   [ 4] .hash             HASH             0000000000400340  00000340  
  34.        0000000000000038  0000000000000004   A       6     0     8  
  35.   [ 5] .gnu.hash         GNU_HASH         0000000000400378  00000378  
  36.        000000000000001c  0000000000000000   A       6     0     8  
  37.   [ 6] .dynsym           DYNSYM           0000000000400398  00000398  
  38.        00000000000000d8  0000000000000018   A       7     1     8  
  39.   [ 7] .dynstr           STRTAB           0000000000400470  00000470  
  40.        000000000000005c  0000000000000000   A       0     0     1  
  41.   [ 8] .gnu.version      VERSYM           00000000004004cc  000004cc  
  42.        0000000000000012  0000000000000002   A       6     0     2  
  43.   [ 9] .gnu.version_r    VERNEED          00000000004004e0  000004e0  
  44.        0000000000000020  0000000000000000   A       7     1     8  
  45.   [10] .rela.dyn         RELA             0000000000400500  00000500  
  46.        0000000000000030  0000000000000018   A       6     0     8  
  47.   [11] .rela.plt         RELA             0000000000400530  00000530  
  48.        0000000000000090  0000000000000018  AI       6    21     8  
  49.   [12] .init             PROGBITS         0000000000401000  00001000  
  50.        000000000000001b  0000000000000000  AX       0     0     4  
  51.   [13] .plt              PROGBITS         0000000000401020  00001020  
  52.        0000000000000070  0000000000000010  AX       0     0     16  
  53.   [14] .plt.sec          PROGBITS         0000000000401090  00001090  
  54.        0000000000000060  0000000000000010  AX       0     0     16  
  55.   [15] .text             PROGBITS         00000000004010f0  000010f0  
  56.        0000000000000145  0000000000000000  AX       0     0     16  
  57.   [16] .fini             PROGBITS         0000000000401238  00001238  
  58.        000000000000000d  0000000000000000  AX       0     0     4  
  59.   [17] .rodata           PROGBITS         0000000000402000  00002000  
  60.        000000000000003f  0000000000000000   A       0     0     8  
  61.   [18] .eh_frame         PROGBITS         0000000000402040  00002040  
  62.        00000000000000fc  0000000000000000   A       0     0     8  
  63.   [19] .dynamic          DYNAMIC          0000000000403e50  00002e50  
  64.        00000000000001a0  0000000000000010  WA       7     0     8  
  65.   [20] .got              PROGBITS         0000000000403ff0  00002ff0  
  66.        0000000000000010  0000000000000008  WA       0     0     8  
  67.   [21] .got.plt          PROGBITS         0000000000404000  00003000  
  68.        0000000000000048  0000000000000008  WA       0     0     8  
  69.   [22] .data             PROGBITS         0000000000404048  00003048  
  70.        0000000000000004  0000000000000000  WA       0     0     1  
  71.   [23] .comment          PROGBITS         0000000000000000  0000304c  
  72.        000000000000002b  0000000000000001  MS       0     0     1  
  73.   [24] .symtab           SYMTAB           0000000000000000  00003078  
  74.        00000000000004c8  0000000000000018          25    30     8  
  75.   [25] .strtab           STRTAB           0000000000000000  00003540  
  76.        0000000000000158  0000000000000000           0     0     1  
  77.   [26] .shstrtab         STRTAB           0000000000000000  00003698  
  78.        00000000000000e1  0000000000000000           0     0     1  
  79. Key to Flags:  
  80.   W (write), A (alloc), X (execute), M (merge), S (strings), I (info),  
  81.   L (link order), O (extra OS processing required), G (group), T (TLS),  
  82.   C (compressed), x (unknown), o (OS specific), E (exclude),  
  83.   l (large), p (processor specific)   //节头↑
  84.   
  85. There are no section groups in this file.  
  86.   
  87. Program Headers:   //程序头↓
  88.   Type           Offset             VirtAddr           PhysAddr  
  89.                  FileSiz            MemSiz              Flags  Align  
  90.   PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040  
  91.                  0x00000000000002a0 0x00000000000002a0  R      0x8  
  92.   INTERP         0x00000000000002e0 0x00000000004002e0 0x00000000004002e0  
  93.                  0x000000000000001c 0x000000000000001c  R      0x1  
  94.       [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]  
  95.   LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000  
  96.                  0x00000000000005c0 0x00000000000005c0  R      0x1000  
  97.   LOAD           0x0000000000001000 0x0000000000401000 0x0000000000401000  
  98.                  0x0000000000000245 0x0000000000000245  R E    0x1000  
  99.   LOAD           0x0000000000002000 0x0000000000402000 0x0000000000402000  
  100.                  0x000000000000013c 0x000000000000013c  R      0x1000  
  101.   LOAD           0x0000000000002e50 0x0000000000403e50 0x0000000000403e50  
  102.                  0x00000000000001fc 0x00000000000001fc  RW     0x1000  
  103.   DYNAMIC        0x0000000000002e50 0x0000000000403e50 0x0000000000403e50  
  104.                  0x00000000000001a0 0x00000000000001a0  RW     0x8  
  105.   NOTE           0x0000000000000300 0x0000000000400300 0x0000000000400300  
  106.                  0x0000000000000020 0x0000000000000020  R      0x8  
  107.   NOTE           0x0000000000000320 0x0000000000400320 0x0000000000400320  
  108.                  0x0000000000000020 0x0000000000000020  R      0x4  
  109.   GNU_PROPERTY   0x0000000000000300 0x0000000000400300 0x0000000000400300  
  110.                  0x0000000000000020 0x0000000000000020  R      0x8  
  111.   GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000  
  112.                  0x0000000000000000 0x0000000000000000  RW     0x10  
  113.   GNU_RELRO      0x0000000000002e50 0x0000000000403e50 0x0000000000403e50  
  114.                  0x00000000000001b0 0x00000000000001b0  R      0x1  
  115.   
  116.  Section to Segment mapping:  
  117.   Segment Sections...  
  118.    00       
  119.    01     .interp   
  120.    02     .interp .note.gnu.property .note.ABI-tag .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt   
  121.    03     .init .plt .plt.sec .text .fini   
  122.    04     .rodata .eh_frame   
  123.    05     .dynamic .got .got.plt .data   
  124.    06     .dynamic   
  125.    07     .note.gnu.property   
  126.    08     .note.ABI-tag   
  127.    09     .note.gnu.property   
  128.    10       
  129.    11     .dynamic .got    //程序头↑
  130.   
  131. Dynamic section at offset 0x2e50 contains 21 entries:  
  132.   Tag        Type                         Name/Value  
  133.  0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]  
  134.  0x000000000000000c (INIT)               0x401000  
  135.  0x000000000000000d (FINI)               0x401238  
  136.  0x0000000000000004 (HASH)               0x400340  
  137.  0x000000006ffffef5 (GNU_HASH)           0x400378  
  138.  0x0000000000000005 (STRTAB)             0x400470  
  139.  0x0000000000000006 (SYMTAB)             0x400398  
  140.  0x000000000000000a (STRSZ)              92 (bytes)  
  141.  0x000000000000000b (SYMENT)             24 (bytes)  
  142.  0x0000000000000015 (DEBUG)              0x0  
  143.  0x0000000000000003 (PLTGOT)             0x404000  
  144.  0x0000000000000002 (PLTRELSZ)           144 (bytes)  
  145.  0x0000000000000014 (PLTREL)             RELA  
  146.  0x0000000000000017 (JMPREL)             0x400530  
  147.  0x0000000000000007 (RELA)               0x400500  
  148.  0x0000000000000008 (RELASZ)             48 (bytes)  
  149.  0x0000000000000009 (RELAENT)            24 (bytes)  
  150.  0x000000006ffffffe (VERNEED)            0x4004e0  
  151.  0x000000006fffffff (VERNEEDNUM)         1  
  152.  0x000000006ffffff0 (VERSYM)             0x4004cc  
  153.  0x0000000000000000 (NULL)               0x0  
  154.   
  155. Relocation section '.rela.dyn' at offset 0x500 contains 2 entries:  
  156.   Offset          Info           Type           Sym. Value    Sym. Name + Addend  
  157. 000000403ff0  000300000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0  
  158. 000000403ff8  000500000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0  
  159.   
  160. Relocation section '.rela.plt' at offset 0x530 contains 6 entries:  
  161.   Offset          Info           Type           Sym. Value    Sym. Name + Addend  
  162. 000000404018  000100000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0  
  163. 000000404020  000200000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0  
  164. 000000404028  000400000007 R_X86_64_JUMP_SLO 0000000000000000 getchar@GLIBC_2.2.5 + 0  
  165. 000000404030  000600000007 R_X86_64_JUMP_SLO 0000000000000000 atoi@GLIBC_2.2.5 + 0  
  166. 000000404038  000700000007 R_X86_64_JUMP_SLO 0000000000000000 exit@GLIBC_2.2.5 + 0  
  167. 000000404040  000800000007 R_X86_64_JUMP_SLO 0000000000000000 sleep@GLIBC_2.2.5 + 0  
  168.   
  169. The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.  
  170.   
  171. Symbol table '.dynsym' contains 9 entries:  
  172.    Num:    Value          Size Type    Bind   Vis      Ndx Name  
  173.      0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND   
  174.      1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@GLIBC_2.2.5 (2)  
  175.      2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)  
  176.      3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)  
  177.      4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getchar@GLIBC_2.2.5 (2)  
  178.      5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__  
  179.      6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND atoi@GLIBC_2.2.5 (2)  
  180.      7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit@GLIBC_2.2.5 (2)  
  181.      8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@GLIBC_2.2.5 (2)  
  182.   
  183. Symbol table '.symtab' contains 51 entries:  
  184.    Num:    Value          Size Type    Bind   Vis      Ndx Name  
  185.      0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND   
  186.      1: 00000000004002e0     0 SECTION LOCAL  DEFAULT    1   
  187.      2: 0000000000400300     0 SECTION LOCAL  DEFAULT    2   
  188.      3: 0000000000400320     0 SECTION LOCAL  DEFAULT    3   
  189.      4: 0000000000400340     0 SECTION LOCAL  DEFAULT    4   
  190.      5: 0000000000400378     0 SECTION LOCAL  DEFAULT    5   
  191.      6: 0000000000400398     0 SECTION LOCAL  DEFAULT    6   
  192.      7: 0000000000400470     0 SECTION LOCAL  DEFAULT    7   
  193.      8: 00000000004004cc     0 SECTION LOCAL  DEFAULT    8   
  194.      9: 00000000004004e0     0 SECTION LOCAL  DEFAULT    9   
  195.     10: 0000000000400500     0 SECTION LOCAL  DEFAULT   10   
  196.     11: 0000000000400530     0 SECTION LOCAL  DEFAULT   11   
  197.     12: 0000000000401000     0 SECTION LOCAL  DEFAULT   12   
  198.     13: 0000000000401020     0 SECTION LOCAL  DEFAULT   13   
  199.     14: 0000000000401090     0 SECTION LOCAL  DEFAULT   14   
  200.     15: 00000000004010f0     0 SECTION LOCAL  DEFAULT   15   
  201.     16: 0000000000401238     0 SECTION LOCAL  DEFAULT   16   
  202.     17: 0000000000402000     0 SECTION LOCAL  DEFAULT   17   
  203.     18: 0000000000402040     0 SECTION LOCAL  DEFAULT   18   
  204.     19: 0000000000403e50     0 SECTION LOCAL  DEFAULT   19   
  205.     20: 0000000000403ff0     0 SECTION LOCAL  DEFAULT   20   
  206.     21: 0000000000404000     0 SECTION LOCAL  DEFAULT   21   
  207.     22: 0000000000404048     0 SECTION LOCAL  DEFAULT   22   
  208.     23: 0000000000000000     0 SECTION LOCAL  DEFAULT   23   
  209.     24: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello.c  
  210.     25: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS   
  211.     26: 0000000000403e50     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_end  
  212.     27: 0000000000403e50     0 OBJECT  LOCAL  DEFAULT   19 _DYNAMIC  
  213.     28: 0000000000403e50     0 NOTYPE  LOCAL  DEFAULT   19 __init_array_start  
  214.     29: 0000000000404000     0 OBJECT  LOCAL  DEFAULT   21 _GLOBAL_OFFSET_TABLE_  
  215.     30: 0000000000401230     5 FUNC    GLOBAL DEFAULT   15 __libc_csu_fini  
  216.     31: 0000000000404048     0 NOTYPE  WEAK   DEFAULT   22 data_start  
  217.     32: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND puts@@GLIBC_2.2.5  
  218.     33: 000000000040404c     0 NOTYPE  GLOBAL DEFAULT   22 _edata  
  219.     34: 0000000000401238     0 FUNC    GLOBAL HIDDEN    16 _fini  
  220.     35: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.5  
  221.     36: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_  
  222.     37: 0000000000404048     0 NOTYPE  GLOBAL DEFAULT   22 __data_start  
  223.     38: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND getchar@@GLIBC_2.2.5  
  224.     39: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__  
  225.     40: 0000000000402000     4 OBJECT  GLOBAL DEFAULT   17 _IO_stdin_used  
  226.     41: 00000000004011c0   101 FUNC    GLOBAL DEFAULT   15 __libc_csu_init  
  227.     42: 0000000000404050     0 NOTYPE  GLOBAL DEFAULT   22 _end  
  228.     43: 0000000000401120     5 FUNC    GLOBAL HIDDEN    15 _dl_relocate_static_pie  
  229.     44: 00000000004010f0    47 FUNC    GLOBAL DEFAULT   15 _start  
  230.     45: 000000000040404c     0 NOTYPE  GLOBAL DEFAULT   22 __bss_start  
  231.     46: 0000000000401125   146 FUNC    GLOBAL DEFAULT   15 main  
  232.     47: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND atoi@@GLIBC_2.2.5  
  233.     48: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND exit@@GLIBC_2.2.5  
  234.     49: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND sleep@@GLIBC_2.2.5  
  235.     50: 0000000000401000     0 FUNC    GLOBAL HIDDEN    12 _init  
  236.   
  237. Histogram for bucket list length (total of 3 buckets):  
  238.  Length  Number     % of total  Coverage  
  239.       0  0          (  0.0%)  
  240.       1  0          (  0.0%)      0.0%  
  241.       2  1          ( 33.3%)     25.0%  
  242.       3  2          ( 66.7%)    100.0%  
  243.   
  244. Version symbols section '.gnu.version' contains 9 entries:  
  245.  Addr: 0x00000000004004cc  Offset: 0x0004cc  Link: 6 (.dynsym)  
  246.   000:   0 (*local*)       2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)  
  247.   004:   2 (GLIBC_2.2.5)   0 (*local*)       2 (GLIBC_2.2.5)   2 (GLIBC_2.2.5)  
  248.   008:   2 (GLIBC_2.2.5)  
  249.   
  250. Version needs section '.gnu.version_r' contains 1 entry:  
  251.  Addr: 0x00000000004004e0  Offset: 0x0004e0  Link: 7 (.dynstr)  
  252.   000000: Version: 1  File: libc.so.6  Cnt: 1  
  253.   0x0010:   Name: GLIBC_2.2.5  Flags: none  Version: 2  
  254.   
  255. Displaying notes found in: .note.gnu.property  
  256.   Owner                Data size    Description  
  257.   GNU                  0x00000010   NT_GNU_PROPERTY_TYPE_0  
  258.       Properties: x86 feature: IBT, SHSTK  
  259.   
  260. Displaying notes found in: .note.ABI-tag  
  261.   Owner                Data size    Description  
  262.   GNU                  0x00000010   NT_GNU_ABI_TAG (ABI version tag)  
  263.     OS: Linux, ABI: 3.2.0 

①ELF头

命令:readelf -h hello

图41

对应1-20行。

②节头

命令:readelf -S hello

图42

图43

对应代码22-83行。起始地址0x3780

③程序头

命令:readelf -l hello

图44

图45

对应87-129行。起始地址0x4010f0,有12个程序头,开始于偏移64的位置。

④重定位节

命令:readelf -r hello

图46

⑤符号表

命令:readelf -s hello

图47

图48

5.4 hello的虚拟地址空间

使用edb加载hello,查看本进程的虚拟地址空间各段信息,并与5.3对照分析说明。

图49

 edbData Dump窗口。窗口显示虚拟地址由0x400000开始,从开始到结束这之间的每一个节对应5.3中的每一个节头表的声明。

图50

举例如下:

程序入口:

图51

.text从0x4010f0开始

图52

图53

5.5 链接的重定位过程分析

objdump -d -r hello 分析hello与hello.o的不同,说明链接的过程。

结合hello.o的重定位项目,分析hello中对其怎么重定位的。

输入命令如下:

图54

得到hello的反汇编代码如下:

  1. hello:     file format elf64-x86-64  
  2.   
  3.   
  4. Disassembly of section .init:  
  5.   
  6. 0000000000401000 <_init>:  
  7.   401000:   f3 0f 1e fa             endbr64   
  8.   401004:   48 83 ec 08             sub    $0x8,%rsp  
  9.   401008:   48 8b 05 e9 2f 00 00    mov    0x2fe9(%rip),%rax        # 403ff8 <__gmon_start__>  
  10.   40100f:   48 85 c0                test   %rax,%rax  
  11.   401012:   74 02                   je     401016 <_init+0x16>  
  12.   401014:   ff d0                   callq  *%rax  
  13.   401016:   48 83 c4 08             add    $0x8,%rsp  
  14.   40101a:   c3                      retq     
  15.   
  16. Disassembly of section .plt:  
  17.   
  18. 0000000000401020 <.plt>:  
  19.   401020:   ff 35 e2 2f 00 00       pushq  0x2fe2(%rip)        # 404008 <_GLOBAL_OFFSET_TABLE_+0x8>  
  20.   401026:   f2 ff 25 e3 2f 00 00    bnd jmpq *0x2fe3(%rip)        # 404010 <_GLOBAL_OFFSET_TABLE_+0x10>  
  21.   40102d:   0f 1f 00                nopl   (%rax)  
  22.   401030:   f3 0f 1e fa             endbr64   
  23.   401034:   68 00 00 00 00          pushq  $0x0  
  24.   401039:   f2 e9 e1 ff ff ff       bnd jmpq 401020 <.plt>  
  25.   40103f:   90                      nop  
  26.   401040:   f3 0f 1e fa             endbr64   
  27.   401044:   68 01 00 00 00          pushq  $0x1  
  28.   401049:   f2 e9 d1 ff ff ff       bnd jmpq 401020 <.plt>  
  29.   40104f:   90                      nop  
  30.   401050:   f3 0f 1e fa             endbr64   
  31.   401054:   68 02 00 00 00          pushq  $0x2  
  32.   401059:   f2 e9 c1 ff ff ff       bnd jmpq 401020 <.plt>  
  33.   40105f:   90                      nop  
  34.   401060:   f3 0f 1e fa             endbr64   
  35.   401064:   68 03 00 00 00          pushq  $0x3  
  36.   401069:   f2 e9 b1 ff ff ff       bnd jmpq 401020 <.plt>  
  37.   40106f:   90                      nop  
  38.   401070:   f3 0f 1e fa             endbr64   
  39.   401074:   68 04 00 00 00          pushq  $0x4  
  40.   401079:   f2 e9 a1 ff ff ff       bnd jmpq 401020 <.plt>  
  41.   40107f:   90                      nop  
  42.   401080:   f3 0f 1e fa             endbr64   
  43.   401084:   68 05 00 00 00          pushq  $0x5  
  44.   401089:   f2 e9 91 ff ff ff       bnd jmpq 401020 <.plt>  
  45.   40108f:   90                      nop  
  46.   
  47. Disassembly of section .plt.sec:  
  48.   
  49. 0000000000401090 :  
  50.   401090:   f3 0f 1e fa             endbr64   
  51.   401094:   f2 ff 25 7d 2f 00 00    bnd jmpq *0x2f7d(%rip)        # 404018   
  52.   40109b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)  
  53.   
  54. 00000000004010a0 :  
  55.   4010a0:   f3 0f 1e fa             endbr64   
  56.   4010a4:   f2 ff 25 75 2f 00 00    bnd jmpq *0x2f75(%rip)        # 404020   
  57.   4010ab:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)  
  58.   
  59. 00000000004010b0 :  
  60.   4010b0:   f3 0f 1e fa             endbr64   
  61.   4010b4:   f2 ff 25 6d 2f 00 00    bnd jmpq *0x2f6d(%rip)        # 404028   
  62.   4010bb:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)  
  63.   
  64. 00000000004010c0 :  
  65.   4010c0:   f3 0f 1e fa             endbr64   
  66.   4010c4:   f2 ff 25 65 2f 00 00    bnd jmpq *0x2f65(%rip)        # 404030   
  67.   4010cb:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)  
  68.   
  69. 00000000004010d0 :  
  70.   4010d0:   f3 0f 1e fa             endbr64   
  71.   4010d4:   f2 ff 25 5d 2f 00 00    bnd jmpq *0x2f5d(%rip)        # 404038   
  72.   4010db:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)  
  73.   
  74. 00000000004010e0 :  
  75.   4010e0:   f3 0f 1e fa             endbr64   
  76.   4010e4:   f2 ff 25 55 2f 00 00    bnd jmpq *0x2f55(%rip)        # 404040   
  77.   4010eb:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)  
  78.   
  79. Disassembly of section .text:  
  80.   
  81. 00000000004010f0 <_start>:  
  82.   4010f0:   f3 0f 1e fa             endbr64   
  83.   4010f4:   31 ed                   xor    %ebp,%ebp  
  84.   4010f6:   49 89 d1                mov    %rdx,%r9  
  85.   4010f9:   5e                      pop    %rsi  
  86.   4010fa:   48 89 e2                mov    %rsp,%rdx  
  87.   4010fd:   48 83 e4 f0             and    $0xfffffffffffffff0,%rsp  
  88.   401101:   50                      push   %rax  
  89.   401102:   54                      push   %rsp  
  90.   401103:   49 c7 c0 30 12 40 00    mov    $0x401230,%r8  
  91.   40110a:   48 c7 c1 c0 11 40 00    mov    $0x4011c0,%rcx  
  92.   401111:   48 c7 c7 25 11 40 00    mov    $0x401125,%rdi  
  93.   401118:   ff 15 d2 2e 00 00       callq  *0x2ed2(%rip)        # 403ff0 <__libc_start_main@GLIBC_2.2.5>  
  94.   40111e:   f4                      hlt      
  95.   40111f:   90                      nop  
  96.   
  97. 0000000000401120 <_dl_relocate_static_pie>:  
  98.   401120:   f3 0f 1e fa             endbr64   
  99.   401124:   c3                      retq     
  100.   
  101. 0000000000401125 
    :  
  102.   401125:   f3 0f 1e fa             endbr64   
  103.   401129:   55                      push   %rbp  
  104.   40112a:   48 89 e5                mov    %rsp,%rbp  
  105.   40112d:   48 83 ec 20             sub    $0x20,%rsp  
  106.   401131:   89 7d ec                mov    %edi,-0x14(%rbp)  
  107.   401134:   48 89 75 e0             mov    %rsi,-0x20(%rbp)  
  108.   401138:   83 7d ec 04             cmpl   $0x4,-0x14(%rbp)  
  109.   40113c:   74 16                   je     401154   
  110.   40113e:   48 8d 3d c3 0e 00 00    lea    0xec3(%rip),%rdi        # 402008 <_IO_stdin_used+0x8>  
  111.   401145:   e8 46 ff ff ff          callq  401090   
  112.   40114a:   bf 01 00 00 00          mov    $0x1,%edi  
  113.   40114f:   e8 7c ff ff ff          callq  4010d0   
  114.   401154:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)  
  115.   40115b:   eb 48                   jmp    4011a5   
  116.   40115d:   48 8b 45 e0             mov    -0x20(%rbp),%rax  
  117.   401161:   48 83 c0 10             add    $0x10,%rax  
  118.   401165:   48 8b 10                mov    (%rax),%rdx  
  119.   401168:   48 8b 45 e0             mov    -0x20(%rbp),%rax  
  120.   40116c:   48 83 c0 08             add    $0x8,%rax  
  121.   401170:   48 8b 00                mov    (%rax),%rax  
  122.   401173:   48 89 c6                mov    %rax,%rsi  
  123.   401176:   48 8d 3d b5 0e 00 00    lea    0xeb5(%rip),%rdi        # 402032 <_IO_stdin_used+0x32>  
  124.   40117d:   b8 00 00 00 00          mov    $0x0,%eax  
  125.   401182:   e8 19 ff ff ff          callq  4010a0   
  126.   401187:   48 8b 45 e0             mov    -0x20(%rbp),%rax  
  127.   40118b:   48 83 c0 18             add    $0x18,%rax  
  128.   40118f:   48 8b 00                mov    (%rax),%rax  
  129.   401192:   48 89 c7                mov    %rax,%rdi  
  130.   401195:   e8 26 ff ff ff          callq  4010c0   
  131.   40119a:   89 c7                   mov    %eax,%edi  
  132.   40119c:   e8 3f ff ff ff          callq  4010e0   
  133.   4011a1:   83 45 fc 01             addl   $0x1,-0x4(%rbp)  
  134.   4011a5:   83 7d fc 07             cmpl   $0x7,-0x4(%rbp)  
  135.   4011a9:   7e b2                   jle    40115d   
  136.   4011ab:   e8 00 ff ff ff          callq  4010b0   
  137.   4011b0:   b8 00 00 00 00          mov    $0x0,%eax  
  138.   4011b5:   c9                      leaveq   
  139.   4011b6:   c3                      retq     
  140.   4011b7:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)  
  141.   4011be:   00 00   
  142.   
  143. 00000000004011c0 <__libc_csu_init>:  
  144.   4011c0:   f3 0f 1e fa             endbr64   
  145.   4011c4:   41 57                   push   %r15  
  146.   4011c6:   4c 8d 3d 83 2c 00 00    lea    0x2c83(%rip),%r15        # 403e50 <_DYNAMIC>  
  147.   4011cd:   41 56                   push   %r14  
  148.   4011cf:   49 89 d6                mov    %rdx,%r14  
  149.   4011d2:   41 55                   push   %r13  
  150.   4011d4:   49 89 f5                mov    %rsi,%r13  
  151.   4011d7:   41 54                   push   %r12  
  152.   4011d9:   41 89 fc                mov    %edi,%r12d  
  153.   4011dc:   55                      push   %rbp  
  154.   4011dd:   48 8d 2d 6c 2c 00 00    lea    0x2c6c(%rip),%rbp        # 403e50 <_DYNAMIC>  
  155.   4011e4:   53                      push   %rbx  
  156.   4011e5:   4c 29 fd                sub    %r15,%rbp  
  157.   4011e8:   48 83 ec 08             sub    $0x8,%rsp  
  158.   4011ec:   e8 0f fe ff ff          callq  401000 <_init>  
  159.   4011f1:   48 c1 fd 03             sar    $0x3,%rbp  
  160.   4011f5:   74 1f                   je     401216 <__libc_csu_init+0x56>  
  161.   4011f7:   31 db                   xor    %ebx,%ebx  
  162.   4011f9:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)  
  163.   401200:   4c 89 f2                mov    %r14,%rdx  
  164.   401203:   4c 89 ee                mov    %r13,%rsi  
  165.   401206:   44 89 e7                mov    %r12d,%edi  
  166.   401209:   41 ff 14 df             callq  *(%r15,%rbx,8)  
  167.   40120d:   48 83 c3 01             add    $0x1,%rbx  
  168.   401211:   48 39 dd                cmp    %rbx,%rbp  
  169.   401214:   75 ea                   jne    401200 <__libc_csu_init+0x40>  
  170.   401216:   48 83 c4 08             add    $0x8,%rsp  
  171.   40121a:   5b                      pop    %rbx  
  172.   40121b:   5d                      pop    %rbp  
  173.   40121c:   41 5c                   pop    %r12  
  174.   40121e:   41 5d                   pop    %r13  
  175.   401220:   41 5e                   pop    %r14  
  176.   401222:   41 5f                   pop    %r15  
  177.   401224:   c3                      retq     
  178.   401225:   66 66 2e 0f 1f 84 00    data16 nopw %cs:0x0(%rax,%rax,1)  
  179.   40122c:   00 00 00 00   
  180.   
  181. 0000000000401230 <__libc_csu_fini>:  
  182.   401230:   f3 0f 1e fa             endbr64   
  183.   401234:   c3                      retq     
  184.   
  185. Disassembly of section .fini:  
  186.   
  187. 0000000000401238 <_fini>:  
  188.   401238:   f3 0f 1e fa             endbr64   
  189.   40123c:   48 83 ec 08             sub    $0x8,%rsp  
  190.   401240:   48 83 c4 08             add    $0x8,%rsp  
  191.   401244:   c3                      retq     

5.5.1 hello与hello.o的不同

hello反汇编结果比hello.o多了许多文件节。如.init节和.plt节,hello.o反汇编得到的代码中只有.text

hello反汇编

hello.o反汇编

hello反汇编文件中的地址是虚拟地址,而hello.o反汇编节中的是相对偏移地址

hello反汇编

hello.o反汇编

hello的反汇编中增加了许多外部链接的共享库函数。如puts@plt共享库函数,printf@plt共享库函数以及getchar@plt函数等如下图:

图55

5.5.2链接过程

查看hello.o的重定位节可知,链接过程中下列偏移量位置需要进行重定位。

图56

5.5.3分析hello,hello.o重定位

①变量重定位:例如main函数偏移量为0x16位置的变量调用,重定位后索引地址为绝对寻址,地址为.rodata节首地址-4,查看hello的ELF格式知对应地址为0x402004。

图57

5.6 hello的执行流程

使用edb执行hello,说明从加载hello到_start,到call main,以及程序终止的所有过程。请列出其调用与跳转的各个子程序名或程序地址。

子程序名

程序地址

正确输入时调用与跳转

hello!_start

00000000004010f0

hello!__libc_csu_init

00000000004011c0

hello!_init

0000000000401000

hello!main

0000000000401125

hello!printf@plt

00000000004010a0

hello!atoi@plt

00000000004010c0

hello!sleep@plt

00000000004010e0

hello!getchar@plt

00000000004010b0

hello!_fini

0000000000401238

hello!_start

00000000004010f0

错误输入时调用与跳转

hello!__libc_csu_fini

0000000000401230

hello!_init

0000000000401000

hello!main

0000000000401125

hello!puts@plt

0000000000401090

hello!exit@plt

00000000004010d0

hello!_fini

0000000000401238

5.7 Hello的动态链接分析

   

分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的内容变化。要截图标识说明。

在程序中动态链接是通过延迟绑定来实现的,延迟绑定的实现依赖全局偏移量表GOT和过程连接表PLT实现。GOT是数据段的一部分,PLT是代码段的一部分。编译系统将过程地址的绑定推迟到第一次调用该过程时。他通过GOT和过程链接表PLT的协作来解析函数的地址。在加载时,动态链接器会重定位GOT中的每个条目,使它包含正确的绝对地址,而PLT中的每个函数负责调用不同函数。

dl_init调用之前,对于每一条PIC函数调用,调用的目标地址都实际指向PLT中的代码逻辑,GOT存放的是PLT中函数调用指令的下一条指令地址。

图58

如图,GOT起始位置0x404000

0x404000后面的16个字节的变化包含了动态链接器在解析函数地址时会使用的信息,包括动态链接器在ld-linux.so模式中的入口地址。

调用前:

图59 调用前

图60 调用后

5.8 本章小结

本章主要介绍了链接的概念和作用,以及生成链接的命令,分析了helloelf格式文件,同时也分析了hello的虚拟地址空间以及重定位过程,遍历了整个hello的执行过程,并且比较了hello.o的反汇编和hello的反汇编。

(第5章1分)


6hello进程管理

6.1 进程的概念与作用

进程:一个执行中的程序的实例,每一个进程都有它自己的地址空间,一般情况下,包括文本区域、数据区域、和堆栈。文本区域存储处理器执行的代码,数据区域存储变量和进程执行期间使用的动态分配的内存,堆栈区域存储区着活动过程调用的指令和本地变量。
作用:进程为用户提供了以下假象:
(1) 我们的程序好像是系统中当前运行的唯一程序一样,我们的程序好像是独占的使用处理器和内存。
(2) 处理器好像是无间断的执行我们程序中的指令,我们程序中的代码和数据好像是系统内存中唯一的对象。

6.2 简述壳Shell-bash的作用与处理流程

Shell-bash的作用:是一种交互型的应用级程序,是Linux的外壳,提供了一个界面,用户可以通过这界面访问操作系统内核。
处理流程:
1、从终端读入用户输入的命令
2、将输入字符串切分获得所有的参数
3、如果是内置命令则立即执行
4、否则则调用fork()创建新子进程,再调用execve()执行指定程序

6.3 Hello的fork进程创建过程

打开shell,使用 ./hello 7203610609 宋浩 1的命令来运行hello程序。

图61

由于我们输入的不是一条内置命令,因此shell会调用fork函数创建一个子进程。这样,我们的hello子进程就被创建了。

6.4 Hello的execve过程

fork生成子进程后,子进程根据传入的命令行参数调用execve函数加载并运行hello程序。加载器删除子进程现有的虚拟内存段,并创建一组新的代码、数据、堆和栈段。新的栈和堆段被初始化为零,通过将虚拟地址空间中的页映射到可执行文件的页大小的片,新的代码和数据段被初始化为可执行文件中的内容。最后加载器设置PC指向_start地址,_start最终调用hello中的main函数。加载器创建的内存映像如下图所示。

图62

6.5 Hello的进程执行

结合进程上下文信息、进程时间片,阐述进程调度的过程,用户态与核心态转换等等。

上下文信息:上下文就是内核重新启动一个被抢占的进程所需要的状态,它由通用寄存器、浮点寄存器、程序计数器、用户栈、状态寄存器、内核栈和各种内核数据结构等对象的值构成。

时间片:一个进程执行它的控制流的一部分的每一时间段叫做时间片

用户模式和内核模式:处理器通常使用一个寄存器提供两种模式的区分,该寄存器描述了进程当前享有的权限,当没有设置模式位时,进程就处于用户模式中,用户模式的进程不允许执行特权指令,也不允许直接引用地址空间中内核区内的代码和数据;设置模式位时,进程处于内核模式,该进程可以执行指令集中的任何命令,并且可以访问系统中的任何内存位置

为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换。进程上下文切换由以下4个步骤组成:

决定是否作上下文切换,包括对进程调度原因的检查分析,以及当前执行进程的资格和CPU执行方式的检查等

保存当前执行进程的上下文

使用进程调度算法,选择一处于就绪状态的进程

恢复或装配所选进程的上下文,将CPU控制权交到所选进程手中

6.6 hello的异常与信号处理

 hello执行过程中会出现哪几类异常,会产生哪些信号,又怎么处理的。

 程序运行过程中可以按键盘,如不停乱按,包括回车,Ctrl-Z,Ctrl-C等,Ctrl-z后可以运行ps  jobs  pstree  fg  kill 等命令,请分别给出各命令及运行结截屏,说明异常与信号的处理。

  1. 可能出现的异常:
    (1)中断:来自I/O设备的信号。比如输入CTRL -C或者CTRL-Z
    (2)陷阱:有意的异常,是执行一条指令的结果,调用后也会返回到下一条指令,用来调用内核的服务进行操作。
    (3)故障:是由错误情况引起的,它可能能够被故障处理程序修正。如果修正成功,则将控制返回到引起故障的指令,否则将终止程序。
    (4)终止:是不可恢复的致命错误造成的结果,通常是一些硬件的错误,处理程序会将控制返回给一个abort例程,该例程会终止这个应用程序。
  2. 可能产生的信号:SIGINT,SIGSTP,SIGCONT,SIGWINCH等等。常见信号种类如下表所示。

ID

名称

默认行为

相应事件

2

SIGINT

终止

来自键盘的中断

9

SIGKILL

终止

杀死程序(该信号不能被捕获不能被忽略)

11

SIGSEGV

终止

无效的内存引用(段故障)

14

SIGALRM

终止

来自alarm函数的定时器信号

17

SIGCHLD

忽略

一个子进程停止或者终止

表1

  1. 各类处理

①运行中按回车:

图63

②运行中按ctrl-z

图64

Shell进程收到SIGSTP信号,Shell显示屏幕提示信息并挂起hello进程。

③运行中按ctrl-c

图65

Shell进程收到SIGINT信号,Shell结束并回收hello进程。

④运行中按其他非信号字符(不停乱按):输入缓存到stdin输入回车符则会多打印一行提示输入。

图66

psjobs命令查看,可以发现hello进程确实被挂起而非被回收,且其job代号为1

图67

图68

图69

图70

输入fg 1则命令将hello进程再次调到前台执行

图71

6.7本章小结

本章介绍了进程的概念与作用,简要说明了shell的作用和处理流程,介绍了hello程序的进程创建、加载、进程执行以及各种异常与信号处理的相关内容。异常控制流发生在计算机系统的各个层次,是计算机系统中提供并发的基本机制。在这一过程中加深了对信号的理解。

(第6章1分)


7hello的存储管理

7.1 hello的存储器地址空间

逻辑地址:源代码经过预处理、编译、汇编后出现在汇编程序中地址,包含在机器语言中用来指定一个操作数或一条指令的地址。每一个逻辑地址都由一个段和偏移量组成,偏移量指明了从段开始的地方到实际地址之间的距离。

线性地址:地址空间是一个非负整数的有序集合,如果地址空间中的整数是连续的,那么我们说它是一个线性地址空间。线性地址就是线性地址空间中的地址。是hello中的虚拟内存地址。

虚拟地址:在采用虚拟内存的系统中,CPU从一个有N = 2n个地址的地址空间中生成虚拟地址,这个地址空间称为虚拟地址空间,里面的地址就是虚拟地址。

物理地址:CPU通过地址总线的寻址,找到真实的物理内存对应地址。 CPU对内存的访问是通过连接着CPU和北桥芯片的前端总线来完成的。在前端总线上传输的内存地址都是物理内存地址。

7.2 Intel逻辑地址到线性地址的变换-段式管理

一个逻辑地址由两部分组成,段标识符和段内偏移量。段标识符是由一个16位长的字段组成,称为段选择符。其中前13位是一个索引号。后面3位包含一些硬件细节。可以通过段标识符的前13位,直接在段描述符表中找到一个具体的段描述符,这个描述符就描述了一个段。一些全局的段描述符,就放在"全局段描述符表(GDT)"中,一些局部的段描述符,就放在所谓的"局部段描述符表(LDT)"中。

7.3 Hello的线性地址到物理地址的变换-页式管理

CPU的页式内存管理单元,负责把一个线性地址,最终翻译为一个物理地址。线性地址被分为以固定长度为单位的组,称为页(page),通过页表与物理页进行映射,页表中的每一个项就是一个地址一一对应的页的地址。物理页(页桢)是分页单元把所有的物理内存也划分为固定长度的管理单位,它的长度一般与内存页是一一对应的。

7.4 TLB与四级页表支持下的VA到PA的变换

Core i7 MMU 使用四级的页表将虚拟地址翻译成物理地址。36VPN 被划分成四个VPN,分别用于一个页表的偏移量。

7.5 三级Cache支持下的物理内存访问

首先CPU发出一个虚拟地址,在TLB里面寻找。如果命中,那么将PTE发送给L1Cache,否则先在页表中更新PTE。然后再进行L1根据PTE寻找物理地址,检测是否命中的工作。这样就能完成CacheTLB的配合工作。

7.6 hello进程fork时的内存映射

fork函数被shell进程调用时,内核为新进程创建各种数据结构,并分配给它一个唯一的PID,为了给这个新进程创建虚拟内存,它创建了当前进程的mm_struct、区域结构和页表的原样副本。它将这两个进程的每个页面都标记为只读,并将两个进程中的每个区域结构都标记为私有的写时复制。

7.7 hello进程execve时的内存映射

加载并运行hello需要以下几个步骤:

删除已存在的用户区域,删除当前进程虚拟地址的用户部分中的已存在的区域结构。

映射私有区域,为新程序的代码、数据、bss和栈区域创建新的区域结构,所有这些新的区域都是私有的、写时复制的。代码和数据区域被映射为hello文件中的.text.data区,bss区域是请求二进制零的,映射到匿名文件,其大小包含在hello中,栈和堆地址也是请求二进制零的,初始长度为零

映射共享区域, hello程序与共享对象libc.so链接,libc.so是动态链接到这个程序中的,然后再映射到用户虚拟地址空间中的共享区域内

设置程序计数器(PC),execve做的最后一件事情就是设置当前进程上下文的程序计数器,使之指向代码区域的入口点

7.8 缺页故障与缺页中断处理

缺页故障当寄存器要访问虚拟内存中的某一页,而这个页又没有加载到内存中时,就会触发一个缺页故障。

缺页中断处理:

1.段错误:首先判断这个缺页的虚拟地址是否合法,遍历所有的合法区域结构,如果这个虚拟地址对所有的区域结构都无法匹配,就返回一个段错误

2.非法访问:查看地址的权限,判断一下进程是否有读写改这个地址的权限

3.如果不是上面两种情况那就是正常缺页,就选择一个页面换入新的页面并更新到页表

7.9动态存储分配管理

通过维护虚拟内存(堆),一种是隐式空闲链表,一种是显式空闲链表。显式空闲链表法是malloc(size_t size)每次声明内存空间都保证至少分配size_t大小的内存,双字对齐,每次必须从空闲块中分配空间,在申请空间时将空闲的空间碎片合并,以尽量减少浪费

Printf会调用malloc,请简述动态内存管理的基本方法与策略。

7.10本章小结

概述了存储器的地址空间,讲述了虚拟地址、物理地址、线性地址、逻辑地址的概念以及进程forkexecve的内存映射的内容。描述了系统应对缺页异常的方法和malloc的内存分配管理机制。

(第7章 2分)


8hello的IO管理

8.1 Linux的IO设备管理方法

一个Linux文件就是一个m个字节的序列:B0, B1, …, Bk, …, Bm-1

所有的I/O设备(例如网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当做对相应文件的读和写来执行,这种将设备映射为文件的方式,允许Linux内核引出一个简单低级的应用接口,称为Unix I/O

设备的模型化:文件

设备管理:unix io接口

8.2 简述Unix IO接口及其函数

linux 提供如下 IO 接口函数:

read  write–最简单的读写函数;

readn 和 writen–原子性读写操作;

recvfrom 和 sendto–增加了目标地址和地址结构长度的参数;

recv 和 send–允许从进程到内核传递标志;

readv writev–允许指定往其中输入数据或从其中输出数据的缓冲区;

recvmsg sendmsg–结合了其他IO函数的所有特性,并具备接受和发送辅助数据的能力。

8.3 printf的实现分析

printf函数代码如下所示:

int printf(const char fmt, …)

{

int i;

char buf[256];

va_list arg = (va_list)((char)(&fmt) + 4);

i = vsprintf(buf, fmt, arg);

write(buf, i);

return i;

}

(char*)(&fmt) + 4) 表示的是…可变参数中的第一个参数的地址。而vsprintf的作用就是格式化。它接受确定输出格式的格式字符串fmt。用格式字符串对个数变化的参数进行格式化,产生格式化输出。接着从vsprintf生成显示信息,到write系统函数,直到陷阱系统调用 int 0x80或syscall。字符显示驱动子程序:从ASCII到字模库到显示vram(存储每一个点的RGB颜色信息)。显示芯片按照刷新频率逐行读取vram,并通过信号线向液晶显示器传输每一个点(RGB分量)。

8.4 getchar的实现分析

异步异常-键盘中断的处理:键盘中断处理子程序。接受按键扫描码转成ascii码,保存到系统的键盘缓冲区。然后getchar等调用read系统函数,通过系统调用读取按键ascii码,直到接受到回车键才返回。

8.5本章小结

本章通过介绍hello中包含的函数所对应的unix I/O,大致了解了I/O接口及其工作方式,同时也了解了硬件设备的使用和管理的技术方法。

(第8章1分)

结论

用计算机系统的语言,逐条总结hello所经历的过程。

你对计算机系统的设计与实现的深切感悟,你的创新理念,如新的设计与实现方法。

Ⅰ.hello所经历过程

①预处理

②编译

③汇编

④链接

⑤加载运行

⑥执行指令

⑦访存

⑧动态申请内存

⑨信号处理

Ⅱ.感悟

如此庞大的计算机系统底层却是由01组成,在惊叹科学知识严谨周密的同时,也佩服于科学工作者的智慧。

这门课程使我们受益匪浅,掌握计算机的底层构造应成为一种学科素养。任何功能都建立在底层之上,顶层更偏向应用,底层逻辑才是学术之本。

(结论0分,缺失 -1分,根据内容酌情加分)


附件

列出所有的中间产物的文件名,并予以说明起作用。

中间结果文件名字

文件作用

hello.i

预处理得到的中间结果

hello.s

hello.i编译后得到的汇编语言文本文件

hello.o

hello.s汇编后得到的可重定位目标文件

hello.out

链接后得到的可执行目标文件

hello

可执行文件

helloelf.txt

hello.o的ELF格式文本

helloELF.txt

hello的ELF格式文本

asmhello.txt

hello.o的反汇编文本

asmhelloexe.txt

hello的反汇编文本

表2

(附件0分,缺失 -1分)


参考文献

为完成本次大作业你翻阅的书籍与网站等

[1]  https://blog.csdn.net/qq_46470984/article/details/110885751

[2]  https://blog.csdn.net/qq_39286580/article/details/106496837

[3] 预处理与编译 - noticeable - 博客园 (cnblogs.com)

[4]  编译_百度百科 (baidu.com)

[5]  Randy E.Bryant,深入理解计算机系统(第3版),机械工业出版社2021.5出版

[6]  ELF文件格式 - 知乎 (zhihu.com)

[7] (175条消息) ELF文件格式解析_mergerly的博客-CSDN博客_elf文件格式

(参考文献0分,缺失 -1分)

你可能感兴趣的:(c语言)