## 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