Commit | Line | Data |
---|---|---|
bab37966 SM |
1 | /* Displaced stepping related things. |
2 | ||
3666a048 | 3 | Copyright (C) 2020-2021 Free Software Foundation, Inc. |
bab37966 SM |
4 | |
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #ifndef DISPLACED_STEPPING_H | |
21 | #define DISPLACED_STEPPING_H | |
22 | ||
480af54c | 23 | #include "gdbsupport/array-view.h" |
c7acb87b SM |
24 | #include "gdbsupport/byte-vector.h" |
25 | ||
187b041e | 26 | struct gdbarch; |
c7acb87b SM |
27 | struct thread_info; |
28 | ||
29 | /* True if we are debugging displaced stepping. */ | |
30 | ||
31 | extern bool debug_displaced; | |
32 | ||
33 | /* Print a "displaced" debug statement. */ | |
34 | ||
35 | #define displaced_debug_printf(fmt, ...) \ | |
74b773fc | 36 | debug_prefixed_printf_cond (debug_displaced, "displaced",fmt, ##__VA_ARGS__) |
c7acb87b | 37 | |
bab37966 SM |
38 | enum displaced_step_prepare_status |
39 | { | |
40 | /* A displaced stepping buffer was successfully allocated and prepared. */ | |
41 | DISPLACED_STEP_PREPARE_STATUS_OK, | |
42 | ||
43 | /* This particular instruction can't be displaced stepped, GDB should fall | |
44 | back on in-line stepping. */ | |
45 | DISPLACED_STEP_PREPARE_STATUS_CANT, | |
46 | ||
47 | /* Not enough resources are available at this time, try again later. */ | |
48 | DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE, | |
49 | }; | |
50 | ||
51 | enum displaced_step_finish_status | |
52 | { | |
53 | /* Either the instruction was stepped and fixed up, or the specified thread | |
54 | wasn't executing a displaced step (in which case there's nothing to | |
55 | finish). */ | |
56 | DISPLACED_STEP_FINISH_STATUS_OK, | |
57 | ||
58 | /* The thread started a displaced step, but didn't complete it. */ | |
59 | DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED, | |
60 | }; | |
61 | ||
187b041e SM |
62 | /* Data returned by a gdbarch displaced_step_copy_insn method, to be passed to |
63 | the matching displaced_step_fixup method. */ | |
c7acb87b SM |
64 | |
65 | struct displaced_step_copy_insn_closure | |
66 | { | |
67 | virtual ~displaced_step_copy_insn_closure () = 0; | |
68 | }; | |
69 | ||
70 | using displaced_step_copy_insn_closure_up | |
71 | = std::unique_ptr<displaced_step_copy_insn_closure>; | |
72 | ||
73 | /* A simple displaced step closure that contains only a byte buffer. */ | |
74 | ||
75 | struct buf_displaced_step_copy_insn_closure : displaced_step_copy_insn_closure | |
76 | { | |
77 | buf_displaced_step_copy_insn_closure (int buf_size) | |
78 | : buf (buf_size) | |
79 | {} | |
80 | ||
187b041e SM |
81 | /* The content of this buffer is up to the user of the class, but typically |
82 | original instruction bytes, used during fixup to determine what needs to | |
83 | be fixed up. */ | |
c7acb87b SM |
84 | gdb::byte_vector buf; |
85 | }; | |
86 | ||
87 | /* Per-inferior displaced stepping state. */ | |
88 | ||
89 | struct displaced_step_inferior_state | |
90 | { | |
91 | displaced_step_inferior_state () | |
92 | { | |
93 | reset (); | |
94 | } | |
95 | ||
96 | /* Put this object back in its original state. */ | |
97 | void reset () | |
98 | { | |
187b041e SM |
99 | failed_before = false; |
100 | in_progress_count = 0; | |
101 | unavailable = false; | |
c7acb87b SM |
102 | } |
103 | ||
104 | /* True if preparing a displaced step ever failed. If so, we won't | |
105 | try displaced stepping for this inferior again. */ | |
187b041e | 106 | bool failed_before; |
c7acb87b | 107 | |
187b041e SM |
108 | /* Number of displaced steps in progress for this inferior. */ |
109 | unsigned int in_progress_count; | |
c7acb87b | 110 | |
187b041e SM |
111 | /* If true, this tells GDB that it's not worth asking the gdbarch displaced |
112 | stepping implementation to prepare a displaced step, because it would | |
113 | return UNAVAILABLE. This is set and reset by the gdbarch in the | |
114 | displaced_step_prepare and displaced_step_finish methods. */ | |
115 | bool unavailable; | |
116 | }; | |
c7acb87b | 117 | |
187b041e | 118 | /* Per-thread displaced stepping state. */ |
c7acb87b | 119 | |
187b041e SM |
120 | struct displaced_step_thread_state |
121 | { | |
122 | /* Return true if this thread is currently executing a displaced step. */ | |
123 | bool in_progress () const | |
124 | { | |
125 | return m_original_gdbarch != nullptr; | |
126 | } | |
127 | ||
128 | /* Return the gdbarch of the thread prior to the step. */ | |
129 | gdbarch *get_original_gdbarch () const | |
130 | { | |
131 | return m_original_gdbarch; | |
132 | } | |
133 | ||
134 | /* Mark this thread as currently executing a displaced step. | |
135 | ||
136 | ORIGINAL_GDBARCH is the current gdbarch of the thread (before the step | |
137 | is executed). */ | |
138 | void set (gdbarch *original_gdbarch) | |
139 | { | |
140 | m_original_gdbarch = original_gdbarch; | |
141 | } | |
142 | ||
143 | /* Mark this thread as no longer executing a displaced step. */ | |
144 | void reset () | |
145 | { | |
146 | m_original_gdbarch = nullptr; | |
147 | } | |
148 | ||
149 | private: | |
150 | gdbarch *m_original_gdbarch = nullptr; | |
151 | }; | |
152 | ||
480af54c | 153 | /* Control access to multiple displaced stepping buffers at fixed addresses. */ |
187b041e | 154 | |
480af54c | 155 | struct displaced_step_buffers |
187b041e | 156 | { |
480af54c SM |
157 | explicit displaced_step_buffers (gdb::array_view<CORE_ADDR> buffer_addrs) |
158 | { | |
159 | gdb_assert (buffer_addrs.size () > 0); | |
160 | ||
161 | m_buffers.reserve (buffer_addrs.size ()); | |
162 | ||
163 | for (CORE_ADDR buffer_addr : buffer_addrs) | |
164 | m_buffers.emplace_back (buffer_addr); | |
165 | } | |
187b041e SM |
166 | |
167 | displaced_step_prepare_status prepare (thread_info *thread, | |
168 | CORE_ADDR &displaced_pc); | |
169 | ||
170 | displaced_step_finish_status finish (gdbarch *arch, thread_info *thread, | |
171 | gdb_signal sig); | |
172 | ||
173 | const displaced_step_copy_insn_closure * | |
174 | copy_insn_closure_by_addr (CORE_ADDR addr); | |
175 | ||
176 | void restore_in_ptid (ptid_t ptid); | |
177 | ||
178 | private: | |
187b041e | 179 | |
480af54c SM |
180 | /* State of a single buffer. */ |
181 | ||
182 | struct displaced_step_buffer | |
183 | { | |
184 | explicit displaced_step_buffer (CORE_ADDR addr) | |
185 | : addr (addr) | |
186 | {} | |
187 | ||
188 | /* Address of the buffer. */ | |
189 | const CORE_ADDR addr; | |
190 | ||
191 | /* The original PC of the instruction currently being stepped. */ | |
192 | CORE_ADDR original_pc = 0; | |
193 | ||
194 | /* If set, the thread currently using the buffer. If unset, the buffer is not | |
195 | used. */ | |
196 | thread_info *current_thread = nullptr; | |
187b041e | 197 | |
480af54c SM |
198 | /* Saved copy of the bytes in the displaced buffer, to be restored once the |
199 | buffer is no longer used. */ | |
200 | gdb::byte_vector saved_copy; | |
c7acb87b | 201 | |
480af54c SM |
202 | /* Closure obtained from gdbarch_displaced_step_copy_insn, to be passed to |
203 | gdbarch_displaced_step_fixup_insn. */ | |
204 | displaced_step_copy_insn_closure_up copy_insn_closure; | |
205 | }; | |
187b041e | 206 | |
480af54c | 207 | std::vector<displaced_step_buffer> m_buffers; |
c7acb87b SM |
208 | }; |
209 | ||
bab37966 | 210 | #endif /* DISPLACED_STEPPING_H */ |