Подтвердить что ты не робот

Clang runtime fault при бросании выровненного типа. Ошибка компилятора?

У меня есть тип, объявленный с помощью __attribute__((aligned(16))). При построении с clang в OS X на x86_64 следующий код вызывает ошибку GP при попытке throw значения, содержащего этот тип. Ошибка возникает из-за того, что компилятор генерирует 128-битную инструкцию перемещения, которая должна быть выровнена по 16-байтовой границе, но адрес неправильно выровнен.

Вот программа, которая воспроизводит проблему:

#include <stdint.h>
#include <stdio.h>

struct __attribute__((aligned(16))) int128 {
    uint64_t w[2];
};

int main()
{
    try {
        int128 x;
        throw x;
    } catch (int128 &e) {
        printf("%p %lu\n", &e, sizeof(e));
    }
}

И разборки с местом повреждения, отмеченным ->:

a.out`main:
    0x100000db0 <+0>:   pushq  %rbp
    0x100000db1 <+1>:   movq   %rsp, %rbp
    0x100000db4 <+4>:   subq   $0x40, %rsp
    0x100000db8 <+8>:   movl   $0x10, %eax
    0x100000dbd <+13>:  movl   %eax, %edi
    0x100000dbf <+15>:  callq  0x100000e8c               ; symbol stub for: __cxa_allocate_exception
    0x100000dc4 <+20>:  movaps -0x10(%rbp), %xmm0
->  0x100000dc8 <+24>:  movaps %xmm0, (%rax)
    0x100000dcb <+27>:  movq   0x23e(%rip), %rsi         ; (void *)0x0000000100001058
    0x100000dd2 <+34>:  xorl   %ecx, %ecx
    0x100000dd4 <+36>:  movl   %ecx, %edx
    0x100000dd6 <+38>:  movq   %rax, %rdi
    0x100000dd9 <+41>:  callq  0x100000e9e               ; symbol stub for: __cxa_throw

Текущий регистр:

(lldb) register read rax
       rax = 0x0000000100905b08

Похоже, что функция __cxa_allocate_exception не знает требования к выравниванию того типа, для которого он выделяет хранилище. В моей системе происходит выделение адреса, который заканчивается на 8, и поэтому не выравнивается по 16 байт. Когда команда movaps пытается перенести данные в эту ячейку памяти, CPU неисправен из-за неравномерного доступа.

Информация о компиляторе (clang из Xcode 6.3.2):

$ clang --version
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.3.0
Thread model: posix

Является ли это ошибкой компилятора? Каким может быть способ обойти это?

UPDATE: я отправил это в базу данных ошибок LLVM: https://llvm.org/bugs/show_bug.cgi?id=23868

4b9b3361

Ответ 1

В этом случае, как представляется, __cxa_allocate_exception в принципе никогда не определяется для понимания выравнивания (для Clang или GCC), поэтому бросание выровненных объектов в основном относится к UB (ну, в любом случае, выравнивание было расширением для компилятора...). Единственное выравнивание, которое, по-видимому, гарантирует 8 байт, поскольку это наибольшее выравнивание, требуемое любым встроенным типом (double).

Простейшим обходным решением, о котором я могу думать, было бы просто использовать неглавный тип в throw:

struct unaligned_int128 {
    uint64_t w[2];
    unaligned_int128(const int128 &x) { w[0] = x.w[0]; w[1] = x.w[1]; }
};

int main()
{
    try {
        int128 x;
        throw unaligned_int128(x);
    } catch (unaligned_int128 &e) {
        printf("%p %lu\n", &e, sizeof(e));
    }
}