Segfault ?
下面的程序会段错误吗?
1 2 3 4 5 6 7 8 9 10 11 12
| #include <stdio.h>
struct foo { long a; };
int main(int argc, char *argv[]) { struct foo *f = NULL;
printf("0x%016lx\n", &f->a); }
|
Segfault
段错误是指程序试图访问一个不存在或没有权限访问的内存位置时操作系统发出的信号,一般都会使程序中止。如果上面的程序的 printf 改为
1
| printf("0x%016lx\n", f->a);
|
则一定段错误,看看 CPU 执行的指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 0000000000001149 <main>: 1149: f3 0f 1e fa endbr64 114d: 55 push %rbp 114e: 48 89 e5 mov %rsp,%rbp 1151: 48 83 ec 20 sub $0x20,%rsp 1155: 89 7d ec mov %edi,-0x14(%rbp) 1158: 48 89 75 e0 mov %rsi,-0x20(%rbp) 115c: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp) 1163: 00 1164: 48 8b 45 f8 mov -0x8(%rbp),%rax 1168: 48 8b 00 mov (%rax),%rax 116b: 48 89 c6 mov %rax,%rsi 116e: 48 8d 05 8f 0e 00 00 lea 0xe8f(%rip),%rax # 2004 <_IO_stdin_used+0x4> 1175: 48 89 c7 mov %rax,%rdi 1178: b8 00 00 00 00 mov $0x0,%eax 117d: e8 ce fe ff ff call 1050 <printf@plt> 1182: b8 00 00 00 00 mov $0x0,%eax 1187: c9 leave 1188: c3 ret
|
触发段错误的是第 11 行的 mov (%rax),%rax
, 因为 rax
寄存器的值是 0, 这条 MOV 指令里的 (%rax)
实际上就是去内存地址 0 取数据。再看看上面不会段错误的程序的指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 0000000000001149 <main>: 1149: f3 0f 1e fa endbr64 114d: 55 push %rbp 114e: 48 89 e5 mov %rsp,%rbp 1151: 48 83 ec 20 sub $0x20,%rsp 1155: 89 7d ec mov %edi,-0x14(%rbp) 1158: 48 89 75 e0 mov %rsi,-0x20(%rbp) 115c: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp) 1163: 00 1164: 48 8b 45 f8 mov -0x8(%rbp),%rax 1168: 48 89 c6 mov %rax,%rsi 116b: 48 8d 05 92 0e 00 00 lea 0xe92(%rip),%rax # 2004 <_IO_stdin_used+0x4> 1172: 48 89 c7 mov %rax,%rdi 1175: b8 00 00 00 00 mov $0x0,%eax 117a: e8 d1 fe ff ff call 1050 <printf@plt> 117f: b8 00 00 00 00 mov $0x0,%eax 1184: c9 leave 1185: c3 ret
|
原来的第 11 行 mov (%rax),%rax
变成了
1 2
| 1168: 48 89 c6 mov %rax,%rsi 116b: 48 8d 05 92 0e 00 00 lea 0xe92(%rip),%rax # 2004 <_IO_stdin_used+0x4>
|
它并没有直接去内存地址 0 去取数据,而是将指令寄存器 rip
的当前值加上 0xe92
后将结果存入 rax
寄存器。
所以上面的程序是不是会段错误,就要看 CPU 是不是访问了 f (NULL)
这个 0 地址。
MOV vs LEA
References