| # Check cases when the first PIC jump table entries of one function can be |
| # interpreted as valid last entries of the previous function. |
| |
| # Conditions to trigger the bug: Function A and B have jump tables that |
| # are adjacent in memory. We run in lite relocation mode. Function B |
| # is not disassembled because it does not have profile. Function A |
| # triggers a special conditional that forced BOLT to rewrite its jump |
| # table in-place (instead of moving it) because it is marked as |
| # non-simple (in this case, containing unknown control flow). The |
| # first entry of B's jump table (a PIC offset) happens to be a valid |
| # address inside A when added to A's jump table base address. In this |
| # case, BOLT could overwrite B's jump table, corrupting it, thinking |
| # the first entry of it is actually part of A's jump table. |
| |
| # REQUIRES: system-linux |
| |
| # RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \ |
| # RUN: %s -o %t.o |
| # RUN: link_fdata %s %t.o %t.fdata |
| # RUN: llvm-strip --strip-unneeded %t.o |
| # RUN: ld.lld %t.o -o %t.exe -q -T %S/Inputs/jt-pic-linkerscript.ld |
| # RUN: llvm-bolt %t.exe -relocs -o %t.out -data %t.fdata \ |
| # RUN: -lite=1 |
| # RUN: llvm-readelf -S %t.out | FileCheck --check-prefix=CHECK %s |
| # The output binary is runnable, but we check for test success with |
| # readelf. This is another way to check this bug: |
| # COM: %t.out |
| |
| # BOLT needs to create a new rodata section, indicating that it |
| # successfully moved the jump table in _start. |
| # CHECK: [{{.*}}] .bolt.org.rodata |
| |
| .globl _start |
| .type _start, %function |
| _start: |
| .cfi_startproc |
| # FDATA: 0 [unknown] 0 1 _start 0 0 1 |
| push %rbp |
| mov %rsp, %rbp |
| mov 0x8(%rbp), %rdi |
| cmpq $3, %rdi |
| ja .L5 |
| jmp .L6 |
| # Unreachable code, here to mark this function as non-simple |
| # (containing unknown control flow) with a stray indirect jmp |
| jmp *%rax |
| .L6: |
| decq %rdi |
| leaq .LJT1(%rip), %rcx |
| movslq (%rcx, %rdi, 4), %rax |
| addq %rcx, %rax |
| jmp *%rax |
| .L1: |
| leaq str1(%rip), %rsi |
| jmp .L4 |
| .L2: |
| leaq str2(%rip), %rsi |
| jmp .L4 |
| .L3: |
| leaq str3(%rip), %rsi |
| jmp .L4 |
| .L5: |
| leaq str4(%rip), %rsi |
| .L4: |
| movq $1, %rdi |
| movq $10, %rdx |
| movq $1, %rax |
| syscall |
| mov 0x8(%rbp), %rdi |
| decq %rdi |
| callq func_b |
| movq %rax, %rdi |
| movq $231, %rax |
| syscall |
| pop %rbp |
| ret |
| .cfi_endproc |
| .size _start, .-_start |
| |
| .globl func_b |
| .type func_b, %function |
| func_b: |
| .cfi_startproc |
| push %rbp |
| mov %rsp, %rbp |
| cmpq $3, %rdi |
| ja .L2_6 |
| # FT |
| leaq .LJT2(%rip), %rcx |
| movslq (%rcx, %rdi, 4), %rax |
| addq %rcx, %rax |
| jmp *%rax |
| .L2_1: |
| movq $0, %rax |
| jmp .L2_5 |
| .L2_2: |
| movq $1, %rax |
| jmp .L2_5 |
| .L2_3: |
| movq $2, %rax |
| jmp .L2_5 |
| .L2_4: |
| movq $3, %rax |
| jmp .L2_5 |
| .L2_6: |
| movq $-1, %rax |
| .L2_5: |
| popq %rbp |
| ret |
| .cfi_endproc |
| .size func_b, .-func_b |
| |
| .rodata |
| str1: .asciz "Message 1\n" |
| str2: .asciz "Message 2\n" |
| str3: .asciz "Message 3\n" |
| str4: .asciz "Highrange\n" |
| # Special case where the first .LJT2 entry is a valid offset of |
| # _start when interpreted with .LJT1 as a base address. |
| .LJT1: |
| .long .L1-.LJT1 |
| .long .L2-.LJT1 |
| .long .L3-.LJT1 |
| .long .L3-.LJT1 |
| .long .L3-.LJT1 |
| .long .L3-.LJT1 |
| .long .L3-.LJT1 |
| .LJT2: |
| .long .L2_1-.LJT2 |
| .long .L2_2-.LJT2 |
| .long .L2_3-.LJT2 |
| .long .L2_4-.LJT2 |