0

我只花了半天的时间在 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

如果幸运的话,什么都不会发生,但如果它恰好为内存中的向上指针返回一个大值,它就会搞砸了,如下所示:

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

并且您的指针无效。

修复和解决方案:

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

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

4

2 回答 2

3

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

这是 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-pointand添加-Werror=array-bounds到列表中,但它们具有误报,因为它们不标记约束违规,仅标记运行时 UB。因此,只要调用 UB 的代码无法访问,包含此类警告的程序仍然可以是正确的程序(作为一个很好的例子,考虑一下分支if (sizeof(int)==sizeof(long)) { ... } else { ... },例如未采用的分支根据结果调用 UBsizeof操作员)。

于 2013-03-11T15:17:33.357 回答
1

这是一个老笑话。我在这里描述了一个类似的情况:A nice 64-bit error in C

为避免这些错误和其他错误,请阅读文章“真实程序中 64 位错误示例集”并使用Viva64 规则

于 2013-03-11T14:25:31.593 回答