diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d519e17..4a844a07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ * Infer jump table boundaries from comparisons of registers correlated to the index register. * Relax constraints for inferring jump table boundaries from comparisons of indirect operands * Fix bug where a relative jump table starting with consecutive zero offsets was truncated at the first non-zero value. +* Add alignment for x86-64 instructions that require explicitly aligned memory + (e.g., some SIMD instructions) # 1.8.0 diff --git a/examples/asm_examples/ex_aligned_data_in_code/Makefile b/examples/asm_examples/ex_aligned_data_in_code/Makefile new file mode 100644 index 00000000..c3181abd --- /dev/null +++ b/examples/asm_examples/ex_aligned_data_in_code/Makefile @@ -0,0 +1,10 @@ + +all: ex_original.s + gcc ex_original.s -o ex + @./ex > out.txt +clean: + rm -f ex out.txt + rm -fr ex.unstripped ex.s *.old* dl_files *.gtirb +check: + ./ex > /tmp/res.txt + @ diff out.txt /tmp/res.txt && echo TEST OK diff --git a/examples/asm_examples/ex_aligned_data_in_code/ex_original.s b/examples/asm_examples/ex_aligned_data_in_code/ex_original.s new file mode 100644 index 00000000..368f2e94 --- /dev/null +++ b/examples/asm_examples/ex_aligned_data_in_code/ex_original.s @@ -0,0 +1,82 @@ +# This example is to demonostrate that data-in-code is properly aligned +# when it is referenced by instructions that require explicitly aligned memory. +# If not properly aligned, it may cause a segmentation fault due to alignment +# requirement violation. +# See Table 15-6 in https://cdrdv2.intel.com/v1/dl/getContent/671200. + + .section .text + +.globl main +.type main, @function +main: + call print_message1 + + # Load data into XMM register using movdqa: `data128.1` needs to be aligned. + movdqa data128.1(%rip), %xmm0 + + # A pair of instructions from an access to `data128.2`, which needs to + # be aligned. + lea data128.2(%rip), %rax + movdqa 0(%rax), %xmm1 + + # Load data into YMM register using movdqa: `data256` needs to be aligned. + vmovapd data256(%rip), %ymm0 + + # Load data into ZMM register using movdqa: `data512` needs to be aligned. + vmovaps data512(%rip), %zmm0 + + # Load data into ZMM register using vmovups: `data512u` does not need to be aligned. + vmovups data512u(%rip), %zmm1 + + call print_message2 + + xorq %rax, %rax + + ret + +.type print_message1, @function +print_message1: + lea message1(%rip), %rdi + call printf + ret + +.align 16 +.type print_message2, @function +print_message2: + lea message2(%rip), %rdi + call printf + ret + .zero 3 + +.align 16 +data128.1: + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +.align 16 +data128.2: + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +.align 32 +data256: + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 +.align 64 +data512: + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + + .zero 3 +data512u: + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + .byte 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + + .section .data + +message1: + .ascii "Performing SIMD operations...\n" + .byte 0 +message2: + .ascii "SIMD operations completed.\n" + .byte 0 diff --git a/src/datalog/arch/arch.dl b/src/datalog/arch/arch.dl index 3b0165a2..9baee8ef 100644 --- a/src/datalog/arch/arch.dl +++ b/src/datalog/arch/arch.dl @@ -187,6 +187,7 @@ cmp_zero_operation(""):- .decl call_operation_op_index(Operation:symbol,operand_index:operand_index) + // =========================================================================== // Address-targeted instruction properties // =========================================================================== @@ -544,6 +545,14 @@ invalid(), etc. instruction_at(Instruction,Instruction):- instruction(Instruction,_,_,_,_,_,_,_,_,_). +/** +Instruction at EA requires alignment on the referenced memory. +*/ +.decl alignment_required(EA:address,AlignInBits:unsigned) + +alignment_required(0,0):- + false. + /** The size of a register, in bytes. */ diff --git a/src/datalog/arch/intel/arch_x86.dl b/src/datalog/arch/intel/arch_x86.dl index 5925a4b3..c4082033 100644 --- a/src/datalog/arch/intel/arch_x86.dl +++ b/src/datalog/arch/intel/arch_x86.dl @@ -273,4 +273,31 @@ simple_data_load(EA,Data,Size):- instruction_memory_access_size(EA,MemIndex,Size), Size != 0. +// Operation that requires aligned memory +.decl operation_alignment_required(Operation:symbol) + +// The following AVX instructions require explicitly aligned memory +// (See Table 15-6 in https://cdrdv2.intel.com/v1/dl/getContent/671200): + +operation_alignment_required("MOVDQA"). +operation_alignment_required("MOVAPS"). +operation_alignment_required("MOVAPD"). +operation_alignment_required("MOVNTPS"). +operation_alignment_required("MOVNTPD"). +operation_alignment_required("MOVNTDQ"). +operation_alignment_required("MOVNTDQA"). + +operation_alignment_required("VMOVDQA"). +operation_alignment_required("VMOVAPS"). +operation_alignment_required("VMOVAPD"). +operation_alignment_required("VMOVNTPS"). +operation_alignment_required("VMOVNTPD"). +operation_alignment_required("VMOVNTDQ"). +operation_alignment_required("VMOVNTDQA"). + +alignment_required(EA,AlignInBits):- + instruction_get_operation(EA,Operation), + operation_alignment_required(Operation), + instruction_memory_access_size(EA,_,AlignInBits). + } diff --git a/src/datalog/main.dl b/src/datalog/main.dl index d67ca456..f2446937 100644 --- a/src/datalog/main.dl +++ b/src/datalog/main.dl @@ -696,6 +696,18 @@ Information about alignment in bits for a given address alignment(0,0):- false. +// Data in code needs to be aligned when referenced by instruction that +// requires aligned memory: e.g., some SIMD instructions +alignment(DataEA, AlignInBits):- + arch.alignment_required(EA,AlignInBits), + ( + pc_relative_operand(EA,_,DataEA); + composite_data_access(_,EA,DataEA,AlignInBits) + ), + data_in_code(Begin,End), + DataEA >= Begin, + DataEA < End. + ////////////////////////////////////////////////////////////////////////////////// // Operations to abstract features of instructions diff --git a/tests/misc_test.py b/tests/misc_test.py index fbe5560f..a04e7562 100644 --- a/tests/misc_test.py +++ b/tests/misc_test.py @@ -436,6 +436,30 @@ def test_soname(self): self.assertEqual(m.aux_data["elfSoname"].data, binary) + @unittest.skipUnless( + platform.system() == "Linux", "This test is linux only." + ) + def test_aligned_data_in_code(self): + """ + Test that alignment directives are correctly generated for + data_in_code referenced by instructions that require aligned memory. + """ + binary = "ex" + with cd(ex_asm_dir / "ex_aligned_data_in_code"): + self.assertTrue(compile("gcc", "g++", "-O0", [])) + ir = disassemble(Path(binary)).ir() + m = ir.modules[0] + + alignments = m.aux_data["alignment"].data.items() + alignment_list = [alignment for uuid, alignment in alignments] + + # alignment=16: `data128.1`, `data128.2`, and `main` + self.assertEqual(alignment_list.count(16), 3) + # alignment=32: `data256` + self.assertEqual(alignment_list.count(32), 1) + # alignment=64: `data512` and `_start` + self.assertEqual(alignment_list.count(64), 2) + class RawGtirbTests(unittest.TestCase): @unittest.skipUnless(