Introduction
回顾 Hello World
// hello.c
#include <stdio.h>
int main(){
printf("Hello, World\n");
return 0;
}
使用 objdump -d a.out
查看 gcc 编译出来的结果,会发现编译出来的文件一点也不小(使用 gcc -static.c
会导致更大的可执行文件)。为了控制编译和链接流程,可以首先通过 gcc -c hello.c
生成目标文件 hello.o
。此时,如果使用 ld hello.o
直接进行链接的话,会发生找不到 puts
的错误 (这就是 gcc 在背后帮我们做的事情,可通过 --verbose
查看具体配置)。
显然,该问题是由于 printf
引起的,删除 printf
和 include
后可以链接生成 a.out
(可以通过 -e main
避免 warning)。然而,在运行时产生了 Segmentation Fault:程序仅靠 “计算” 指令是无法正常结束的,这就是操作系统需要提供服务的地方。
系统调用和最小 Hello World
用户程序只能通过系统调用 (以操作系统所允许的方式) 来向操作系统请求服务。在最小的 Hello World 中,我们仅需要请求两个服务:write()
和 exit()
。
// hello.S
.section .data
msg: .asciz "Hello World\n" ;
.section .text
.globl _start
_start:
movq $1, %rax // write system call
movq $1, %rdi // file descriptor
movq $msg, %rsi // pointer to message
movq $12, %rdx // message length
syscall
movq $60, %rax // exit system call
movq $0, %rdi // return number
syscall
运行上述代码:
cpp hello.S > hello.i
as hello.i -o hello.o
ld hello.o
./a.out
系统调用追踪
我们可以通过 strace
来追踪一个应用程序在执行过程中产生的系统调用:
strace ./a.out
strace
实际上是通过 ptrace()
这个系统调用来实现的,其可以在被追踪的程序每次进入或退出内核时停止其执行,然后追踪程序就可以进一步使用 ptrace()
来获取程序的状态。例如,下列代码给出了利用 ptrace()
实现的一个简易 tracer:
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscall.h>
int main(int argc, char *argv[]) {
if (argc < 2) {
printf(stderr, "Usage: %s <command>\n", argv[0]);
exit(EXIT_FAILURE);
}
pid_t child = fork();
if (child == 0) {
// this process (tracee) is to be traced
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
execvp(argv[1], &argv[1]);
} else {
int status;
struct user_regs_struct regs;
// wait for the tracee to stop itself
waitpid(child, &status, 0);
while (1) {
// restart the stopped tracee, and arrange for the tracee to be stopped
// at the next entry to or exit from a system call
ptrace(PTRACE_SYSCALL, child, NULL, NULL);
waitpid(child, &status, 0);
// if the child exits
if (WIFEXITED(status))
break;
// copy the tracee's general-purpose registers
ptrace(PTRACE_GETREGS, child, NULL, ®s);
printf("System call number: %lld\n", regs.orig_rax);
}
}
return 0;
}