0. 起因
上周末下着大雨,自己在家吃着火锅听着歌,听到周杰伦的《七里香》,里面有句歌词:
雨下整夜,我的爱溢出就像雨水
听到“溢出”这个词我脑子里就想到的栈溢出,我就将歌词篡改成“雨下整天,我的占溢出就像雨水”发了朋友圈,并配了下图1。
图中包含的是一个简单C的Hello World
程序:
#include <stdio.h>
void main() {
char *str = "hello world.";
*str = 'A';
}
但编译后执行遇到了段错误。
原本这张图是我网上随便档下来的,当时也没太在意,但却在朋友圈引发了讨论。焦点主要存在于:1)这是不是栈溢出;2)这么简单的代码为什么会引发段错误。
讨论是不是栈溢出感觉没什么意义,因为本来就是随便配图。所以令人好奇的是为什么会出现段错误。通过先验知识,我们知道段错误(segmentation fault)一般是因为程序非法访问了某一段地址。代码中hello world
是个字符串,在Java等语言中字符串是不可变对象,因此我也猜测C中的字符串是不是也有类似性质,即字符串是只读的。因此我的观点是操作系统将字符串所在的内存区域标记成只读,现在程序进行写操作,显然它没有权限,因此非法。
在我想着怎么去验证我的想法的时候,有前辈直接通过导出的汇编代码看到了真相。
.file "test.c"
.section .rodata
.LC0:
.string "hello world."
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq $.LC0, -8(%rbp)
movq -8(%rbp), %rax
movb $65, (%rax)
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits
从汇编代码中看到,gcc通过.section
这个汇编指令将