1.global DW_CFA_expression_testcase 2 3.extern recover_register 4 5.text 6 7# CFI expressions were added in DWARF v3 to allow compilers to specify memory 8# locations or register values using DWARF programs. These programs are simple 9# stack-based operations which allow the compiler to encode integer mathematics 10# and other complex logic. CFI expressions are therefore more powerful than the 11# conventional register + offset schemes. 12# 13# These tests capture a bug we have fixed in libunwind. CFI expression programs 14# always start with the current CFA pushed onto the stack. This file contains a 15# pair of routines which test CFI expression parsing. Specifically they test 16# DW_CFA_expression logic, which uses DWARF expressions to compute the address 17# where a non-volatile register was stored. 18# 19# Main calls DW_CFA_expression_testcase, which sets up known state in a 20# non-volatile (caller-saved) register. We use r12 for this purpose. After this 21# DW_CFA_expression_testcase then calls DW_CFA_expression_inner, which clobbers 22# r12 after stashing its value on the stack. This routine contains a DWARF3 CFI 23# expression to restore the value of r12 on unwind which should allow libunwind 24# to recover clobbered state. DW_CFA_expression_inner calls recover_register to 25# retrieve the cached register value. This function recovers the register value 26# by using libunwind to unwind the stack through DW_CFA_expression_inner and up 27# to the call site in DW_CFA_expression_testcase. If our expression is correct, 28# libunwind will be able to restore r12 from the stack. 29# 30# BE CAREFUL WITH rdi, rsi, rax HERE! The arguments to recover_register are 31# passed in via rdi, rsi and I just let them flow through unchanged. Similarly 32# RAX flows back unchanged. Adding any function calls to the below may clobber 33# these registers and cause this test to fail mysteriously. 34 35 36######################################################## 37# Test: Restoring a register using a DW_CFA_expression # 38# which uses implicit CFA pushed onto stack. # 39######################################################## 40 41.type DW_CFA_expression_testcase STT_FUNC 42DW_CFA_expression_testcase: 43 .cfi_startproc 44 push %r12 45 .cfi_adjust_cfa_offset 8 46 # Move our sentinel (known) value into non-volatile (Callee-saved) r12 47 mov $111222333, %r12 48 .cfi_rel_offset %r12, 0 49 call DW_CFA_expression_inner 50 pop %r12 51 .cfi_restore %r12 52 .cfi_adjust_cfa_offset -8 53 ret 54 .cfi_endproc 55.size DW_CFA_expression_testcase,.-DW_CFA_expression_testcase 56 57.type DW_CFA_expression_inner STT_FUNC 58DW_CFA_expression_inner: 59 .cfi_startproc 60 push %r12 61 .cfi_adjust_cfa_offset 8 62 # !! IMPORTANT BIT !! The test is all about how we parse the following bytes. 63 # Now we use an expression to describe where our sentinel value is stored: 64 # DW_CFA_expression(0x10), r12(0x0c), Length(0x02), (preamble) 65 # DW_OP_lit16(0x40), DW_OP_minus(0x1c) (instructions) 66 # Parsing starts with the CFA on the stack, then pushes 16, then does a minus 67 # which is eqivalent to a=pop(), b=pop(), push(b-a), leaving us with a value 68 # of cfa-16 (cfa points at old rsp, cfa-8 is our rip, so we stored r12 at 69 # cfa-16). 70 xor %r12, %r12 # Trash r12 71 .cfi_escape 0x10, 0x0c, 0x2, 0x40, 0x1c # DW_CFA_expression for recovery 72 call recover_register 73 pop %r12 74 .cfi_restore %r12 75 .cfi_adjust_cfa_offset -8 76 ret 77 .cfi_endproc 78.size DW_CFA_expression_inner,.-DW_CFA_expression_inner 79