题 目 程序人生-Hello’s P2P
专 业 人工智能
学 号 7203610116
班 级 2036015
学 生 陈九成
指 导 教 师 史先俊
本文将根据计算机系统课程的内容和计算机系统实验课程的实际操作进行分析hello小程序的一生,全面梳理和回顾我们所学。我们主要是Ubuntu进行相关操作,加深对计算机系统的了解。
hello;计算机系统;Linux;
目 录
1.1. Hello的简介... - 5 -
P2P.. - 5 -
020. - 5 -
1.2. 环境与工具... - 5 -
1.3. 中间结果... - 5 -
1.4. 本章小结... - 6 -
2.1 预处理的概念和作用... - 7 -
预处理的概念... - 7 -
预处理的作用... - 7 -
2.2 在Ubuntu下一个预处理命令... - 7 -
2.3 Hello的预处理结果解析... - 8 -
2.4 本章小结... - 11 -
3.1 编译的概念与作用... - 12 -
编译的概念... - 12 -
编译的作用... - 12 -
3.2 在Ubuntu下编译的命令... - 12 -
3.3 Hello的编译结果解析... - 13 -
3.3.1.数据... - 14 -
3.3.2. 算术运算... - 17 -
3.3.3关系运算与控制转移... - 19 -
3.3.4数组/指针/结构操作... - 19 -
3.3.5函数操作... - 20 -
3.4 本章小结... - 23 -
4.1 汇编的概念与作用... - 24 -
汇编的概念... - 24 -
汇编的作用... - 24 -
4.2 在Ubuntu下汇编的命令... - 24 -
4.3 可重定位目标elf格式... - 24 -
4.3.1生成elf文件命令... - 24 -
4.3.2 ELF头... - 25 -
4.3.3节头... - 26 -
4.3.4 重定位条目... - 26 -
4.3.5符号表... - 27 -
4.4 Hello.o的结果解析... - 28 -
4.5 本章小结... - 30 -
5.1 链接的概念与作用... - 31 -
链接的概念... - 31 -
链接的作用... - 31 -
5.2 在Ubuntu下链接的命令... - 31 -
5.3 可执行目标文件hello的格式... - 31 -
5.4 hello的虚拟地址空间... - 34 -
5.5 链接的重定位过程分析... - 35 -
5.6 hello的执行流程... - 37 -
5.7 Hello的动态链接分析... - 37 -
5.8 本章小结... - 38 -
6.1 进程的概念与作用... - 39 -
6.2 简述壳Shell-bash的作用与处理流程... - 39 -
6.3 Hello的fork进程创建过程... - 39 -
6.4 Hello的execve过程... - 40 -
6.5 Hello的进程执行... - 40 -
6.6 hello的异常与信号处理... - 41 -
6.7本章小结... - 43 -
7.1 hello的存储器地址空间... - 44 -
7.2 Intel逻辑地址到线性地址的变换-段式管理... - 44 -
7.3 Hello的线性地址到物理地址的变换-页式管理... - 45 -
7.4 TLB与四级页表支持下的VA到PA的变换... - 45 -
7.5 三级Cache支持下的物理内存访问... - 45 -
7.6 hello进程fork时的内存映射... - 46 -
7.7 hello进程execve时的内存映射... - 46 -
7.8 缺页故障与缺页中断处理... - 46 -
7.9... - 47 -
7.10本章小结... - 47 -
8.1 Linux的IO设备管理方法... - 48 -
8.2 简述Unix IO接口及其函数... - 48 -
8.3 printf的实现分析... - 49 -
8.4 getchar的实现分析... - 50 -
8.5本章小结... - 50 -
第1章 概述
根据Hello的自白,利用计算机系统的术语,简述Hello的P2P,020的整个过程。
P2P
P2P就是 From Program to Process,在Linux中,我们编写的代码程序,就先记为hello.c叭,就是P2P中的Program,而Process就是将源程序hello.c经过cpp的预处理、ccl的编译、as的汇编、ld的链接最终成为可执行目标程序hello,这一整套工序,统称为From Program to Process,即P2P。最终在shell中启动程序,然后fork生成子进程开始运行。
020
再经过P2P过程之后,shell为我们编写生成的hello可执行程序进行系统调用,映射虚拟内存,进入程序入口后程序开始载入物理内存,然后进入 main函数执行目标代码,CPU为运行的hello分配时间片执行逻辑控制流。当程序运行结束后,shell父进程负责回收hello进程,内核删除相关数据结构。
列出你为编写本论文,折腾Hello的整个过程中,使用的软硬件环境,以及开发与调试工具。
- 硬件环境:
CPU: AMD Ryzen7 4800HS With Radeon Graphic
16GB RAM 512GB SSD 64位操作系统
- 软件环境:Window10、VMware、Ubuntu18.04
- 开发与调试环境:
Window下的VScode、VS2019
Linux下的gdb、CodeBlocks、gcc、edb、vim、as、ld
列出你为编写本论文,生成的中间结果文件的名字,文件的作用等。
- hello:经过系统编译、汇编、链接生成的可执行文件
- hello.c:我们(用户)编写的源程序
- hello.i:将源程序进行预处理后生产的文本文件
- hello.s: 将预处理后文件编译生成的汇编文件
- hello.o: 汇编之后可重定向的目标文件
- hello.out: 跟hello一样都是hello.c的可执行程序
- elf.txt: hello.o的elf格式文件
- dis_hello.s: hello.o的反汇编代码内容
- hello.elf: hello的elf格式内容
- hello_objdump.s hello的反汇编代码
本章节对hell.c文件进行了一个总体的概括,这其实也适用于所有C语言程序乃至大部分高级语言程序。介绍了P2P、020过程及作用,说明了此报告在撰写时使用的硬件环境、软件环境与开发和调试工具,最后列举了从编写的.c文件到可执行文件过程中生成的中间文件。
第2章 预处理
预处理的概念,这就需要求助无所不知的百度 Google Bing了
预处理的概念
预处理为编译器做的预备工作阶段,在编译器处理程序之前预扫描源代码,完成头文件的包含,宏扩展,条件编译, 行控制(line control)等操作。预处理是文件在编译过程中一个单独的步骤。简言之,预处理进行的是一个文本替换操作,它们会指示编译器在实际编译之前完成所需的预处理。同时我们把 C 预处理器(C Preprocessor)简写为 CPP
预处理的作用
- 将源程序文件中的宏定义部分,比如define进行删除并用实际值进行替换
- 处理源程序中的库文件引用,当源程序带有#include时,预处理时会把包含的库文件插入到程序文件中
- 删除所有的注释,如:用//及/*包括的语句
- 添加行号和文件名标识,以便编译时编译器产生调试用的行号信息,保留所有#pragma编译指令(编译器需要用)
- 处理所有条件编译,比如ifdef和endif等
根据第一章所学内容,预处理命令为:
cpp hello.c > hello.i
图 1 预处理命令及结果展示
图2 hello.i文件部分代码
可以看出,经过预处理hello.c文件已经变为3105行内容,而其中绝大部分内容为插入的本地库函数和库文件, hello.c文件的内容从预处理文件的3092行开始一直到最后.由于hello.c文件没有define ifdef等指令,所以前面的内容是对于#include引用的库文件进行插入,以hello.c文件为例:
图 3 hello.i部分代码
hello.c 文件开头引用的库文件包括<stdio.h> <unistd.h> <stdlib.h>,首先Linux对hello.c 引用的库文件stdio.h进行寻找,找到如下路径
/usr/include/stdio.h
进入到这个路径内找到源文件后打开,stdio.h文件内容如下图所示
图4 stdio.h部分代码
可以发现,stdio.h文件中含有很多#define #ifdef #endif等定义,此时预处理也会对stdio.h文件中的宏定义进行处理,这也是为什么最终的hello.i文件是没有#denfine等内容.同时cpp会对stdio中的ifdef等进行判断是否执行,将执行部分保留,不执行部分直接删除,剩下的部分再与经过同样处理后的unistd.h stdlib.h文件部分进行连接排序最后就得到了我们预处理得到的 hello.i文件.
图5 hello.i中的声明函数部分
本章节主要介绍了预处理的概念及功能,包括头文件的展开、宏替换、去掉注释、条件编译等,以及Linxu下预处理的指令,同时以hello.c文件为例,查看预处理结果hello.i文本文件,并对其进行解析,详细了解了预处理的过程与功能。
第3章 编译
编译的概念
一般说的编译是说将某种编程语言写成的源代码(原始语言)转换成另一种编程语言(目标语言)。
在这里指的是把高级语言文本程序翻译成等价的汇编语言文本程序:编译器ccl把预处理后的文本文件.i文件进行一系列语法分析及优化后生成相应的汇编语言文件.s文件的过程。其中.s文件中包含一个汇编语言程序。
编译的作用
- 语法分析:编译程序的语法分析器以单词符号作为输入,分析单词符号串是否形成符合语法规则的语法单位
- 词法分析:词法分析的任务是对由字符组成的单词进行处理,从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号,把作为字符串的源程序改造成为单词符号串的中间程序。执行词法分析的程序称为词法分析程序或扫描器。
- 语义检查和中间代码生成:源程序的一种内部表示,或称中间语言。中间代码的作用是可使编译程序的结构在逻辑上更为简单明确,特别是可使目标代码的优化比较容易实现中间代码。
- 源代码优化:指对程序进行多种等价变换,使得从变换后的程序出发,能生成更有效的目标代码。
- 目标代码生成:生成是编译的最后一个阶段。目标代码生成器把语法分析后或优化后的中间代码变换成目标代码。此处指汇编语言代码,须经过汇编程序汇编后,成为可执行的机器语言代码
编译的命令为,如下图所示
gcc -S hello.i -o hello.s
图6 编译命令及结果展示
首先看hello.s中的内容都是什么,如下图所示
图7 hello.s部分代码
首先解析每行命令开头,主要分为一下几部分
- .file: 源文件名
- .text: 代码部分
- .data: 数据部分
- .global: 全局变量
- .align: 对齐方式
- .type: 指定是对象类型或是函数类型
- .size: 大小
- .long: 长整型
- .section .rodata:下面是.rodata节
3.3.1.数据
- 常量:
常量就是代码中的确定数值,比如hello.c文件里的4、8,这些都是人为给定的数值
图8 hello.c中的常量
而在hello.s中,这部分数据会放置在代码段,跟指令结合在一起
图9 hello.s中的常量
可以看出.c文件中的4就存储在代码段中,跟条件判断语句结合在一起。
而对于printf函数中的常量,即
图10 hello.c 中 printf scanf函数中的字符串常量
函数当中的字符串常量将存储在LC0和LC1中
.
图11 hello.s中的字符串常量所在位置
- 变量
- 全局变量global
初始化的全局变量储存在.data节,它的初始化不需要汇编语句,而是直接完成的。但是我们的hello.c没有全局变量,所以hello.s文件也就没有相应区域
- 局部变量
局部变量通常会被分配到栈中(隶属于各自的进程,生命周期结束后会在栈上被释放),它没有标识符,也不需要被声明,而是直接使用。。比如hello.c中的int i;就是一个局部变量的定义,他在.s文件中的相应位置如下
图12 hello.s中局部变量int i的位置
此外,hello.c 还有局部变量int argc[],这是hello.c运行时传入的第一个参数,他会存储在寄存器edi中,表述输入的参数个数,而后利用mov语句赋值到-20(%rbp)中
图13 hello.s中int argc[]的部分
- 指针数组变量
在hello.c中的 char *argv[],根据数组在汇编中的位置,我们知道数组的首地址会存储在寄存器当中,根据数组下标来索引所需数组的位置。由下图可知,argv数组首地址存放在寄存器(%rbp-32)中,通过改变偏移量大小来获取argv的各个数组值,比如首地址+8就得到argv[1]的地址。同理,+16得到了argv[2]的地址。
图14 hello.s中指针型数组位置
比如在hello.c中对i = 0的赋值
图15 hello.s中赋值操作
可以发现,对于局部变量的赋值,是通过mov语句实现的
- 算术运算
在hello.c中的循环操作中,有i++的操作,这在hello.s文件中是通过add语句实现的
图16 hello.s中的i++操作汇编实现
除此之外,汇编中还有其他的算术操作,比如在进行数组的索引时,也会进行算术运算,如3.3.1所言,数组是通过改变偏移量来寻址的,即通过argv[i] = argv[0]+8*i来得到argv[i]的地址。
图17 hello.s中数组寻址的算术操作
3.3.3关系运算与控制转移
图18 hello.c中的关系运算
由上图可知,hello.c中的关系运算 “!=”,在hello.s中的汇编语言中,会以cmpl等形式出现
图19 hello.s中的关系运算
此外,hello.c中在循环运算中也有关系判断,如下图所示
图20 hello.c中循环部分的关系运算
而在汇编语言中,也会以cmpl的形式出现,如下图所示
图21 hello.s中循环部分的关系运算
其中cmpl判断后面的jxx语句用来根据判断结果来决定跳转的位置,也就是控制转移。语句jxx中的xx就是判断条件,根据上条指令的结果和标志运算符的结果来决定是否跳转
3.3.4数组/指针/结构操作
hello.c中有个指针数组char *argv[],作为main函数的一个输入参数, argv[0]表示输入程序的路径和名称,argv[1]和argv[2]分别表示两个字符串。
图22 hello.c中的指针数组
char *的数据类型占用8个字节,所以在进行数组操作时,每个数组地址偏移量差8
图23 hello.s中的指针数组argv[]
可以看出argv的首地址,即argv[0]先是存储在%rsi中,再通过偏移量+8 +16来获得argv[1]和argv[2]
图24 hello.s中argv[1]和argv[2]
3.3.5函数操作
经过本学期的学习,我们知道程序中的函数调用一般通过跳转到特定代码执行待定函数之后再返回来实现的,在计算机系统当中函数主要是在堆栈中实现的。函数操作主要分为以下几部分:
在x86 64位的操作系统下,由于寄存器比较充足,因此函数传递参数通常使用寄存器来完成该功能的。而当寄存器不足的时候(或者参数太多),系统可以像32位下一样借用堆栈来传递参数。先按照:%rdi,%rsi,%rdx,%rcx,%r8,%r9的顺序传递参数,从第七个参数开始放在调用者栈结构中。
- c
call指令会将返回地址压入栈中,并且将rip的值指向所调用函数的地址,等函数执行完之后调用ret弹出原来的rip并且将栈帧结构恢复
函数在自己栈帧内进行操作,返回值存入RAX寄存器。
函数返回时,如果有返回值,一般是返回寄存器%rax的值,即将返回值存在%rax中,再用leave和ret等操作返回,控制权还给调用函数
下面我们深入到hello.c文件中,看一看hello.c文件的函数调用,根据hello.c的文件内容,可以发现,hello.c文件调用的函数有
main() printf() exit() sleep() atoi() getchar() 这六个,下面我们逐个分析这些函数
参数传递:传入参数为int argc[]和char *argv[],分别用寄存器%rdi和%rsi存储
函数调用:作为程序的主函数,在系统启动时就会被调用
函数操作:作为主函数, main函数使用栈指针,同时使用栈帧%rbp来记录使用情况。
函数返回:根据源程序的return 0相对应的汇编为设置%eax为0并且返回,对应return 0
图25 main函数操作的汇编
由图可知,函数传递的参数argc和argv分别存储在%edi和%rsi中,最终%eax赋值为0函数退出
图26 main函数的函数返回
hello.c中引用了两个printf,在如下图位置:
图27 hello.c中的printf
参数传递:call puts时只传入了字符串参数首地址;for循环中call printf时传入了argv[1]和argc[2]的地址。
函数调用:if判断满足条件后调用,与for循环中被调用。
图28 hello.s中的printf调用
函数返回:由上图可以看出,printf的函数返回应该是在printf函数中完成的。
参数传递:由下图及源函数,exit的函数传递参数为1,汇编中将%edi寄存器赋值为1,进行传递
函数调用:由if条件判断后,如果不跳转,则由call进行调用exit函数
函数返回:同样在exit函数中返回
图29 hello.s中exit函数调用
参数传递:源程序中,sleep函数传递的参数为atoi函数的返回值,即atoi(argv[3]),在汇编之中,atoi的返回值存储在%eax寄存器当中,再通过mov指令赋值到edi寄存器当中实现sleep函数参数的传递
函数调用: 通过call exit@PLT函数,进行函数调用
函数返回:在sleep函数中返回
图30 sleep的函数调用
参数传递:汇编中,将argv[3]的值通过寄存器%rdi来传递给atoi函数
函数调用: 通过call atoi@PLT函数,进行函数调用。
函数返回:在atoi函数中返回
图31 atoi的函数调用
参数传递:getchar()是个无参数传递的函数
函数调用: 通过call getchar@PLT调用getchar;
函数返回:在getchar中返回
图32 getchar函数的调用
本章主要介绍了编译的概念以及过程。同时通过示例程序hello.c以及hello.s表现了c语言转换成为汇编的形式。介绍了汇编代码如何实现各种类型,包括变量、常量、函数调用、分支和循环控制、算术运算与关系判断。编译所做的工作,就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码表示。包括之前对编译的结果进行解析,都令我更深刻地理解了C语言的数据与操作,对C语言翻译成汇编语言有了更好的掌握,也相当于掌握了一定的汇编语言。
第4章 汇编
4.1 汇编的概念与作用
汇编的概念
汇编器(as)将.s汇编程序翻译成机器语言,把这些机器语言指令打包成可重定位目标程序的格式,并将结果保存在.o目标文件中,这个过程就叫做汇编.
汇编的作用
将编译之后的汇编语言汇编为机器语言,机器语言才是机器真正能直接识别并执行程序的语言,所以生成的.o文件是二进制文件,包含着程序包含的指令代码
4.2 在Ubuntu下汇编的命令
在Linux下的汇编指令为
as hello.s -o hello.o
图33 汇编指令及结果如图
4.3 可重定位目标elf格式
4.3.1生成elf文件命令
命令为:
readelf -a hello.o > ./elf.txt
图34 elf文件生成命令
4.3.2 ELF头
ELF头将提供Magic,类别,数据,版本,OS/ABI,ABI,ELF头的大小、目标文件的类型、机器类型、字节头部表(section header table)的文件偏移,以及节头部表中条目的大小和数量等信息.ELF头的具体内容如下图所示:
图35 elf头
4.3.3节头
节头部表描述了在hello.o文件中不同节的位置和大小,其中目标文件中每个节都有一个固定大小的条目。具体的描述包括节的名称、类型、地址和偏移量等,如下图所示:
图 36 elf节头内容
4.3.4 重定位条目
当汇编器生成一个目标模块时,程序并不知道数据和代码最终将放在内存中的什么位置,它也不知道这个模块引用的任何外部定义的函数或者全局变量的位置。于是它就会生成一个重定位条目,用于告诉链接器在将目标文件合并成可执行目标文件时如何修改这个引用。在链接时,重定位节也会对这些位置的地址进行修改。链接器会通过重定位条目的类型判断该使用什么养的方法计算正确的地址值,通过偏移量等信息计算出正确的地址。其中代码的重定位条目放在.rel.text中,已初始化数据的重定位条目放在.rel.data中。重定位部分具体代码如下图所示:
图37 elf的重定位部分
由上图可以看出,给的例程hello.c中需要重定位的信息有:.rodata中的模式串,puts,exit,printf,slepsecs,sleep,getchar。
4.3.5符号表
符号表,用于存放在程序中定义和引用的函数和全局变量的信息。具体内容如下图所示:
图37 elf符号表内容
4.4 Hello.o的结果解析
要想知道hello.o的结果,首先要进行反汇编查看,命令为:
objdump -d -r hello.o > dis_hello.s
这样就可以查看生成的dis_hello.s反汇编文件来进行解析,dis_hello.s的具体内容如下:
图38 dis_hello.s代码
下面将hello.o反汇编生成的dis_hello.s与第三章编译之后生成的hello.s文件进行对比,有如下不同:
- 操作数不同
在hello.s中数进制都是10进制而反汇编生成的dis_hello.s都是16进制,数值均是0x开头
图39 反汇编的操作数
- 分支跳转控制不同
在hello.s中,跳转为L2 L4 区段,包括整个hello.s文件都分为了LC0 LC1等部分
而在反汇编生成的dis_hello.s中,则用相应的地址代替
图40 dis_hello.s中的分支跳转控制
- 函数调用不同:
hello.s中,call指令使用的是函数名称,而反汇编代码中call指令使用的是main函数的相对偏移地址。因为函数只有在链接之后才能确定运行执行的地址,因此在.rela.text节中为其添加了重定位条目。
图41 dis_hello.s中的函数调用
通过比较可以发现,反汇编生成的汇编语言更贴合Linux的机器语言
4.5 本章小结
本章对汇编结果进行了详尽的介绍。经过汇编器的操作,汇编语言转化为机器语言,hello.o可重定位目标文件的生成为后面的链接做了准备。通过对比hello.s和hello.o反汇编代码生成的文件dis_hello.s的区别,使得我们对该内容有了更加深入地理解,更深刻地理解了汇编语言到机器语言实现地转变,和这过程中为链接做出的准备,对可重定位目标elf格式进行了详细的分析,侧重点在重定位项目上
第5章 链接
5.1 链接的概念与作用
链接的概念
链接是将各种不同文件的代码和数据部分收集(符号解析和重定位)起来并组合成一个单一文件的过程。
链接的作用
当程序调用函数库(如标准C库)中的一个函数,这个函数存在于一个名为printf.o的单独的预编译好了的目标文件中,而这个函数必须通过链接器(ld)将这个文件合并到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
图42 链接的命令
5.3 可执行目标文件hello的格式
命令:readelf -a hello > hello.elf,此命令输入后可得到hello.elf文件
根据hello.elf的内容,可以看出
ELF头为:
图43 hello.elf的ELF头内容
节头:
图44 hello.elf中的节头内容
符号表
图45 符号表的内容
5.4 hello的虚拟地址空间
使用edb加载hello,查看Data Dump窗口可以看到hello程序的虚拟地址空间各段信息。
图46 用edb加载hello查看虚拟内存
可以看出程序的虚拟内存地址从0x400000 – 0x401000
5.5 链接的重定位过程分析
命令: objdump -d -r hello > hello_objdump.s
图47 objdump内容
1.链接增加了新函数:
在hello中链接加入了在hello.c中用到的库函数,如exit、printf、sleep、getchar等函数。
2.增加了节:
hello中增加了.init和.plt节,和一些节中定义的函数。
3.函数调用:
hello中无hello.o中的重定位条目,并且跳转和函数调用的地址在hello中都变成了虚拟内存地址。对于hello.o的反汇编代码,函数只有在链接之后才能确定运行执行的地址,因此在.rela.text节中为其添加了重定位条目。
4.地址访问:
hello.o中的相对偏移地址变成了hello中的虚拟内存地址。而hello.o文件中对于某些地址的定位是不明确的,其地址也是在运行时确定的,因此访问也需要重定位,在汇编成机器语言时,将操作数全部置为0,并且添加重定位条目。
链接就是链接器(ld)将各个目标文件(各种.o文件)组装在一起,文件中的各个函数段按照一定规则累积在一起。
5.6 hello的执行流程
使用edb执行hello,从加载hello到_start,到call main,以及程序终止的主要过程如下:
- ld-2.27.so!_dl_start 7efb ff4d8ea0
- ld-27.so!_dl_init 7efb ff4e7630
- hello!_start 400500
- libc-2.27.so!__libc_start_main 7efb ff100ab0
- hello!puts@plt 4004b0
- hello!exit@plt 4004e0
5.7 Hello的动态链接分析
分析hello程序的动态链接项目,通过edb调试,分析在dl_init前后,这些项目的 内容变化。要截图标识说明。
在elf文件中可以找到:
进入edb查看地址,可以发现
对于变量而言,我们利用代码段和数据段的相对位置不变的原则计算正确地址。对于库函数而言,需要plt、got合作,plt初始存的是一批代码,它们跳转到got所指示的位置,然后调用链接器。初始时got里面存的都是plt的第二条指令,随后链接器修改got,下一次再调用plt时,指向的就是正确的内存地址。plt就能跳转到正确的区域。
5.8 本章小结
本章结合实验中的hello可执行程序依此介绍了链接的概念及作用,在Ubuntu下链接的命令行;并对hello的elf格式进行了详细的分析对比,同时注意到了hello的虚拟地址空间知识;并通过反汇编hello文件,将其与hello.o反汇编文件对比,详细了解了重定位过程;遍历了整个hello的执行过程,在最后对hello进行了动态链接分析。
第6章 hello进程管理
6.1 进程的概念与作用
: