Код, созданный с помощью g ++ 7, вылетает при доступе к невыровненной памяти - PullRequest
0 голосов
/ 19 октября 2018

Странное поведение, когда я пытаюсь запустить программу, созданную с помощью g ++ и оптимизации -O2.Использование:

  • g ++ - 7 (Ubuntu 7.3.0-16ubuntu3) 7.3.0
  • ядро ​​4.15.0-36-generic

У меня естьструктура с двумя членами:

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 Beзаметил, что 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

Исходный код на gitlab находится здесь .Он может быть построен и запущен, как указано ниже:

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

1 Ответ

0 голосов
/ 19 октября 2018

Из документации из alignas, что, насколько я могу судить, эквивалентно атрибуту упаковки gcc:

Если в объявлении выровнены самые строгие (самые большие)более слабое, чем выравнивание, которое оно будет иметь без каких-либо спецификаторов alignas (то есть, более слабое, чем его естественное выравнивание или более слабое, чем alignas в другом объявлении того же объекта или типа), программа плохо сформирована:

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

...