本论文通过hello.c程序,对在CSAPP课程中所学知识进行整理,在Ubuntu虚拟机Linux系统下进行所有操作,运用Linux系统的工具,分析hello程序的一生。
关键词: hello,计算机系统,linux,程序人生
第1章 概述
1.1 Hello简介
Hello的P2P过程:
在VSCode等编辑器中键入Hello代码得到hello.c程序;对程序进行预处理、编译、汇编、链接生成可执行目标程序;在shell中启动后,shell调用fork创建子进程;hello从Program转换为Process。
Hello的020过程:
然后shell为hello的execve映射虚拟内存,进入程序入口后开始载入物理内存;进入main函数执行目标代码,CPU为hello分配时间片以执行逻辑控制流;程序运行结束后,shell父进程回收hello进程,释放占用的内存,删除数据结构。
1.2 环境与工具
硬件环境
X64 CPU;1.6GHz;8G RAM;256G SSD Disk;1T HDD Disk
软件环境
Windows10 64位;Vmware 14pro;Ubuntu 20.04.2 LTS 64位
开发工具
Visual Studio Code 64位;vim/gpedit+gcc/as/ld/edb/readelf;
1.3 中间结果
文件的作用 | 文件名 |
---|---|
预处理后的文件 | hello.i |
编译之后的汇编文件 | hello.s |
汇编之后的可重定位目标文件 | hello.o |
链接之后的可执行目标文件 | hello |
hello.o 的 ELF格式 | elf.txt |
hello.o 的反汇编代码 | hello_o_disass.s |
hello的ELF 格式 | elf_hello.txt |
hello 的反汇编代码 | hello_disass.s |
1.4 本章小结
本章简要介绍了hello的P2P,020过程,并列出了大作业的软硬件环境及开发工具,还列出了操作过程中产生的中间结果。
第2章 预处理
2.1 预处理的概念与作用
预处理又称预编译,(对于c/c++来说)预处理指的是在程序编译之前,根据以字符#开头的命令(即头文件/define等),修改原始的c程序。
预处理的主要作用如下:
●将源文件中以"include"格式包含的文件复制到编译的源文件中。
●用实际值替换用"#define"定义的字符串。
●根据"#if"后面的条件决定需要编译的代码。
2.2在Ubuntu下预处理的命令
命令:gcc -E hello.c -o hello.i
2.3 Hello的预处理结果解析
程序扩展为3065行,hello源程序出现在3047行,#include <stdio.h> #include <stdlib.h> #include <unistd.h>三个头文件消失,替代的是一大段代码,描述的是运行库在计算机中的位置,方便下一步翻译成汇编语言。
2.4 本章小结
本章主要介绍了预处理的概念和应用功能,同时将hello.c文件进行预处理生成hello.i文本文件,并对生成的hello.i文件进行分析,详细了解了预处理的内涵。
第3章 编译
3.1 编译的概念与作用
编译是利用编译程序从源语言编写的源程序产生目标程序的过程,是用编译程序产生目标程序的动作,把高级语言变成计算机可以识别的2进制语言。
作用:
把用高级程序设计语言书写的源程序,翻译成等价的计算机汇编或机器语言书写的目标程序,编译程序以高级程序设计语言书写的源程序作为输入,而以汇编或机器语言表示的目标程序作为输出。
注意:这儿的编译是指从 .i 到 .s 即预处理后的文件到生成汇编语言程序
3.2 在Ubuntu下编译的命令
命令:gcc -S hello.i -o hello.s
3.3 Hello的编译结果解析
3.3.1 数据
- 常量
字符串:如下两个函数中的字符串被存储在 .rodata节中
- 变量
1) 全局变量
初始化的全局变量储存在.data节,sleepsecs全局变量被存放在.data节
2) 局部变量
局部变量存储在寄存器或栈中。程序中有局部变量 int i
在汇编代码中如下:
i被存储在栈中,-4(%rbp)的位置。
3.3.2 操作
- 算术操作
在循环操作中,使用 i++ 自增操作,每次循环结束后对 i 加1,对栈上存储i 的位置加1;
- 关系操作
1) 程序第16行中判断argc是否等于3,源代码为:
汇编代码为:
2) 程序第21行中判断 i 是否小于10,源代码为:
汇编代码为,汇编优化为 i <= 9:
- 控制转移
在使用比较关系操作进行判断后,程序将按判断结果经如下代码跳转至L2或L4,进入if语句或继续进入循环
- 数组/指针/结构操作
主函数main的参数中有指针数组char *argv[],argv[0]指向输入程序的路径和名称,argv[1]和argv[2]分别表示两个字符串。
- 函数操作
1) main函数
参数传递:传入参数argc,argv[],分别用寄存器%edi和%rsi存储
函数调用:被系统启动函数调用
函数返回:设置%eax为0,并返回
2) printf函数
i. call puts@PLT
参数传递:传入字符串参数首地址
函数调用:if判断满足条件后被调用
ii. call printf@PLT
参数传递:传入 argv[1]和argc[2]的地址
函数调用:for循环中被调用
3) exit函数
参数传递:传入参数1
<