#13624: loadObj() does not respect alignment -------------------------------------+------------------------------------- Reporter: tmcdonell | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: Component: Runtime | Version: 8.0.1 System (Linker) | Keywords: | Operating System: Unknown/Multiple Architecture: | Type of failure: None/Unknown Unknown/Multiple | Test Case: | Blocked By: Blocking: | Related Tickets: Differential Rev(s): | Wiki Page: -------------------------------------+------------------------------------- This is perhaps known, but I'll write it down here in case somebody else runs into this problem as well. Since `loadObj()` just `mmap()`s the entire object file and decodes it ''in place'', it does not respect the alignment requirements specified in the section headers. This is problematic for instructions which require alignment, e.g. SSE, AVX. The attached `map.ll` program is `map (+1)` over an array of floating point numbers. In particular, the core loop is 8-way SIMD vectorised x 4-way unrolled, for 32-elements per loop iteration. A tail loop handles any remainder one-at-a-time. You can compile it using `llc -filetype=obj -mcpu=native map.ll`. For a CPU with AVX instructions (sandy bridge or later) you should get the following: {{{ $ objdump -d map.o Disassembly of section .text: 0000000000000000 <map>: 0: 49 89 f3 mov %rsi,%r11 3: 49 29 fb sub %rdi,%r11 6: 0f 8e f9 00 00 00 jle 105 <map+0x105> c: 49 83 fb 20 cmp $0x20,%r11 10: 0f 82 bd 00 00 00 jb d3 <map+0xd3> 16: 4d 89 da mov %r11,%r10 19: 49 83 e2 e0 and $0xffffffffffffffe0,%r10 1d: 4d 89 d9 mov %r11,%r9 20: 49 83 e1 e0 and $0xffffffffffffffe0,%r9 24: 0f 84 a9 00 00 00 je d3 <map+0xd3> 2a: 49 01 fa add %rdi,%r10 2d: 48 8d 44 ba 60 lea 0x60(%rdx,%rdi,4),%rax 32: 49 8d 7c b8 60 lea 0x60(%r8,%rdi,4),%rdi 37: c5 fc 28 05 00 00 00 vmovaps 0x0(%rip),%ymm0 # 3f <map+0x3f> 3e: 00 3f: 4c 89 c9 mov %r9,%rcx 42: 66 66 66 66 66 2e 0f data16 data16 data16 data16 nopw %cs:0x0(%rax,%rax,1) 49: 1f 84 00 00 00 00 00 50: c5 f8 10 4f a0 vmovups -0x60(%rdi),%xmm1 55: c5 f8 10 57 c0 vmovups -0x40(%rdi),%xmm2 5a: c5 f8 10 5f e0 vmovups -0x20(%rdi),%xmm3 5f: c5 f8 10 27 vmovups (%rdi),%xmm4 63: c4 e3 75 18 4f b0 01 vinsertf128 $0x1,-0x50(%rdi),%ymm1,%ymm1 6a: c4 e3 6d 18 57 d0 01 vinsertf128 $0x1,-0x30(%rdi),%ymm2,%ymm2 71: c4 e3 65 18 5f f0 01 vinsertf128 $0x1,-0x10(%rdi),%ymm3,%ymm3 78: c4 e3 5d 18 67 10 01 vinsertf128 $0x1,0x10(%rdi),%ymm4,%ymm4 7f: c5 f4 58 c8 vaddps %ymm0,%ymm1,%ymm1 83: c5 ec 58 d0 vaddps %ymm0,%ymm2,%ymm2 87: c5 e4 58 d8 vaddps %ymm0,%ymm3,%ymm3 8b: c5 dc 58 e0 vaddps %ymm0,%ymm4,%ymm4 8f: c4 e3 7d 19 48 b0 01 vextractf128 $0x1,%ymm1,-0x50(%rax) 96: c5 f8 11 48 a0 vmovups %xmm1,-0x60(%rax) 9b: c4 e3 7d 19 50 d0 01 vextractf128 $0x1,%ymm2,-0x30(%rax) a2: c5 f8 11 50 c0 vmovups %xmm2,-0x40(%rax) a7: c4 e3 7d 19 58 f0 01 vextractf128 $0x1,%ymm3,-0x10(%rax) ae: c5 f8 11 58 e0 vmovups %xmm3,-0x20(%rax) b3: c4 e3 7d 19 60 10 01 vextractf128 $0x1,%ymm4,0x10(%rax) ba: c5 f8 11 20 vmovups %xmm4,(%rax) be: 48 83 e8 80 sub $0xffffffffffffff80,%rax c2: 48 83 ef 80 sub $0xffffffffffffff80,%rdi c6: 48 83 c1 e0 add $0xffffffffffffffe0,%rcx ca: 75 84 jne 50 <map+0x50> cc: 4d 39 cb cmp %r9,%r11 cf: 75 05 jne d6 <map+0xd6> d1: eb 32 jmp 105 <map+0x105> d3: 49 89 fa mov %rdi,%r10 d6: 4c 29 d6 sub %r10,%rsi d9: 4a 8d 04 92 lea (%rdx,%r10,4),%rax dd: 4b 8d 0c 90 lea (%r8,%r10,4),%rcx e1: c5 fa 10 05 00 00 00 vmovss 0x0(%rip),%xmm0 # e9 <map+0xe9> e8: 00 e9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) f0: c5 fa 58 09 vaddss (%rcx),%xmm0,%xmm1 f4: c5 fa 11 08 vmovss %xmm1,(%rax) f8: 48 83 c0 04 add $0x4,%rax fc: 48 83 c1 04 add $0x4,%rcx 100: 48 ff ce dec %rsi 103: 75 eb jne f0 <map+0xf0> 105: c5 f8 77 vzeroupper 108: c3 req }}} The attached `test.c` will load the object file and try to execute it. The `#define N` on line 7 will change the size of the array. For fewer than 32 elements this works as expected (where the input array is [0..N-1]): {{{ $ ./build.sh + llc-4.0 -filetype=obj -mcpu=native map.ll + ghc --make -no-hs-main test.c $ ./a.out array size is 31 calling function... ok 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0 31.0 }}} For 32 elements or larger (i.e. entering the core loop) the program will (almost certainly) segfault. {{{ $ lldb a.out (lldb) target create "a.out" Current executable set to 'a.out' (x86_64). (lldb) run Process 7294 launched: '<snip>/a.out' (x86_64) array size is 32 calling function... Process 7294 stopped * thread #1: tid = 0xc41676, 0x000000010019f207, queue = 'com.apple.main- thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT) frame #0: 0x000000010019f207 -> 0x10019f207: vmovaps 0xe1(%rip), %ymm0 0x10019f20f: movq %r9, %rcx 0x10019f212: nopw %cs:(%rax,%rax) 0x10019f220: vmovups -0x60(%rdi), %xmm1 }}} The `VMOVAPS` instruction requires the source address to be 32-byte aligned. It is attempting to load 8 floats from one of the const sections (the ones for the +1), but since the section was not loaded at the required alignment, fails. I've tested this on x86_64 macOS (Mach-O) and ubuntu (ELF). I don't have any other systems to test on. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/13624> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler