Commit | Line | Data |
---|---|---|
bab37966 SM |
1 | /* Displaced stepping related things. |
2 | ||
3 | Copyright (C) 2020 Free Software Foundation, Inc. | |
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, ...) \ | |
36 | do \ | |
37 | { \ | |
38 | if (debug_displaced) \ | |
39 | debug_prefixed_printf ("displaced", __func__, fmt, ##__VA_ARGS__); \ | |
40 | } \ | |
41 | while (0) | |
42 | ||
bab37966 SM |
43 | enum displaced_step_prepare_status |
44 | { | |
45 | /* A displaced stepping buffer was successfully allocated and prepared. */ | |
46 | DISPLACED_STEP_PREPARE_STATUS_OK, | |
47 | ||
48 | /* This particular instruction can't be displaced stepped, GDB should fall | |
49 | back on in-line stepping. */ | |
50 | DISPLACED_STEP_PREPARE_STATUS_CANT, | |
51 | ||
52 | /* Not enough resources are available at this time, try again later. */ | |
53 | DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE, | |
54 | }; | |
55 | ||
56 | enum displaced_step_finish_status | |
57 | { | |
58 | /* Either the instruction was stepped and fixed up, or the specified thread | |
59 | wasn't executing a displaced step (in which case there's nothing to | |
60 | finish). */ | |
61 | DISPLACED_STEP_FINISH_STATUS_OK, | |
62 | ||
63 | /* The thread started a displaced step, but didn't complete it. */ | |
64 | DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED, | |
65 | }; | |
66 | ||
187b041e SM |
67 | /* Data returned by a gdbarch displaced_step_copy_insn method, to be passed to |
68 | the matching displaced_step_fixup method. */ | |
c7acb87b SM |
69 | |
70 | struct displaced_step_copy_insn_closure | |
71 | { | |
72 | virtual ~displaced_step_copy_insn_closure () = 0; | |
73 | }; | |
74 | ||
75 | using displaced_step_copy_insn_closure_up | |
76 | = std::unique_ptr<displaced_step_copy_insn_closure>; | |
77 | ||
78 | /* A simple displaced step closure that contains only a byte buffer. */ | |
79 | ||
80 | struct buf_displaced_step_copy_insn_closure : displaced_step_copy_insn_closure | |
81 | { | |
82 | buf_displaced_step_copy_insn_closure (int buf_size) | |
83 | : buf (buf_size) | |
84 | {} | |
85 | ||
187b041e SM |
86 | /* The content of this buffer is up to the user of the class, but typically |
87 | original instruction bytes, used during fixup to determine what needs to | |
88 | be fixed up. */ | |
c7acb87b SM |
89 | gdb::byte_vector buf; |
90 | }; | |
91 | ||
92 | /* Per-inferior displaced stepping state. */ | |
93 | ||
94 | struct displaced_step_inferior_state | |
95 | { | |
96 | displaced_step_inferior_state () | |
97 | { | |
98 | reset (); | |
99 | } | |
100 | ||
101 | /* Put this object back in its original state. */ | |
102 | void reset () | |
103 | { | |
187b041e SM |
104 | failed_before = false; |
105 | in_progress_count = 0; | |
106 | unavailable = false; | |
c7acb87b SM |
107 | } |
108 | ||
109 | /* True if preparing a displaced step ever failed. If so, we won't | |
110 | try displaced stepping for this inferior again. */ | |
187b041e | 111 | bool failed_before; |
c7acb87b | 112 | |
187b041e SM |
113 | /* Number of displaced steps in progress for this inferior. */ |
114 | unsigned int in_progress_count; | |
c7acb87b | 115 | |
187b041e SM |
116 | /* If true, this tells GDB that it's not worth asking the gdbarch displaced |
117 | stepping implementation to prepare a displaced step, because it would | |
118 | return UNAVAILABLE. This is set and reset by the gdbarch in the | |
119 | displaced_step_prepare and displaced_step_finish methods. */ | |
120 | bool unavailable; | |
121 | }; | |
c7acb87b | 122 | |
187b041e | 123 | /* Per-thread displaced stepping state. */ |
c7acb87b | 124 | |
187b041e SM |
125 | struct displaced_step_thread_state |
126 | { | |
127 | /* Return true if this thread is currently executing a displaced step. */ | |
128 | bool in_progress () const | |
129 | { | |
130 | return m_original_gdbarch != nullptr; | |
131 | } | |
132 | ||
133 | /* Return the gdbarch of the thread prior to the step. */ | |
134 | gdbarch *get_original_gdbarch () const | |
135 | { | |
136 | return m_original_gdbarch; | |
137 | } | |
138 | ||
139 | /* Mark this thread as currently executing a displaced step. | |
140 | ||
141 | ORIGINAL_GDBARCH is the current gdbarch of the thread (before the step | |
142 | is executed). */ | |
143 | void set (gdbarch *original_gdbarch) | |
144 | { | |
145 | m_original_gdbarch = original_gdbarch; | |
146 | } | |
147 | ||
148 | /* Mark this thread as no longer executing a displaced step. */ | |
149 | void reset () | |
150 | { | |
151 | m_original_gdbarch = nullptr; | |
152 | } | |
153 | ||
154 | private: | |
155 | gdbarch *m_original_gdbarch = nullptr; | |
156 | }; | |
157 | ||
480af54c | 158 | /* Control access to multiple displaced stepping buffers at fixed addresses. */ |
187b041e | 159 | |
480af54c | 160 | struct displaced_step_buffers |
187b041e | 161 | { |
480af54c SM |
162 | explicit displaced_step_buffers (gdb::array_view<CORE_ADDR> buffer_addrs) |
163 | { | |
164 | gdb_assert (buffer_addrs.size () > 0); | |
165 | ||
166 | m_buffers.reserve (buffer_addrs.size ()); | |
167 | ||
168 | for (CORE_ADDR buffer_addr : buffer_addrs) | |
169 | m_buffers.emplace_back (buffer_addr); | |
170 | } | |
187b041e SM |
171 | |
172 | displaced_step_prepare_status prepare (thread_info *thread, | |
173 | CORE_ADDR &displaced_pc); | |
174 | ||
175 | displaced_step_finish_status finish (gdbarch *arch, thread_info *thread, | |
176 | gdb_signal sig); | |
177 | ||
178 | const displaced_step_copy_insn_closure * | |
179 | copy_insn_closure_by_addr (CORE_ADDR addr); | |
180 | ||
181 | void restore_in_ptid (ptid_t ptid); | |
182 | ||
183 | private: | |
187b041e | 184 | |
480af54c SM |
185 | /* State of a single buffer. */ |
186 | ||
187 | struct displaced_step_buffer | |
188 | { | |
189 | explicit displaced_step_buffer (CORE_ADDR addr) | |
190 | : addr (addr) | |
191 | {} | |
192 | ||
193 | /* Address of the buffer. */ | |
194 | const CORE_ADDR addr; | |
195 | ||
196 | /* The original PC of the instruction currently being stepped. */ | |
197 | CORE_ADDR original_pc = 0; | |
198 | ||
199 | /* If set, the thread currently using the buffer. If unset, the buffer is not | |
200 | used. */ | |
201 | thread_info *current_thread = nullptr; | |
187b041e | 202 | |
480af54c SM |
203 | /* Saved copy of the bytes in the displaced buffer, to be restored once the |
204 | buffer is no longer used. */ | |
205 | gdb::byte_vector saved_copy; | |
c7acb87b | 206 | |
480af54c SM |
207 | /* Closure obtained from gdbarch_displaced_step_copy_insn, to be passed to |
208 | gdbarch_displaced_step_fixup_insn. */ | |
209 | displaced_step_copy_insn_closure_up copy_insn_closure; | |
210 | }; | |
187b041e | 211 | |
480af54c | 212 | std::vector<displaced_step_buffer> m_buffers; |
c7acb87b SM |
213 | }; |
214 | ||
bab37966 | 215 | #endif /* DISPLACED_STEPPING_H */ |