目录

Chapter2-部分二刷-个人理解System call


二刷部分Chapter2 $\Longrightarrow$ 如何理解System call

问题:构造最小的”Hello world”程序

基本程序和静态编译

1
2
3
4
5
6
#include <stdio.h>

int main() {
	printf("Hello world");
	return 0;
}
1
gcc -static hello.c -o hello && ./hello

编译出来很大(Ubuntu上约900KB)

  • 如果只编译,不链接,看一下obj文件的反汇编
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
gcc -c hello.c && objdump -d hello.o

hello.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:	f3 0f 1e fa          	endbr64 
   4:	55                   	push   %rbp
   5:	48 89 e5             	mov    %rsp,%rbp
   8:	48 8d 05 00 00 00 00 	lea    0x0(%rip),%rax        # f <main+0xf>
   f:	48 89 c7             	mov    %rax,%rdi
  12:	b8 00 00 00 00       	mov    $0x0,%eax
  17:	e8 00 00 00 00       	call   1c <main+0x1c>
  1c:	b8 00 00 00 00       	mov    $0x0,%eax
  21:	5d                   	pop    %rbp
  22:	c3                   	ret    
  • 就和普通的函数差不多,只有参数压栈,call和参数出栈的过程
    • 普通函数fun.c
1
2
void f() {
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
gcc -c fun.c && objdump -d fun.o 

fun.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <f>:
   0:	f3 0f 1e fa          	endbr64 
   4:	55                   	push   %rbp
   5:	48 89 e5             	mov    %rsp,%rbp
   8:	90                   	nop
   9:	5d                   	pop    %rbp
   a:	c3                   	ret
  • 可以看到不链接的话和普通的函数调用过程差不多

  • 链接失败!

1
2
3
4
○ → ld hello.o
ld: 警告: 无法找到项目符号 _start; 缺省为 0000000000401000
ld: hello.o: in function `main':
hello.c:(.text+0x18): undefined reference to `printf'
  • 本来以为找的main,但是它实际上在找_start这个东西?

那我们就把main改成_start

1
2
3
4
5
6
#include <stdio.h>

void _start() {
    printf("Hello world");
    return 0;
}
/img/Operating System/support1-1.png
ld仍然不予通过-> 找不到printf?
  • 那把printf也给去了
1
2
3
4
5
#include <stdio.h>

void _start() {
	
}
  • 编译与链接全部成功!
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
○ → gcc -c hello.c && objdump -d hello.o && ld hello.o

hello.o:     文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <_start>:
   0:	f3 0f 1e fa          	endbr64 
   4:	55                   	push   %rbp
   5:	48 89 e5             	mov    %rsp,%rbp
   8:	90                   	nop
   9:	5d                   	pop    %rbp
   a:	c3                   	ret    
  • 程序很小,只有99个字节

  • 但是运行 $\Longrightarrow$ 段错误!

  • 但是里面加入while(1)这种死循环就没事了


  • 对于没有while(1)的a.out,使用gdb狠狠的拷打调试
/img/Operating System/support1-2.jpg

这个时候我们看到rsp的地址是合法的,里面的值是1,当执行ret后,rsp将栈顶的值1赋给了pc,gdb显示pc访问了非法位置

/img/Operating System/support1-3.png
🤡呦吼,崩溃了!
  • CPU只会执行指令,该怎么停下来?(mov,add这些指令停不下来)
  • 需要什么指令让它停下来? $\Longrightarrow$ System call 也就是管态

最小的Hello world程序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <sys/syscall.h>

.globl _start
_start:
  movq $SYS_write, %rax   # write(
  movq $1,         %rdi   #   fd=1,
  movq $st,        %rsi   #   buf=st,
  movq $(ed - st), %rdx   #   count=ed-st
  syscall                 # );

  movq $SYS_exit,  %rax   # exit(
  movq $1,         %rdi   #   status=1
  syscall                 # );

st:
  .ascii "\033[01;31mHello, OS World\033[0m\n"
ed:
  • 它在干什么
    • 给许多寄存器赋值,然后call了syscall
/img/Operating System/support1-4.png
编译后查看反汇编代码
  • ld链接,编译成功,运行成功!
1
○ → objdump -d a.out | less
/img/Operating System/support1-5.png
查看反汇编代码
/img/Operating System/support1-6.png
man syscall查看syscall的文档

我们会发现,syscall会告诉我们在各种架构的计算机中该如何通过寄存器为syscall传入参数,man syscalls会告诉我们Linux操作系统的接口

  • 所以程序的执行模式
感谢

感谢jyy互助群里zweix大佬提供的 jyyslide-md

使用过程中发现因为Windows很烂的编码问题始终会出现gbk解码错误的error,和zweix大佬差不多沟通了大半个下午,由于懒得给vscode配环境直接改成手动print看数据🤣,感觉就是命令行传参编码的问题,最后终于找到了解决方法😆,只能说Windows这个编码设置也是没谁了。