strerror的坑

最近写的一段代码,总是出core,精简了一下,稳定复现。

#include <stdio.h>
#include <errno.h>

int main()
{
    printf("%s\n", strerror(errno));
    return 0;
}

编译并执行,就会报Segmentation fault (core dumped)。

看下core的栈:

(gdb) bt
#0  0x0000003f0b06feb0 in strlen () from /lib64/tls/libc.so.6
#1  0x0000003f0b0429ac in vfprintf () from /lib64/tls/libc.so.6
#2  0x0000003f0b047f08 in printf () from /lib64/tls/libc.so.6
#3  0x000000000040058c in main ()

问题应该是出在strerror(errno)上了。

反编译一下代码,

0000000000400558 <main>:
  400558:       55                      push   %rbp
  400559:       48 89 e5                mov    %rsp,%rbp
  40055c:       e8 27 ff ff ff          callq  400488 <__errno_location@plt>
  400561:       8b 38                   mov    (%rax),%edi
  400563:       b8 00 00 00 00          mov    $0x0,%eax
  400568:       e8 0b ff ff ff          callq  400478 <strerror@plt>
  40056d:       89 c6                   mov    %eax,%esi                 最关键的地方:strerror返回的地址存放在eax中,eax的值赋给esi
  40056f:       bf 7c 06 40 00          mov    $0x40067c,%edi            这里0x40067c指向字符串"%s\n"
  400574:       b8 00 00 00 00          mov    $0x0,%eax
  400579:       e8 ea fe ff ff          callq  400468 <printf@plt>
  40057e:       b8 00 00 00 00          mov    $0x0,%eax

差不多到这里问题就追踪出来了。由于是在64位系统上执行,因此eax只取了32位,赋给esi也只取了32位。而其实在64位系统上,strerror返回的是char *指针,应该是64位的。借用一张他人绘制的rax,eax间的关系图:

|63..32|31..16|15-8|7-0|
               |AH.|AL.|
               |AX.....|
       |EAX............|
|RAX...................|

结论:

strerror函数声明在string.h头文件里,由于没有包含该头文件,编译器将strerror的返回值当做了int类型来处理,而int类型是32位的,因此当做指针传给printf,就跪了。后来我编译的时候打开warning,其实已经给出了问题所在:

cc -Wall test.c 

test.c: In function `main':
test.c:6: warning: implicit declaration of function `strerror'
test.c:6: warning: format argument is not a pointer (arg 2)

所以再一次的,请编译时开启warning提示。

 

posted @ 2015-11-17 14:31  driftcloudy  阅读(1174)  评论(0编辑  收藏