GDB (xrefs)
Loading...
Searching...
No Matches
displaced-stepping.c
Go to the documentation of this file.
1/* Displaced stepping related things.
2
3 Copyright (C) 2020-2023 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#include "defs.h"
21#include "displaced-stepping.h"
22
23#include "cli/cli-cmds.h"
24#include "command.h"
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"
31
32/* Default destructor for displaced_step_copy_insn_closure. */
33
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 gdb_printf (file, _("Displace stepping debugging is %s.\n"), value);
44}
45
47displaced_step_buffers::prepare (thread_info *thread, CORE_ADDR &displaced_pc)
48{
49 gdb_assert (!thread->displaced_step_state.in_progress ());
50
51 /* Sanity check: the thread should not be using a buffer at this point. */
53 gdb_assert (buf.current_thread != thread);
54
56 const address_space *aspace = regcache->aspace ();
57 gdbarch *arch = regcache->arch ();
58 ULONGEST len = gdbarch_displaced_step_buffer_length (arch);
59
60 /* Search for an unused buffer. */
61 displaced_step_buffer *buffer = nullptr;
64
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. */
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 }
98 }
99
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. */
108
109 /* Return displaced step buffer address to caller. */
110 displaced_pc = buffer->addr;
111
112 /* Save the original contents of the displaced stepping buffer. */
113 buffer->saved_copy.resize (len);
114
115 int status = target_read_memory (buffer->addr,
116 buffer->saved_copy.data (), len);
117 if (status != 0)
118 throw_error (MEMORY_ERROR,
119 _("Error accessing memory address %s (%s) for "
120 "displaced-stepping scratch space."),
121 paddress (arch, buffer->addr), safe_strerror (status));
122
123 displaced_debug_printf ("saved %s: %s",
124 paddress (arch, buffer->addr),
125 bytes_to_string (buffer->saved_copy).c_str ());
126
127 /* Save this in a local variable first, so it's released if code below
128 throws. */
131 buffer->addr, regcache);
132
133 if (copy_insn_closure == nullptr)
134 {
135 /* The architecture doesn't know how or want to displaced step
136 this instruction or instruction sequence. Fallback to
137 stepping over the breakpoint in-line. */
139 }
140
141 /* This marks the buffer as being in use. */
142 buffer->current_thread = thread;
143
144 /* Save this, now that we know everything went fine. */
145 buffer->copy_insn_closure = std::move (copy_insn_closure);
146
147 /* Reset the displaced step buffer state if we failed to write PC.
148 Otherwise we will prevent this buffer from being used, as it will
149 always have a thread in buffer->current_thread. */
150 auto reset_buffer = make_scope_exit
151 ([buffer] ()
152 {
153 buffer->current_thread = nullptr;
154 buffer->copy_insn_closure.reset ();
155 });
156
157 /* Adjust the PC so it points to the displaced step buffer address that will
158 be used. This needs to be done after we save the copy_insn_closure, as
159 some architectures (Arm, for one) need that information so they can adjust
160 other data as needed. In particular, Arm needs to know if the instruction
161 being executed in the displaced step buffer is thumb or not. Without that
162 information, things will be very wrong in a random way. */
164
165 /* PC update successful. Discard the displaced step state rollback. */
166 reset_buffer.release ();
167
168 /* Tell infrun not to try preparing a displaced step again for this inferior if
169 all buffers are taken. */
170 thread->inf->displaced_step_state.unavailable = true;
171 for (const displaced_step_buffer &buf : m_buffers)
172 {
173 if (buf.current_thread == nullptr)
174 {
175 thread->inf->displaced_step_state.unavailable = false;
176 break;
177 }
178 }
179
181}
182
183static void
184write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr,
185 const gdb_byte *myaddr, int len)
186{
187 scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
188
189 inferior_ptid = ptid;
190 write_memory (memaddr, myaddr, len);
191}
192
193static bool
195 (gdbarch *arch, const target_waitstatus &status)
196{
197 if (status.kind () == TARGET_WAITKIND_STOPPED
198 && status.sig () != GDB_SIGNAL_TRAP)
199 return false;
200
201 /* All other (thread event) waitkinds can only happen if the
202 instruction fully executed. For example, a fork, or a syscall
203 entry can only happen if the syscall instruction actually
204 executed. */
205
207 {
210 return false;
211 }
212
213 return true;
214}
215
219{
220 gdb_assert (thread->displaced_step_state.in_progress ());
221
222 /* Find the buffer this thread was using. */
223 displaced_step_buffer *buffer = nullptr;
224
225 for (displaced_step_buffer &candidate : m_buffers)
226 {
227 if (candidate.current_thread == thread)
228 {
229 buffer = &candidate;
230 break;
231 }
232 }
233
234 gdb_assert (buffer != nullptr);
235
236 /* Move this to a local variable so it's released in case something goes
237 wrong. */
239 = std::move (buffer->copy_insn_closure);
240 gdb_assert (copy_insn_closure != nullptr);
241
242 /* Reset BUFFER->CURRENT_THREAD immediately to mark the buffer as available,
243 in case something goes wrong below. */
244 buffer->current_thread = nullptr;
245
246 /* Now that a buffer gets freed, tell infrun it can ask us to prepare a displaced
247 step again for this inferior. Do that here in case something goes wrong
248 below. */
249 thread->inf->displaced_step_state.unavailable = false;
250
251 ULONGEST len = gdbarch_displaced_step_buffer_length (arch);
252
253 /* Restore memory of the buffer. */
254 write_memory_ptid (thread->ptid, buffer->addr,
255 buffer->saved_copy.data (), len);
256
257 displaced_debug_printf ("restored %s %s",
258 thread->ptid.to_string ().c_str (),
259 paddress (arch, buffer->addr));
260
261 regcache *rc = get_thread_regcache (thread);
262
263 bool instruction_executed_successfully
265
266 gdbarch_displaced_step_fixup (arch, copy_insn_closure.get (),
267 buffer->original_pc, buffer->addr,
268 rc, instruction_executed_successfully);
269
270 return (instruction_executed_successfully
273}
274
277{
278 for (const displaced_step_buffer &buffer : m_buffers)
279 {
280 /* Make sure we have active buffers to compare to. */
281 if (buffer.current_thread != nullptr && addr == buffer.addr)
282 {
283 /* The closure information should always be available. */
284 gdb_assert (buffer.copy_insn_closure.get () != nullptr);
285 return buffer.copy_insn_closure.get ();
286 }
287 }
288
289 return nullptr;
290}
291
292void
294{
295 for (const displaced_step_buffer &buffer : m_buffers)
296 {
297 if (buffer.current_thread == nullptr)
298 continue;
299
300 regcache *regcache = get_thread_regcache (buffer.current_thread);
301 gdbarch *arch = regcache->arch ();
302 ULONGEST len = gdbarch_displaced_step_buffer_length (arch);
303
304 write_memory_ptid (ptid, buffer.addr, buffer.saved_copy.data (), len);
305
306 displaced_debug_printf ("restored in ptid %s %s",
307 ptid.to_string ().c_str (),
308 paddress (arch, buffer.addr));
309 }
310}
311
313void
315{
317 &debug_displaced, _("\
318Set displaced stepping debugging."), _("\
319Show displaced stepping debugging."), _("\
320When non-zero, displaced stepping specific debugging is enabled."),
321 NULL,
324}
int breakpoint_in_range_p(const address_space *aspace, CORE_ADDR addr, ULONGEST len)
displaced_step_inferior_state displaced_step_state
Definition inferior.h:664
gdbarch * arch() const
Definition regcache.c:231
const address_space * aspace() const
Definition regcache.h:343
ptid_t ptid
Definition gdbthread.h:259
displaced_step_thread_state displaced_step_state
Definition gdbthread.h:552
struct inferior * inf
Definition gdbthread.h:301
struct cmd_list_element * showdebuglist
Definition cli-cmds.c:167
struct cmd_list_element * setdebuglist
Definition cli-cmds.c:165
set_show_commands add_setshow_boolean_cmd(const char *name, enum command_class theclass, bool *var, const char *set_doc, const char *show_doc, const char *help_doc, cmd_func_ftype *set_func, show_value_ftype *show_func, struct cmd_list_element **set_list, struct cmd_list_element **show_list)
Definition cli-decode.c:809
@ class_maintenance
Definition command.h:65
void write_memory(CORE_ADDR memaddr, const bfd_byte *myaddr, ssize_t len)
Definition corefile.c:347
static bool displaced_step_instruction_executed_successfully(gdbarch *arch, const target_waitstatus &status)
bool debug_displaced
static void write_memory_ptid(ptid_t ptid, CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
static void show_debug_displaced(struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value)
void _initialize_displaced_stepping()
#define displaced_debug_printf(fmt,...)
displaced_step_prepare_status
@ DISPLACED_STEP_PREPARE_STATUS_CANT
@ DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE
@ DISPLACED_STEP_PREPARE_STATUS_OK
std::unique_ptr< displaced_step_copy_insn_closure > displaced_step_copy_insn_closure_up
displaced_step_finish_status
@ DISPLACED_STEP_FINISH_STATUS_OK
@ DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED
int gdbarch_have_nonsteppable_watchpoint(struct gdbarch *gdbarch)
Definition gdbarch.c:3564
displaced_step_copy_insn_closure_up gdbarch_displaced_step_copy_insn(struct gdbarch *gdbarch, CORE_ADDR from, CORE_ADDR to, struct regcache *regs)
Definition gdbarch.c:4107
void gdbarch_displaced_step_fixup(struct gdbarch *gdbarch, struct displaced_step_copy_insn_closure *closure, CORE_ADDR from, CORE_ADDR to, struct regcache *regs, bool completed_p)
Definition gdbarch.c:4141
ULONGEST gdbarch_displaced_step_buffer_length(struct gdbarch *gdbarch)
Definition gdbarch.c:4240
mach_port_t mach_port_t name mach_port_t mach_port_t name kern_return_t int status
Definition gnu-nat.c:1790
ptid_t inferior_ptid
Definition infcmd.c:74
CORE_ADDR regcache_read_pc(struct regcache *regcache)
Definition regcache.c:1333
void regcache_write_pc(struct regcache *regcache, CORE_ADDR pc)
Definition regcache.c:1377
struct regcache * get_thread_regcache(process_stratum_target *target, ptid_t ptid)
Definition regcache.c:400
displaced_step_copy_insn_closure_up copy_insn_closure
std::vector< displaced_step_buffer > m_buffers
displaced_step_prepare_status prepare(thread_info *thread, CORE_ADDR &displaced_pc)
void restore_in_ptid(ptid_t ptid)
const displaced_step_copy_insn_closure * copy_insn_closure_by_addr(CORE_ADDR addr)
displaced_step_finish_status finish(gdbarch *arch, thread_info *thread, const target_waitstatus &status)
virtual ~displaced_step_copy_insn_closure()=0
Definition value.h:130
bool target_have_steppable_watchpoint()
Definition target.c:508
int target_read_memory(CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len)
Definition target.c:1785
bool target_stopped_by_watchpoint()
Definition target.c:470
const char * paddress(struct gdbarch *gdbarch, CORE_ADDR addr)
Definition utils.c:3166
void gdb_printf(struct ui_file *stream, const char *format,...)
Definition utils.c:1886
@ TARGET_WAITKIND_STOPPED
Definition waitstatus.h:36