c - x86-64 中返回指针的未声明函数导致无效指针扩展

标签 c pointers size x86-64 expansion

<分区>

我只花了半天时间来解决 x86-64 代码中的一个微妙崩溃,所以这对其他人来说是一个提醒 - 我没有在其他地方看到过这个问题。

如果您在没有正确声明的情况下使用 libc 函数,gcc 将假定它返回 int。例如。 setlocale() 被假定为 int setlocale(),在 EAX 中返回一个 32 位 int 值。

尝试通过隐式或显式转换将此返回值转换为指针,将强制通过符号扩展从 32 位转换为 64 位,即使被调用函数在 RAX 中返回了有效的 64 位指针值!例如

    char *p = setlocale(0, 0);      // bear with me for a second

被编译成

    1c: b8 00 00 00 00          mov    $0x0,%eax
    21: e8 00 00 00 00          callq  26 <hard_locale+0x26>
    26: 48 98                   cltq   ;   <--- eax is expanded in rax 

GCC 甚至试图告诉您:

    warning: initialization makes pointer from integer without a cast

如果您添加显式强制转换,警告将更改为,这表明了问题:

    warning: cast to pointer from integer of different size

如果运气好的话,什么都没有发生,但是如果恰好返回了一个很大的内存中up指针的值,那就乱七八糟了,如下:

    function returns in RAX: 0x07ffff7b9705e
    cltq considers EAX with negative sign: 0xf7b9705e
    now RAX is: 0xfffffffff7b9705e

并且您的指针无效。

修复和解决方案:

  • 始终使用正确的函数声明

  • -Wall -Werror 应该在 x86-64 编译器中默认打开。

最佳答案

未声明的函数不是有效的 C。只需添加 -Werror=implicit-function-declaration 问题就会消失。您不需要将其他警告(主要是系统性考虑)变成错误。

这里是 GCC 拒绝无效 C 而没有任何误报的警告错误选项列表。它遗漏了一些东西(GCC 不支持捕捉)但它大部分是完整的:

  • -Werror=implicit-function-declaration
  • -Werror=implicit-int
  • -Werror=pointer-sign
  • -Werror-pointer-arith
  • -Werror=return-type
  • -std=c99(或 -std=c11 等,视需要而定)
  • -pedantic-errors(可选;拒绝一些有效但不完全可移植的代码)

请注意,我最初还添加了 -Werror=sequence-point-Werror=array-bounds 到列表中,但它们有误报,因为它们没有标记约束违规,仅运行时 UB。因此,包含此类警告的程序仍然可以是一个正确的程序,只要调用 UB 的代码不可访问(作为一个很好的例子,想想像 if (sizeof(int)==sizeof(long )) { ... } else { ... } 其中未采用的分支根据 sizeof 运算符的结果调用 UB。

关于c - x86-64 中返回指针的未声明函数导致无效指针扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15339694/

相关文章:

尝试在 c 中使用 struct 时出现编译错误

c# - 如何在不同的机器上调用dll方法?

c - (C) 将std in解析成链表,无限循环

C string strip whitespace 抛出段错误(core dumped)

c# - 在 C# 中将结构体指针作为参数传递

c++ - 2位大小变量

android - 如何根据String的大小自动调整TextView的高度?

c - C 中奇怪的段错误

c++ - 在 C++ 中添加指向字符串的指针

html - 中屏幕不存在属性