【问题标题】:alignment requirements for int32_t: gcc/linux 86 vs amd64int32_t 的对齐要求:gcc/linux 86 vs amd64
【发布时间】:2015-07-29 18:05:48
【问题描述】:

首先我们谈谈 gcc/linux(x86, amd64) 和 c99。

代码如下:

#include <stdint.h>

void f(void *p)
{
  uint32_t *num = p;
  *num = 17;
}

int main()
{
  char buf[8] __attribute__ ((aligned (8)));
  f(&buf[3]);
}

问题是UB吗?

一方面,英特尔 CPU 允许非对齐访问, 从另一只手我发现了这个: http://www.uclibc.org/docs/psABI-i386.pdf http://www.x86-64.org/documentation/abi.pdf 并且他们都提到了4字节整数的4字节对齐。

所以即使它编译并且工作正常,它仍然是 UB 吗? 因为 gcc 认为“uint32_t *”指针的值指向 4字节对齐地址, 并毫不犹豫地在“f”函数中使用例如“SSE”?

gcc 维护者认为这样的代码是“未定义代码”: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66194

最初的问题来自这里(俄语): https://ru.stackoverflow.com/questions/268888/%D0%9E%D0%BF%D1%8F%D1%82%D1%8C-%D0%BE%D0%B1-%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8-gcc-o3-intel-i5-2500-segmentation-fault

【问题讨论】:

  • stackoverflow.com/q/9608171/995714 gcc 将假定指针未对齐,除非您已指定
  • 实际上在上面的链接中,gcc 假设“float*”对齐 4,而不是“未对齐”,再加上这个关于 ARM 架构的话题,不清楚英特尔的情况是否相同。跨度>
  • 你可以随时反汇编代码,看看它产生了什么指令。
  • 在实际代码中 gcc 生成导致 seg 错误的指令(它使用 SSE),所以问题的答案将是它是 gcc 错误还是程序员编写了代码错误。
  • 对于像这样明确导致访问不对齐的代码,我认为它要求编译器自动将代码转换为正确对齐的东西。这是编译器应该放手的情况,因为程序员已经告诉它他们知道自己在做什么。他们是否真的知道自己在做什么取决于特定的 CPU:如果 CPU 无法处理它,那么它是程序员引起的错误,就像未定义行为的情况一样。

标签: c linux gcc intel c99


【解决方案1】:

编译和链接正常没有任何意义,未定义的行为发生在运行时。就 C 标准而言,代码可能会或可能不会调用 UB。

6.3.2.3/7:

指向对象类型的指针可以转换为指向对象类型的指针 不同的对象类型。如果结果指针不正确 为引用的类型对齐,行为未定义。

“正确对齐”一词取决于 CPU 架构。如果你的CPU没有对齐限制,那么uint32_t指针就被认为是正确对齐的,那么你就没有UB了。

如果 CPU 允许未对齐的访问,但会导致代码效率降低,那么您也将没有 UB。

但是如果CPU不支持未对齐访问,那么代码就是UB。

【讨论】:

  • 你的答案是得到答案的方法,而不是答案。正如您在问题中看到的那样,有指向 ABI 的链接(但我不确定是否适用于 gcc/linux(x86/amd64) 案例),但出于这个原因,我提到了 CPU 和编译器+OS(对于 ABI),这个是因为我找不到 x86/amd64 的 gcc 对齐要求的确切参考。
  • @user1034749 好吧,您实际上是在问两个问题:1)代码是未定义的行为,2)您的特定 ABI 有哪些对齐要求。我回答了 1),我对 2) 的了解还不够,无法自信地回答。
  • 我不同意。问题只有一个 - (1)。我重读了你的答案,我认为这是错误的。您提到 CPU,是的 x86 和 amd64 允许未对齐的访问。但是在标准中没有关于 CPU 的注释,如果 CPU 允许,但编译器不允许,并且它(编译器)生成需要对齐的代码大于 CPU 需要对齐的代码。在 c99 标准中没有说明应该如何实现“对齐要求”,所以这将是有效的实现。
  • @user1034749 该标准只关注“实现”,即从编译器到 ABI 再到 CPU 的所有内容。如果 CPU 支持,ABI 将不允许未对齐的访问,这似乎很奇怪。
  • 例如在intel misaligned access减慢程序上,它还需要额外的代码生成并且不允许对某些代码进行向量化(在编译阶段),所以这样的ABI有很多优点,但这只是幻想。我不知道在 linux/x86_64 和 linux/i686 上使用哪个 ABI gcc。
【解决方案2】:

x86 和 x64 指令集都允许非对齐访问,而不会导致未定义的行为,但它们的 SIMD 扩展指令集除外。

您的代码在 x86/x64 中的问题在于,由于别名违规,它会调用未定义的行为。由于您已经在使用gcc 扩展,您可以使用-fno-strict-aliasing 禁用别名规则。

【讨论】:

  • 谢谢,但“-fno-strict-aliasing”没有帮助,看起来像 gcc 错误?
  • @user1034749 你说的“没有帮助”是什么意思,你认为gcc的bug在哪里?
  • 我在实际代码中有更复杂的“void f(void *p)”函数,并且 gcc 生成“sse/avx”的东西,需要 4 字节对齐,即使我使用“-fno-strict-aliasing”
  • @user1034749 我认为您应该使用显示崩溃行为的真实测试用例提出问题。 SIMD 要求可能远远高于 4 字节。
  • 当然它们更高,但我不使用它 (simd)不关心对齐的指令,如果 x64 ABI 不需要 4 字节对齐,这是 gcc 处理这种未对齐情况的问题。
猜你喜欢
  • 1970-01-01
  • 2010-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-09
  • 1970-01-01
  • 2018-07-29
相关资源
最近更新 更多