隐式声明及编译选项不加-Werror导致的灾难

隐式声明及编译选项不加-Werror导致的灾难

实验分析:

两个.c文件:
test.c源文件:

#include 
#include 
char *test(void)
{
    char *addr = NULL;
    addr = 0x334485667788;

    printf("[%s] addr = %p \n", __func__, addr);
    printf("[%s] sizeof(int32_t) = %d \n", __func__, sizeof(int32_t));
    printf("[%s] sizeof(addr) = %d \n", __func__, sizeof(addr));
    return addr;
}

main.c源文件:

#include 
#include 
int main() {
    //char *test();
    void *addr = test();
    printf("[%s] addr = %p \n", __func__, addr);
    return 0;
}

分别注释或者不注释main函数中char *test()的声明,运行结果如下:
未声明结果:

[test] addr = 0x334485667788
[test] sizeof(int32_t) = 4
[test] sizeof(addr) = 8
[main] addr = 0xffffffff85667788

声明结果:

[test] addr = 0x334485667788
[test] sizeof(int32_t) = 4
[test] sizeof(addr) = 8
[main] addr = 0x334485667788

区别在于通过test接口返回的地址发生了变化,将两个可执行文件进行反汇编,进行比较:
未声明相比于声明,调用test之后,main中多了一个操作cltq:

000000000040052d 
: 40052d: 55 push %rbp 40052e: 48 89 e5 mov %rsp,%rbp 400531: 48 83 ec 10 sub $0x10,%rsp 400535: be 00 00 00 00 mov $0x0,%esi 40053a: bf 64 00 00 00 mov $0x64,%edi 40053f: b8 00 00 00 00 mov $0x0,%eax 400544: e8 28 00 00 00 callq 400571 400549: 48 98 cltq 40054b: 48 89 45 f8 mov %rax,-0x8(%rbp) 40054f: 48 8b 45 f8 mov -0x8(%rbp),%rax 400553: 48 89 c2 mov %rax,%rdx 400556: be 85 06 40 00 mov $0x400685,%esi 40055b: bf 74 06 40 00 mov $0x400674,%edi 400560: b8 00 00 00 00 mov $0x0,%eax 400565: e8 a6 fe ff ff callq 400410 <printf@plt> 40056a: b8 00 00 00 00 mov $0x0,%eax 40056f: c9 leaveq 400570: c3 retq

cltq操作:Convert %eax to quad word 即有符号的扩展,将%eax扩展为4字

综上,问题在于:

  1. 编译未加-Werror
  2. gcc会优化未声明的函数,使用隐式声明,函数返回类型为int型,会对超过int型的返回值截断
  3. 将函数返回值赋值会将int型强转为函数定义的类型,如果比int型长,将会产生补位,即汇编中的cltq操作
  4. 函数返回值又被作为地址,当访问这个地址的时候就会crash

经验教训:

  1. 如果进行了函数声明,则不会函数返回值的截断与补位操作
  2. 编译加上编译选项-Werror

当时遇到这类问题,突然想到之前写的一篇文章中的隐式声明,所以找到问题突破口:隐式声明与GCC内建函数

之后在csdn上搜索到一篇类似文章,大家可以参考:一条cltq指令引发的血案

你可能感兴趣的:(Linux,gcc,编译问题,C语言)