Intro:
- 绪论2:应用视角的操作系统。
- 目的:通过精简程序,了解整个编译过程。
一、GCC 编译 hello.c 的全过程
.c
(源代码) ->.i
(预编译源代码) -gcc->.S
(汇编代码) -as->.o
-ld->a.out
(一)GCC 编译
hello.c
中只有一行 printf("hello,world\n");
指令(return 由编译器处理,代码中便没有指定)。于是根据上面的编译全流程,使用:
gcc -E hello.c | less # 查看预编译结果
gcc -c hello.c # 生成 hello.o
ld hello.o # 强行链接
objdump -d a.out | less # 查看可执行文件的内容
因为我们的代码中使用了 printf
这样一个外部依赖函数,所以 LD 会报错。如果去掉 printf
这一行则报错消失,并使用 ld hello.o -e main
指定程序的 entrypoint 来消除警告。
(二)GDB 调试
执行 ./a.out
会报错,接下来使用 GDB 单步调试。
gdb a.out # 进入调试命令行
(gdb) starti # 从第一条指令开始执行
(gdb) layout asm # 将汇编语言的 text ui 显示在整个界面上方
(gdb) si # stepi 单步执行
# 一些辅助命令
(gdb) info registers # 查看寄存器中的值
(gdb) p $rsp # 打印 rsp 寄存器,由于它是栈指针,所以结果是内存地址
(gdb) x $rsp # rsp 指针指向的值,相当于 *rsp 解引用
最后我们发现:返回指令导致问题。
二、使用汇编“强行”精简代码
上面尝试精简 C 程序的方法遇到了阻碍,于是课程从汇编角度入手:通过手动执行“系统调用”的方式,结束当前程序、而非异常退出。
movq $SYS_exit, %rax # exit(
movq $1, %rdi # status=1
syscall # );
上面的程序把 “系统调用” 的参数放到寄存器中,然后执行 syscall,让操作系统接管程序。在此基础上,课程实现了最小的 Hello World 程序:minimal.S
。
gcc -c minimal.S # 得到 minimal.o
ld minimal.o # 链接
objdump -d a.out | wc -l # 查看行数
得到的可执行程序无论是大小还是行数,都远小于 C 代码版本。
标签:代码,程序,最小,HelloWorld,编译,gdb,rsp,hello,out From: https://www.cnblogs.com/7ytr5/p/18281747