gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / gdb / displaced-stepping.c
CommitLineData
e088209c
SM
1#include "defs.h"
2
3#include "displaced-stepping.h"
4
5#include "gdbarch.h"
6#include "gdbthread.h"
7#include "target.h"
8#include "inferior.h"
9#include "gdbcore.h"
10
11displaced_step_copy_insn_closure::~displaced_step_copy_insn_closure() = default;
12
13displaced_step_prepare_status
9635ae5d 14multiple_displaced_buffer_manager::prepare (thread_info *thread)
e088209c 15{
5514706f
SM
16 gdb_assert (thread != nullptr);
17
18 gdbarch *arch = thread->arch ();
19
20 /* This class requires that the arch implements both copy_insn and fixup. */
21 gdb_assert (gdbarch_displaced_step_copy_insn_p (arch));
22 gdb_assert (gdbarch_displaced_step_fixup_p (arch));
23
24 /* It's invalid to `prepare` a thread that already has a displaced step in
25 progress. */
9635ae5d 26 gdb_assert (!thread->displaced_step_state.in_progress ());
9635ae5d 27
5514706f 28 /* Sanity check: no buffer is currently assigned to this thread. */
9635ae5d
SM
29 for (displaced_step_buffer_state &buf : m_buffers)
30 gdb_assert (buf.m_current_thread != thread);
31
32 /* Search for an unused buffer. */
5514706f
SM
33 displaced_step_buffer_state *buffer = nullptr;
34
9635ae5d 35 for (displaced_step_buffer_state &candidate : m_buffers)
e088209c 36 {
9635ae5d
SM
37 if (candidate.m_current_thread == nullptr)
38 {
39 buffer = &candidate;
40 break;
41 }
e088209c
SM
42 }
43
9635ae5d
SM
44 if (buffer == nullptr)
45 return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE;
46
9635ae5d
SM
47
48 if (debug_displaced)
49 fprintf_unfiltered (gdb_stdlog, "displaced: selected buffer at %s\n",
50 paddress (arch, buffer->m_buffer_addr));
51
e088209c
SM
52 struct regcache *regcache = thread->regcache ();
53 ULONGEST len = gdbarch_max_insn_length (arch);
9635ae5d 54 buffer->m_original_pc = regcache_read_pc (regcache);
e088209c
SM
55
56 /* Save the original contents of the displaced stepping buffer. */
9635ae5d 57 buffer->m_saved_copy.resize (len);
e088209c 58
9635ae5d 59 int status = target_read_memory (buffer->m_buffer_addr, buffer->m_saved_copy.data (), len);
e088209c
SM
60 if (status != 0)
61 throw_error (MEMORY_ERROR,
62 _("Error accessing memory address %s (%s) for "
63 "displaced-stepping scratch space."),
9635ae5d 64 paddress (arch, buffer->m_buffer_addr), safe_strerror (status));
e088209c
SM
65
66 if (debug_displaced)
67 {
68 fprintf_unfiltered (gdb_stdlog, "displaced: saved %s: ",
9635ae5d
SM
69 paddress (arch, buffer->m_buffer_addr));
70 displaced_step_dump_bytes (gdb_stdlog, buffer->m_saved_copy.data (), len);
e088209c
SM
71 };
72
9635ae5d
SM
73 buffer->m_copy_insn_closure
74 = gdbarch_displaced_step_copy_insn (arch, buffer->m_original_pc,
75 buffer->m_buffer_addr, regcache);
76 if (buffer->m_copy_insn_closure == nullptr)
e088209c
SM
77 {
78 /* The architecture doesn't know how or want to displaced step
79 this instruction or instruction sequence. Fallback to
80 stepping over the breakpoint in-line. */
81 return DISPLACED_STEP_PREPARE_STATUS_ERROR;
82 }
83
84 try
85 {
86 /* Resume execution at the copy. */
9635ae5d 87 regcache_write_pc (regcache, buffer->m_buffer_addr);
e088209c
SM
88 }
89 catch (...)
90 {
91 /* Failed to write the PC. Release the architecture's displaced
92 stepping resources and the thread's displaced stepping state. */
9635ae5d 93 buffer->m_copy_insn_closure.reset ();
e088209c
SM
94
95 return DISPLACED_STEP_PREPARE_STATUS_ERROR;
96 }
97
98 /* This marks the buffer as being in use. */
9635ae5d 99 buffer->m_current_thread = thread;
e088209c
SM
100
101 return DISPLACED_STEP_PREPARE_STATUS_OK;
102}
103
104static void
105write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr,
106 const gdb_byte *myaddr, int len)
107{
108 scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
109
110 inferior_ptid = ptid;
111 write_memory (memaddr, myaddr, len);
112}
113
114static bool
115displaced_step_instruction_executed_successfully (gdbarch *arch, gdb_signal signal)
116{
117 if (signal != GDB_SIGNAL_TRAP)
118 return false;
119
120 if (target_stopped_by_watchpoint ())
121 {
122 // FIXME: Not sure about this condition.
123 if (gdbarch_have_nonsteppable_watchpoint (arch)
124 || target_have_steppable_watchpoint)
125 return false;
126 }
127
128 return true;
129}
130
131displaced_step_finish_status
9635ae5d
SM
132multiple_displaced_buffer_manager::finish (gdbarch *arch, thread_info *thread,
133 gdb_signal sig)
e088209c
SM
134{
135 displaced_step_finish_status status;
9635ae5d 136 displaced_step_buffer_state *buffer = nullptr;
e088209c 137
9635ae5d
SM
138 gdb_assert (thread->displaced_step_state.in_progress ());
139
140 /* Find the buffer this thread was using. */
141 for (displaced_step_buffer_state &candidate : m_buffers)
142 {
143 if (thread == candidate.m_current_thread)
144 {
145 buffer = &candidate;
146 break;
147 }
148 }
149
150 gdb_assert (buffer != nullptr);
e088209c
SM
151
152 ULONGEST len = gdbarch_max_insn_length (arch);
153
9635ae5d
SM
154 /* Restore memory of the buffer. */
155 write_memory_ptid (thread->ptid, buffer->m_buffer_addr,
156 buffer->m_saved_copy.data (), len);
e088209c
SM
157 if (debug_displaced)
158 fprintf_unfiltered (gdb_stdlog, "displaced: restored %s %s\n",
159 target_pid_to_str (thread->ptid).c_str (),
9635ae5d 160 paddress (arch, buffer->m_buffer_addr));
e088209c
SM
161
162 regcache *rc = get_thread_regcache (thread);
163
164 bool instruction_executed_successfully
165 = displaced_step_instruction_executed_successfully (arch, sig);
166
e088209c
SM
167 if (instruction_executed_successfully)
168 {
9635ae5d
SM
169 gdbarch_displaced_step_fixup (arch, buffer->m_copy_insn_closure.get (),
170 buffer->m_original_pc,
171 buffer->m_buffer_addr, rc);
e088209c
SM
172 status = DISPLACED_STEP_FINISH_STATUS_OK;
173 }
174 else
175 {
176 /* Since the instruction didn't complete, all we can do is relocate the
177 PC. */
178 CORE_ADDR pc = regcache_read_pc (rc);
9635ae5d 179 pc = buffer->m_original_pc + (pc - buffer->m_buffer_addr);
e088209c
SM
180 regcache_write_pc (rc, pc);
181 status = DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED;
182 }
183
9635ae5d
SM
184 buffer->m_copy_insn_closure.reset ();
185 buffer->m_current_thread = nullptr;
e088209c
SM
186
187 return status;
188}
348a832d 189
65a7e4d6
SM
190bool
191default_supports_displaced_step (target_ops *target, thread_info *thread)
192{
193 /* Only check for the presence of `prepare`. `finish` is required by the
194 gdbarch verification to be provided if `prepare` is. */
195 gdbarch *arch = thread->arch ();
196 return gdbarch_displaced_step_prepare_p (arch);
197}
198
348a832d 199displaced_step_prepare_status
65a7e4d6 200default_displaced_step_prepare (target_ops *target, thread_info *thread)
348a832d
SM
201{
202 gdbarch *arch = thread->arch ();
203 return gdbarch_displaced_step_prepare (arch, thread);
204}
205
206displaced_step_finish_status
207default_displaced_step_finish (target_ops *target,
208 thread_info *thread,
209 gdb_signal sig)
210{
211 gdbarch *arch = thread->arch ();
212 return gdbarch_displaced_step_finish (arch, thread, sig);
213}
This page took 0.031377 seconds and 4 git commands to generate.