• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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