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



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

  • g++-7 (Ubuntu 7.3.0-16ubuntu3( 7.3.0
  • 内核 4.15.0-36 通用

我有两个成员的结构:

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)

你知道吗:我做错了什么?

源代码:

测试.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;    
};

测试.cpp:

#include "test.h"
// Int128::Int128() : Base128{0} {} // Fixes (why ?!)
Int128::Int128() {}

主.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

gitlab上的源代码在这里。它可以按下面列出的方式构建和运行:

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

alignas的文档来看,据我所知,它相当于 gcc 的打包属性:

如果声明上最严格(最大(的对齐方式弱于

没有任何对齐说明符的对齐方式(即,比其自然对齐方式弱或比同一对象或类型的另一个声明上的对齐方式弱或弱(,则程序格式不正确:

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

这个例子实际上与你的完全相同(将类的父级视为它的第一个成员(。

因此,我们可以肯定地说您的程序格式不正确,因此调用了未定义的行为。您的大多数解决方法都可以被忽略为基本上只是"运气"。无需解释它们为什么有效。

有趣的是,当在代码中交换标准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

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

最新更新