/src/elfutils/backends/s390_unwind.c
Line | Count | Source (jump to first uncovered line) |
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 | | 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 | 0 | { |
50 | | /* Caller already assumed caller adjustment but S390 instructions are 4 bytes |
51 | | long. Undo it. */ |
52 | 0 | if ((pc & 0x3) != 0x3) |
53 | 0 | return false; |
54 | 0 | pc++; |
55 | | /* We can assume big-endian read here. */ |
56 | 0 | Dwarf_Word instr; |
57 | 0 | if (! readfunc (pc, &instr, arg)) |
58 | 0 | return false; |
59 | | /* Fetch only the very first two bytes. */ |
60 | 0 | instr = (instr >> (ebl->class == ELFCLASS64 ? 48 : 16)) & 0xffff; |
61 | | /* See GDB s390_sigtramp_frame_sniffer. */ |
62 | | /* Check for 'svc' as the first instruction. */ |
63 | 0 | if (((instr >> 8) & 0xff) != 0x0a) |
64 | 0 | return false; |
65 | | /* Check for 'sigreturn' or 'rt_sigreturn' as the second instruction. */ |
66 | 0 | if ((instr & 0xff) != 119 && (instr & 0xff) != 173) |
67 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | return false; |
86 | 0 | if (! setfunc (-1, 1, &val, arg)) |
87 | 0 | 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 | 0 | { |
93 | 0 | if (! readfunc (sigreg_ptr, &gprs[i], arg)) |
94 | 0 | return false; |
95 | 0 | sigreg_ptr += word_size; |
96 | 0 | } |
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 | 0 | { |
106 | 0 | if (! readfunc (sigreg_ptr, &val, arg)) |
107 | 0 | return false; |
108 | 0 | if (ebl->class == ELFCLASS32) |
109 | 0 | { |
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 | 0 | } |
115 | 0 | fprs[i] = val; |
116 | 0 | sigreg_ptr += 8; |
117 | 0 | } |
118 | | /* If we have them, the GPR upper halves are appended at the end. */ |
119 | 0 | if (ebl->class == ELFCLASS32) |
120 | 0 | { |
121 | | /* Skip signal number. */ |
122 | 0 | sigreg_ptr += 4; |
123 | 0 | for (int i = 0; i < 16; i++) |
124 | 0 | { |
125 | 0 | if (! readfunc (sigreg_ptr, &val, arg)) |
126 | 0 | 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 | 0 | } |
132 | 0 | } |
133 | 0 | if (! setfunc (0, 16, gprs, arg)) |
134 | 0 | return false; |
135 | 0 | if (! setfunc (16, 16, fprs, arg)) |
136 | 0 | return false; |
137 | 0 | *signal_framep = true; |
138 | 0 | return true; |
139 | 0 | } |