构造最小的 Hello, World “应用程序”

int main() {
  printf("Hello, World\n");
}

gcc 编译出来的文件一点也不小

  • objdump 工具可以查看对应的汇编代码
  • --verbose 可以查看所有编译选项 (真不少)
    • printf 变成了 puts@plt
  • -Wl,--verbose 可以查看所有链接选项 (真不少)
    • 原来链接了那么多东西
    • 还解释了 end 符号的由来
  • -static 会链接 libc (大量的代码)

强行构造最小的 Hello, World?

我们可以手动链接编译的文件,直接指定二进制文件的入口

  • 直接用 ld 链接失败
    • ld 不知道怎么链接 printf
  • 不调用 printf 可以链接
    • 但得到奇怪的警告 (可以定义成 _start 避免警告)
    • 而且 Segmentation Fault 了
  • while (1); 可以链接并正确运行

问题:为什么会 Segmentation Fault?

  • 当然是观察程序的执行
    • 初学者必须克服的恐惧:STFW/RTFM (M 非常有用)
    • starti 可以帮助我们从第一条指令开始执行程序

解决异常退出

有办法让程序 “停下来” 吗?

  • 纯 “计算” 的状态机:不行
  • 没有 “停机” 的指令

解决办法:用一条特殊的指令请操作系统帮忙

movq $SYS_exit,  %rax   # exit(
movq $1,         %rdi   #   status=1
syscall                 # );
  • 把 “系统调用” 的参数放到寄存器中
  • 执行 syscall,操作系统接管程序
    • 程序把控制权完全交给操作系统
    • 操作系统可以改变程序状态甚至终止程序

对一些细节的补充解释

为什么用 gcc 编译?

  • gcc 会进行预编译 (可以使用 __ASSEMBLER__ 宏区分汇编/C 代码)

ANSI Escape Code 的更多应用

  • vi.c from busybox
  • dialog --msgbox 'Hello, OS World!' 8 32
  • ssh -o 'HostKeyAlgorithms +ssh-rsa' sshtron.zachlatta.com

更重要的问题:怎样才能变强?

  • 问正确的问题、用正确的方式找答案
    • syscall (2), syscalls (2) -- RTFM & RTFSC
    • Q & A 论坛;Q & A 机器人

汇编代码的状态机模型

Everything is a state machine: 计算机 = 数字电路 = 状态机

  • 状态 = 内存 $M$ + 寄存器 $R$
  • 初始状态 = ABI 规定 (例如有一个合法的 %rsp)
  • 状态迁移 = 执行一条指令
    • 我们花了一整个《计算机系统基础》解释这件事
    • gdb 同样可以观察状态和执行

操作系统上的程序

  • 所有的指令都只能计算
    • deterministic: mov, add, sub, call, ...
    • non-deterministic: rdrand, ...
    • syscall 把 $(M,R)$ 完全交给操作系统