Branch data Line data Source code
1 : : /* Get previous frame state for an existing frame state.
2 : : Copyright (C) 2013 Red Hat, Inc.
3 : : This file is part of elfutils.
4 : :
5 : : This file is free software; you can redistribute it and/or modify
6 : : it under the terms of either
7 : :
8 : : * the GNU Lesser General Public License as published by the Free
9 : : Software Foundation; either version 3 of the License, or (at
10 : : your option) any later version
11 : :
12 : : or
13 : :
14 : : * the GNU General Public License as published by the Free
15 : : Software Foundation; either version 2 of the License, or (at
16 : : your option) any later version
17 : :
18 : : or both in parallel, as here.
19 : :
20 : : elfutils is distributed in the hope that it will be useful, but
21 : : WITHOUT ANY WARRANTY; without even the implied warranty of
22 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 : : General Public License for more details.
24 : :
25 : : You should have received copies of the GNU General Public License and
26 : : the GNU Lesser General Public License along with this program. If
27 : : not, see <http://www.gnu.org/licenses/>. */
28 : :
29 : : #ifdef HAVE_CONFIG_H
30 : : # include <config.h>
31 : : #endif
32 : :
33 : : #include <stdlib.h>
34 : : #include <assert.h>
35 : :
36 : : #define BACKEND s390_
37 : : #include "libebl_CPU.h"
38 : :
39 : : /* s390/s390x do not annotate signal handler frame by CFI. It would be also
40 : : difficult as PC points into a stub built on stack. Function below is called
41 : : only if unwinder could not find CFI. Function then verifies the register
42 : : state for this frame really belongs to a signal frame. In such case it
43 : : fetches original registers saved by the signal frame. */
44 : :
45 : : bool
46 : 16 : s390_unwind (Ebl *ebl, Dwarf_Addr pc, ebl_tid_registers_t *setfunc,
47 : : ebl_tid_registers_get_t *getfunc, ebl_pid_memory_read_t *readfunc,
48 : : void *arg, bool *signal_framep)
49 : : {
50 : : /* Caller already assumed caller adjustment but S390 instructions are 4 bytes
51 : : long. Undo it. */
52 [ + + ]: 16 : if ((pc & 0x3) != 0x3)
53 : : return false;
54 : 12 : pc++;
55 : : /* We can assume big-endian read here. */
56 : 12 : Dwarf_Word instr;
57 [ - + ]: 12 : if (! readfunc (pc, &instr, arg))
58 : : return false;
59 : : /* Fetch only the very first two bytes. */
60 [ + + ]: 12 : instr = (instr >> (ebl->class == ELFCLASS64 ? 48 : 16)) & 0xffff;
61 : : /* See GDB s390_sigtramp_frame_sniffer. */
62 : : /* Check for 'svc' as the first instruction. */
63 [ + - ]: 12 : if (((instr >> 8) & 0xff) != 0x0a)
64 : : return false;
65 : : /* Check for 'sigreturn' or 'rt_sigreturn' as the second instruction. */
66 [ # # ]: 0 : if ((instr & 0xff) != 119 && (instr & 0xff) != 173)
67 : : return false;
68 : : /* See GDB s390_sigtramp_frame_unwind_cache. */
69 : 0 : Dwarf_Word this_sp;
70 [ # # ]: 0 : if (! getfunc (0 + 15, 1, &this_sp, arg))
71 : : return false;
72 [ # # ]: 0 : unsigned word_size = ebl->class == ELFCLASS64 ? 8 : 4;
73 : 0 : Dwarf_Addr next_cfa = this_sp + 16 * word_size + 32;
74 : : /* "New-style RT frame" is not supported,
75 : : assuming "Old-style RT frame and all non-RT frames".
76 : : Pointer to the array of saved registers is at NEXT_CFA + 8. */
77 : 0 : Dwarf_Word sigreg_ptr;
78 [ # # ]: 0 : if (! readfunc (next_cfa + 8, &sigreg_ptr, arg))
79 : : return false;
80 : : /* Skip PSW mask. */
81 : 0 : sigreg_ptr += word_size;
82 : : /* Read PSW address. */
83 : 0 : Dwarf_Word val;
84 [ # # ]: 0 : if (! readfunc (sigreg_ptr, &val, arg))
85 : : return false;
86 [ # # ]: 0 : if (! setfunc (-1, 1, &val, arg))
87 : : return false;
88 : 0 : sigreg_ptr += word_size;
89 : : /* Then the GPRs. */
90 : 0 : Dwarf_Word gprs[16];
91 [ # # ]: 0 : for (int i = 0; i < 16; i++)
92 : : {
93 [ # # ]: 0 : if (! readfunc (sigreg_ptr, &gprs[i], arg))
94 : : return false;
95 : 0 : sigreg_ptr += word_size;
96 : : }
97 : : /* Then the ACRs. Skip them, they are not used in CFI. */
98 [ # # ]: 0 : for (int i = 0; i < 16; i++)
99 : 0 : sigreg_ptr += 4;
100 : : /* The floating-point control word. */
101 : 0 : sigreg_ptr += 8;
102 : : /* And finally the FPRs. */
103 : 0 : Dwarf_Word fprs[16];
104 [ # # ]: 0 : for (int i = 0; i < 16; i++)
105 : : {
106 [ # # ]: 0 : if (! readfunc (sigreg_ptr, &val, arg))
107 : : return false;
108 [ # # ]: 0 : if (ebl->class == ELFCLASS32)
109 : : {
110 : 0 : Dwarf_Addr val_low;
111 [ # # ]: 0 : if (! readfunc (sigreg_ptr + 4, &val_low, arg))
112 : 0 : return false;
113 : 0 : val = (val << 32) | val_low;
114 : : }
115 : 0 : fprs[i] = val;
116 : 0 : sigreg_ptr += 8;
117 : : }
118 : : /* If we have them, the GPR upper halves are appended at the end. */
119 [ # # ]: 0 : if (ebl->class == ELFCLASS32)
120 : : {
121 : : /* Skip signal number. */
122 : 0 : sigreg_ptr += 4;
123 [ # # ]: 0 : for (int i = 0; i < 16; i++)
124 : : {
125 [ # # ]: 0 : if (! readfunc (sigreg_ptr, &val, arg))
126 : : return false;
127 : 0 : Dwarf_Word val_low = gprs[i];
128 : 0 : val = (val << 32) | val_low;
129 : 0 : gprs[i] = val;
130 : 0 : sigreg_ptr += 4;
131 : : }
132 : : }
133 [ # # ]: 0 : if (! setfunc (0, 16, gprs, arg))
134 : : return false;
135 [ # # ]: 0 : if (! setfunc (16, 16, fprs, arg))
136 : : return false;
137 : 0 : *signal_framep = true;
138 : 0 : return true;
139 : : }
|