I have a type that is declared with __attribute__((aligned(16)))
. When building with clang
on OS X on x86_64, the following code causes a GP fault when attempting to throw
a value containing this type. The fault happens because the compiler generates a 128-bit move instruction which must be aligned on a 16-byte boundary, but the address is not correctly aligned.
Here is a program that reproduces the problem:
#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));
}
}
And the disassembly with the fault location marked with ->
:
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
Current register:
(lldb) register read rax
rax = 0x0000000100905b08
It looks like what is happening is the __cxa_allocate_exception
function has no knowledge of the alignment requirements of the type for which it is allocating storage. On my system it happens to allocate an address that ends in 8, and is therefore not 16-byte aligned. When the movaps
instruction attempts to move data into that memory location, the CPU faults due to unaligned access.
Compiler info (clang
from 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
Is this a compiler bug? What might be a way to work around this?
UPDATE: I have submitted this to the LLVM bug database: https://llvm.org/bugs/show_bug.cgi?id=23868
Looking into this a bit further, it seems like __cxa_allocate_exception
is basically never defined to understand alignment (for Clang or GCC), so throwing aligned objects basically falls into UB (well, alignment was a compiler-specific extension anyway...). The only alignment it appears to guarantee is 8 bytes since that is the largest alignment required by any built-in type (double
).
The easiest workaround I can think of would be simply to use an unaligned type 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));
}
}
According to the LLVM bug tracking this issue, the internal definition of alignment used by __cxa_allocation_exception was updated to match GCC which assumes a "maximum useful alignment".
The Clang and libunwind unwind.h header state:
The Itanium ABI requires that _Unwind_Exception objects are "double-word aligned". GCC has interpreted this to mean "use the maximum useful alignment for the target"; so do we.
https://bugs.llvm.org/show_bug.cgi?id=23868
So it's technically an ABI issue that's been resolved by a looser implementation by clang and gcc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With