2015-06-17 8 views
13

Ho un tipo dichiarato con __attribute__((aligned(16))). Quando si crea con clang su OS X su x86_64, il seguente codice causa un errore GP quando si tenta di throw un valore contenente questo tipo. L'errore si verifica perché il compilatore genera un'istruzione di spostamento a 128 bit che deve essere allineata su un limite di 16 byte, ma l'indirizzo non è allineato correttamente.Errore di runtime Clang durante il lancio di un tipo allineato. Bug del compilatore?

Ecco un programma che riproduce il problema:

#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)); 
    } 
} 

E lo smontaggio con la localizzazione dei guasti segnato con ->:

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 

registro attuale:

(lldb) register read rax 
     rax = 0x0000000100905b08 

Si guarda come quello che sta succedendo è la funzione __cxa_allocate_exception non ha saputo sporgenza dei requisiti di allineamento del tipo per il quale sta allocando la memoria. Sul mio sistema capita di allocare un indirizzo che finisce in 8, e quindi non è allineato a 16 byte. Quando l'istruzione movaps tenta di spostare i dati in quella posizione di memoria, la CPU si guasta a causa di un accesso non allineato.

informazioni Compiler (clang da 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 

Si tratta di un bug del compilatore? Quale potrebbe essere un modo per aggirare questo problema?

UPDATE: ho presentato questo al database dei bug LLVM: https://llvm.org/bugs/show_bug.cgi?id=23868

+2

riprodotto sulla mia macchina con clangore, ma nessun errore con GCC 4.9. Sente l'odore di un piccolo bug del compilatore poco noto ... – nneonneo

+0

La soluzione ovvia sarebbe dichiarare il campo struct come 'uint16_t w [8]' e gestire i valori 'uint64_t' usando le funzioni dei membri accessor, se necessario. – rodrigo

+0

@rodrigo: la struttura 'int128' nel mio esempio è in realtà la struttura' BID_UINT128' dalla [Libreria matematica virgola mobile Intel decimal] (https://software.intel.com/en-us/articles/intel-decimal -floating-point-math-library), quindi non è pratico cambiarne la definizione. –

risposta

4

Guardando in questo un po 'più lontano, sembra che __cxa_allocate_exception è fondamentalmente mai definito da capire allineamento (per Clang o GCC), in modo da lancio di oggetti allineati fondamentalmente rientra in UB (beh, l'allineamento era un'estensione specifica del compilatore comunque ...). L'unico allineamento che sembra garantire è di 8 byte poiché questo è l'allineamento più grande richiesto da qualsiasi tipo integrato (double).

La soluzione più semplice mi viene in mente sarebbe semplicemente di utilizzare un tipo non allineato in 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)); 
    } 
} 
+0

Nella mia situazione, il tipo allineato in questione è in realtà un campo di un campo di una classe utilizzato in una gerarchia di eccezioni. Quindi sarebbe piuttosto imbarazzante dichiarare una versione non allineata del tipo solo per lanciare l'eccezione, e quindi doverla copiare su un'istanza correttamente allineata per usarla effettivamente nel gestore delle eccezioni. Grazie per l'idea, però. –

+1

Questo pone la domanda: perché stai usando gli oggetti allineati all'interno di una gerarchia di eccezioni? Lanciare oggetti allineati sembra essere UB fondamentalmente, non importa cosa ... – nneonneo

+0

L'oggetto allineato è un numero decimale a virgola mobile a 128 bit come indicato sopra. L'implementazione proviene da una biblioteca che non ho scritto. Un campo della mia eccezione contiene uno di questi oggetti che viene dichiarato come allineato. (Non sapevo nemmeno che fosse stato dichiarato allineato fino a quando mi sono imbattuto in questo bug.) –

Problemi correlati