llvm/bolt/test/X86/jump-table-pic-conflict.s

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