1 /* Displaced stepping related things.
2 
3    Copyright (C) 2020-2021 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 
23 #include "gdbsupport/array-view.h"
24 #include "gdbsupport/byte-vector.h"
25 
26 struct gdbarch;
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   debug_prefixed_printf_cond (debug_displaced, "displaced",fmt, ##__VA_ARGS__)
37 
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 
62 /* Data returned by a gdbarch displaced_step_copy_insn method, to be passed to
63    the matching displaced_step_fixup method.  */
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 {
buf_displaced_step_copy_insn_closurebuf_displaced_step_copy_insn_closure77   buf_displaced_step_copy_insn_closure (int buf_size)
78   : buf (buf_size)
79   {}
80 
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.  */
84   gdb::byte_vector buf;
85 };
86 
87 /* Per-inferior displaced stepping state.  */
88 
89 struct displaced_step_inferior_state
90 {
displaced_step_inferior_statedisplaced_step_inferior_state91   displaced_step_inferior_state ()
92   {
93     reset ();
94   }
95 
96   /* Put this object back in its original state.  */
resetdisplaced_step_inferior_state97   void reset ()
98   {
99     failed_before = false;
100     in_progress_count = 0;
101     unavailable = false;
102   }
103 
104   /* True if preparing a displaced step ever failed.  If so, we won't
105      try displaced stepping for this inferior again.  */
106   bool failed_before;
107 
108   /* Number of displaced steps in progress for this inferior.  */
109   unsigned int in_progress_count;
110 
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 };
117 
118 /* Per-thread displaced stepping state.  */
119 
120 struct displaced_step_thread_state
121 {
122   /* Return true if this thread is currently executing a displaced step.  */
in_progressdisplaced_step_thread_state123   bool in_progress () const
124   {
125     return m_original_gdbarch != nullptr;
126   }
127 
128   /* Return the gdbarch of the thread prior to the step.  */
get_original_gdbarchdisplaced_step_thread_state129   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).  */
setdisplaced_step_thread_state138   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.  */
resetdisplaced_step_thread_state144   void reset ()
145   {
146     m_original_gdbarch = nullptr;
147   }
148 
149 private:
150   gdbarch *m_original_gdbarch = nullptr;
151 };
152 
153 /* Control access to multiple displaced stepping buffers at fixed addresses.  */
154 
155 struct displaced_step_buffers
156 {
displaced_step_buffersdisplaced_step_buffers157   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   }
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:
179 
180   /* State of a single buffer.  */
181 
182   struct displaced_step_buffer
183   {
displaced_step_bufferdisplaced_step_buffers::displaced_step_buffer184     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;
197 
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;
201 
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   };
206 
207   std::vector<displaced_step_buffer> m_buffers;
208 };
209 
210 #endif /* DISPLACED_STEPPING_H */
211