Automatic Copyright Year update after running gdb/copyright.py
[deliverable/binutils-gdb.git] / gdb / displaced-stepping.c
CommitLineData
c7acb87b
SM
1/* Displaced stepping related things.
2
88b9d363 3 Copyright (C) 2020-2022 Free Software Foundation, Inc.
c7acb87b
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#include "defs.h"
21#include "displaced-stepping.h"
c7acb87b 22
187b041e 23#include "cli/cli-cmds.h"
c7acb87b 24#include "command.h"
187b041e
SM
25#include "gdbarch.h"
26#include "gdbcore.h"
27#include "gdbthread.h"
28#include "inferior.h"
29#include "regcache.h"
30#include "target/target.h"
c7acb87b
SM
31
32/* Default destructor for displaced_step_copy_insn_closure. */
33
34displaced_step_copy_insn_closure::~displaced_step_copy_insn_closure ()
35 = default;
36
37bool debug_displaced = false;
38
39static void
40show_debug_displaced (struct ui_file *file, int from_tty,
41 struct cmd_list_element *c, const char *value)
42{
43 fprintf_filtered (file, _("Displace stepping debugging is %s.\n"), value);
44}
45
187b041e 46displaced_step_prepare_status
480af54c 47displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
187b041e
SM
48{
49 gdb_assert (!thread->displaced_step_state.in_progress ());
50
480af54c
SM
51 /* Sanity check: the thread should not be using a buffer at this point. */
52 for (displaced_step_buffer &buf : m_buffers)
53 gdb_assert (buf.current_thread != thread);
187b041e
SM
54
55 regcache *regcache = get_thread_regcache (thread);
56 const address_space *aspace = regcache->aspace ();
57 gdbarch *arch = regcache->arch ();
58 ULONGEST len = gdbarch_max_insn_length (arch);
59
480af54c
SM
60 /* Search for an unused buffer. */
61 displaced_step_buffer *buffer = nullptr;
62 displaced_step_prepare_status fail_status
63 = DISPLACED_STEP_PREPARE_STATUS_CANT;
187b041e 64
480af54c
SM
65 for (displaced_step_buffer &candidate : m_buffers)
66 {
67 bool bp_in_range = breakpoint_in_range_p (aspace, candidate.addr, len);
68 bool is_free = candidate.current_thread == nullptr;
69
70 if (!bp_in_range)
71 {
72 if (is_free)
73 {
74 buffer = &candidate;
75 break;
76 }
77 else
78 {
79 /* This buffer would be suitable, but it's used right now. */
80 fail_status = DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE;
81 }
82 }
83 else
84 {
85 /* There's a breakpoint set in the scratch pad location range
86 (which is usually around the entry point). We'd either
87 install it before resuming, which would overwrite/corrupt the
88 scratch pad, or if it was already inserted, this displaced
89 step would overwrite it. The latter is OK in the sense that
90 we already assume that no thread is going to execute the code
91 in the scratch pad range (after initial startup) anyway, but
92 the former is unacceptable. Simply punt and fallback to
93 stepping over this breakpoint in-line. */
94 displaced_debug_printf ("breakpoint set in displaced stepping "
95 "buffer at %s, can't use.",
96 paddress (arch, candidate.addr));
97 }
187b041e
SM
98 }
99
480af54c
SM
100 if (buffer == nullptr)
101 return fail_status;
102
103 displaced_debug_printf ("selected buffer at %s",
104 paddress (arch, buffer->addr));
105
106 /* Save the original PC of the thread. */
107 buffer->original_pc = regcache_read_pc (regcache);
108
109 /* Return displaced step buffer address to caller. */
110 displaced_pc = buffer->addr;
187b041e
SM
111
112 /* Save the original contents of the displaced stepping buffer. */
480af54c 113 buffer->saved_copy.resize (len);
187b041e 114
480af54c
SM
115 int status = target_read_memory (buffer->addr,
116 buffer->saved_copy.data (), len);
187b041e
SM
117 if (status != 0)
118 throw_error (MEMORY_ERROR,
119 _("Error accessing memory address %s (%s) for "
120 "displaced-stepping scratch space."),
480af54c 121 paddress (arch, buffer->addr), safe_strerror (status));
187b041e
SM
122
123 displaced_debug_printf ("saved %s: %s",
480af54c 124 paddress (arch, buffer->addr),
187b041e 125 displaced_step_dump_bytes
480af54c 126 (buffer->saved_copy.data (), len).c_str ());
187b041e
SM
127
128 /* Save this in a local variable first, so it's released if code below
129 throws. */
130 displaced_step_copy_insn_closure_up copy_insn_closure
480af54c
SM
131 = gdbarch_displaced_step_copy_insn (arch, buffer->original_pc,
132 buffer->addr, regcache);
187b041e
SM
133
134 if (copy_insn_closure == nullptr)
135 {
136 /* The architecture doesn't know how or want to displaced step
480af54c
SM
137 this instruction or instruction sequence. Fallback to
138 stepping over the breakpoint in-line. */
187b041e
SM
139 return DISPLACED_STEP_PREPARE_STATUS_CANT;
140 }
141
142 /* Resume execution at the copy. */
480af54c 143 regcache_write_pc (regcache, buffer->addr);
187b041e
SM
144
145 /* This marks the buffer as being in use. */
480af54c 146 buffer->current_thread = thread;
187b041e
SM
147
148 /* Save this, now that we know everything went fine. */
480af54c 149 buffer->copy_insn_closure = std::move (copy_insn_closure);
187b041e 150
480af54c
SM
151 /* Tell infrun not to try preparing a displaced step again for this inferior if
152 all buffers are taken. */
187b041e 153 thread->inf->displaced_step_state.unavailable = true;
480af54c
SM
154 for (const displaced_step_buffer &buf : m_buffers)
155 {
156 if (buf.current_thread == nullptr)
157 {
158 thread->inf->displaced_step_state.unavailable = false;
159 break;
160 }
161 }
187b041e
SM
162
163 return DISPLACED_STEP_PREPARE_STATUS_OK;
164}
165
166static void
167write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr,
168 const gdb_byte *myaddr, int len)
169{
170 scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
171
172 inferior_ptid = ptid;
173 write_memory (memaddr, myaddr, len);
174}
175
176static bool
177displaced_step_instruction_executed_successfully (gdbarch *arch,
178 gdb_signal signal)
179{
180 if (signal != GDB_SIGNAL_TRAP)
181 return false;
182
183 if (target_stopped_by_watchpoint ())
184 {
185 if (gdbarch_have_nonsteppable_watchpoint (arch)
186 || target_have_steppable_watchpoint ())
187 return false;
188 }
189
190 return true;
191}
192
193displaced_step_finish_status
480af54c
SM
194displaced_step_buffers::finish (gdbarch *arch, thread_info *thread,
195 gdb_signal sig)
187b041e
SM
196{
197 gdb_assert (thread->displaced_step_state.in_progress ());
480af54c
SM
198
199 /* Find the buffer this thread was using. */
200 displaced_step_buffer *buffer = nullptr;
201
202 for (displaced_step_buffer &candidate : m_buffers)
203 {
204 if (candidate.current_thread == thread)
205 {
206 buffer = &candidate;
207 break;
208 }
209 }
210
211 gdb_assert (buffer != nullptr);
187b041e
SM
212
213 /* Move this to a local variable so it's released in case something goes
214 wrong. */
215 displaced_step_copy_insn_closure_up copy_insn_closure
480af54c 216 = std::move (buffer->copy_insn_closure);
187b041e
SM
217 gdb_assert (copy_insn_closure != nullptr);
218
480af54c
SM
219 /* Reset BUFFER->CURRENT_THREAD immediately to mark the buffer as available,
220 in case something goes wrong below. */
221 buffer->current_thread = nullptr;
187b041e
SM
222
223 /* Now that a buffer gets freed, tell infrun it can ask us to prepare a displaced
224 step again for this inferior. Do that here in case something goes wrong
225 below. */
226 thread->inf->displaced_step_state.unavailable = false;
227
228 ULONGEST len = gdbarch_max_insn_length (arch);
229
480af54c
SM
230 /* Restore memory of the buffer. */
231 write_memory_ptid (thread->ptid, buffer->addr,
232 buffer->saved_copy.data (), len);
187b041e
SM
233
234 displaced_debug_printf ("restored %s %s",
235 target_pid_to_str (thread->ptid).c_str (),
480af54c 236 paddress (arch, buffer->addr));
187b041e
SM
237
238 regcache *rc = get_thread_regcache (thread);
239
240 bool instruction_executed_successfully
241 = displaced_step_instruction_executed_successfully (arch, sig);
242
243 if (instruction_executed_successfully)
244 {
480af54c
SM
245 gdbarch_displaced_step_fixup (arch, copy_insn_closure.get (),
246 buffer->original_pc,
247 buffer->addr, rc);
187b041e
SM
248 return DISPLACED_STEP_FINISH_STATUS_OK;
249 }
250 else
251 {
252 /* Since the instruction didn't complete, all we can do is relocate the
253 PC. */
254 CORE_ADDR pc = regcache_read_pc (rc);
480af54c 255 pc = buffer->original_pc + (pc - buffer->addr);
187b041e
SM
256 regcache_write_pc (rc, pc);
257 return DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED;
258 }
259}
260
261const displaced_step_copy_insn_closure *
480af54c 262displaced_step_buffers::copy_insn_closure_by_addr (CORE_ADDR addr)
187b041e 263{
480af54c
SM
264 for (const displaced_step_buffer &buffer : m_buffers)
265 {
266 if (addr == buffer.addr)
267 return buffer.copy_insn_closure.get ();
268 }
269
270 return nullptr;
187b041e
SM
271}
272
273void
480af54c 274displaced_step_buffers::restore_in_ptid (ptid_t ptid)
187b041e 275{
480af54c 276 for (const displaced_step_buffer &buffer : m_buffers)
187b041e 277 {
480af54c
SM
278 if (buffer.current_thread == nullptr)
279 continue;
280
281 regcache *regcache = get_thread_regcache (buffer.current_thread);
187b041e
SM
282 gdbarch *arch = regcache->arch ();
283 ULONGEST len = gdbarch_max_insn_length (arch);
284
480af54c 285 write_memory_ptid (ptid, buffer.addr, buffer.saved_copy.data (), len);
187b041e
SM
286
287 displaced_debug_printf ("restored in ptid %s %s",
288 target_pid_to_str (ptid).c_str (),
480af54c 289 paddress (arch, buffer.addr));
187b041e
SM
290 }
291}
292
c7acb87b
SM
293void _initialize_displaced_stepping ();
294void
295_initialize_displaced_stepping ()
296{
297 add_setshow_boolean_cmd ("displaced", class_maintenance,
298 &debug_displaced, _("\
299Set displaced stepping debugging."), _("\
300Show displaced stepping debugging."), _("\
301When non-zero, displaced stepping specific debugging is enabled."),
302 NULL,
303 show_debug_displaced,
304 &setdebuglist, &showdebuglist);
305}
This page took 0.102163 seconds and 4 git commands to generate.