使用 g++7 构建的代码因访问未对齐的内存而崩溃

Code which was built with g++7, crashes with access to unaligned memory

当我尝试 运行 程序时,这是一种奇怪的行为,它是用 g++ 和优化 -O2 构建的。 使用:

我有两个成员的结构:

struct A {                                   
    uint8_t m8;                              
    Int128 m128;                                          
};                                           

其中 Int128 是:

// #pragma pack(push, 1)               
struct Base128 {
    __int128_t v{0};        
};              
// #pragma pack(pop)  

#pragma pack(push, 1)               
struct Int128: Base128 {            
    Int128();                       
};                                   
#pragma pack(pop)  

Base128 显式未打包,但 Int128 打包对齐 1 请注意,Base128 具有显式成员初始化,而 Int128 在另一个翻译单元中具有手动定义的空主体构造函数(以避免内联)。

当我将 Base128 包装更改为与 Int128 相同时,程序不会崩溃。

似乎编译器生成了无效指令:MOVAPS 而不是 MOVUPS 以访问构造函数中的 __int128_t 成员:

00000000000006b0 <_ZN6Int128C1Ev>:
 6b0:    66 0f ef c0              pxor   %xmm0,%xmm0
 6b4:    55                       push   %rbp
 6b5:    48 89 e5                 mov    %rsp,%rbp
 6b8:    0f 29 07                 movaps %xmm0,(%rdi)
 6bb:    5d                       pop    %rbp
 6bc:    c3                       retq   
 6bd:    0f 1f 00                 nopl   (%rax)

反之亦然:

00000000000006b0 <_ZN6Int128C1Ev>:
 6b0:    66 0f ef c0              pxor   %xmm0,%xmm0
 6b4:    55                       push   %rbp
 6b5:    48 89 e5                 mov    %rsp,%rbp
 6b8:    0f 11 07                 movups %xmm0,(%rdi)
 6bb:    5d                       pop    %rbp
 6bc:    c3                       retq   
 6bd:    0f 1f 00                 nopl   (%rax)

你知道我做错了什么吗?

源代码:

test.h:

#pragma once

#include <cstdint>

//#pragma pack(push, 1) // it fixes problem
struct Base128 {
    __int128_t v{0};
};
//#pragma pack(pop)

#pragma pack(push, 1)
struct Int128: Base128 {
    Int128();
}; 
#pragma pack(pop)

struct A {
    uint8_t m8; 
    //Int128 __attribute__((aligned(16))) m128; // it fixes problem
    Int128 m128;    
};

test.cpp:

#include "test.h"

// Int128::Int128() : Base128{0} {} // Fixes (why ?!)
Int128::Int128() {}

main.cpp:

#include "test.h"
int main() {
    A a;
    return 0;
}

建筑和运行宁:

g++-7 --save-temps -Wall -Wextra -std=c++14 -O2 -g main.cpp test.cpp && ./a.out

Source code on gitlab is here。它可以构建和 运行 如下所示:

./build.sh # build and run (crashes)
./build.sh [1..5] # where 1..5 -- different fixes

来自alignasdocumentation,据我所知相当于gcc的打包属性:

If the strictest (largest) alignas on a declaration is weaker than the alignment it would have without any alignas specifiers (that is, weaker than its natural alignment or weaker than alignas on another declaration of the same object or type), the program is ill-formed:

struct alignas(8) S {};
struct alignas(1) U { S s; }; // error: alignment of U would have been 8 without alignas(1)

此示例实际上与您的示例完全相同(将 class 的父级视为其第一个成员)。

所以我们可以肯定地说您的程序格式错误,因此调用了未定义的行为。您的大部分解决方法都可以被忽略,因为基本上只是 "luck"。无需解释它们为何起作用。

有趣的是,在代码中交换标准 alignas() 时,gcc 仍然没有报错,但 clang 开始正确报告错误:https://gcc.godbolt.org/z/EEErXg

编辑: alignas() 和 gcc 打包的等价性参考:

GCC 说它是 MSVC 功能的直接端口:https://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Structure_002dPacking-Pragmas.html

微软说alignas()是一回事:https://msdn.microsoft.com/en-us/library/2e70t5y1.aspx