该内容源码参考了 <深入理解计算机系统>[3.10.3]
学习该章节之前,一直有个疑问,Segmentation fault 这样的错误会具体引发的原因是啥, 可不可以从汇编代码的层面进行一个学习和了解. 所有就有了这篇文章, 纯属个人学习研究.
完整的测试源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
*/
char* gets(char* s)
{
int c;
char *dest = s;
// read c char write to dest memory;
while((c = getchar()) != '\n' && c != EOF)
{
*dest++= c;
}
if(c == EOF && dest == s)
{
return NULL;
}
*dest++ ='\0';
return s;
}
void echo(){
char buf[8];
gets(buf);
puts(buf);
}
int main()
{
echo();
return 0;
}
编译过程,
shell>gcc -g demo.c -o demo
程序编译成功后,随便输入几个字符, 可以正常的回显, 但是超过某个长度就会出现文章开始的问题, 那么新的问题来了,
这个长度是不是就是代码中定义的buf的长度呢, 会不会有其他问题发生,
验证思路:
1 通过汇编代码, 查看寄存器地址中的内容, 直接明了的去观察, 那么就存在一个对比的过程,
第一遍的时候是正常的输入, 不会发生错误,
第二遍会输入较长的内容, 对比寄存器中的值.
正常输入时,
在没有输入任何字符的时候, 相关的寄存器的值,
这个是在程序调用getchar() 函数前寄存器的值.
(gdb) i r ebp
ebp 0xbfffea68 0xbfffea68
(gdb) x/16cb 0xbfffea68
0xbfffea68: 120 'x' -22 '\352' -1 '\377' -65 '\277' 100 'd' -124 '\204' 4 '\004'
8 '\b'
0xbfffea70: 0 '\000' -106 '\226' 117 'u' 0 '\000' -112 '\220' -22 '\352' -1 '\377
' -65 '\277'
(gdb) i r esp
esp 0xbfffea50 0xbfffea50
(gdb) x/16cb 0xbfffea50
0xbfffea50: -12 '\364' -1 '\377' -118 '\212' 0 '\000' 4 '\004' -30 '\342'
-118 '\212' 0 '\000'
0xbfffea58: -120 '\210' -22 '\352' -1 '\377' -65 '\277' -87 '\251' -124 '\204'
4 '\004' 8 '\b'
(gdb) i r eax
eax 0xbfffea60 0xbfffea60
(gdb) x/16cb 0xbfffea60
0xbfffea60: 37 '%' -98 '\236' 121 'y' 0 '\000' 28 '\034' -21 '\353' -1 '\377'
-65 '\277'
0xbfffea68: 120 'x' -22 '\352' -1 '\377' -65 '\277' 100 'd' -124 '\204' 4 '\004'
8 '\b'
开始调用getchar() 函数
此时有个隐藏的信息是, gets() 函数中, 参数s的地址是 0xbfffea60, 这个需要知道, 就是这个参数出现了问题,
gdb> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
在输入完成后,
再次查看寄存器的内容,
(gdb) x/16cb 0xbfffea68
0xbfffea68: 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a'
0xbfffea70: 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a'
(gdb) x/32cb 0xbfffea50
0xbfffea50: 96 '`' -22 '\352' -1 '\377' -65 '\277' 4 '\004' -30 '\342' -118 '\2
12' 0 '\000'
0xbfffea58: -120 '\210' -22 '\352' -1 '\377' -65 '\277' -87 '\251' -124 '\204'
4 '\004' 8 '\b'
0xbfffea60: 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a'
0xbfffea68: 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a'
(gdb) x/16cb 0xbfffea60
0xbfffea60: 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a'
0xbfffea68: 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 97 'a'
对比发现 0xbfffea68 地址中的内容被改写了, 这个地址是程序被调用时的入栈地址, 在gets()函数内部把值改写了,
导致程序返回的时候,出现了问题,
如果用户输入了缓冲区大小内的内容后, 则器寄存器中的值为
(gdb) x/16cb 0xbfffea60
0xbfffea60: 97 'a' 97 'a' 97 'a' 97 'a' 97 'a' 0 '\000' -1 '\377' -65 '\277'
0xbfffea68: 120 'x' -22 '\352' -1 '\377' -65 '\277' 100 'd' -124 '\204' 4 '\004'
8 '\b'
可以发现 0xbfffea68地址中的值和原来的值是一致的, 此时程序退出则是正常的,
具体可以使用的空间的大小的问题, 什么时候,会触发返回地址被改写.