xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/guile/scm-disasm.c (revision 8b657b0747480f8989760d71343d6dd33f8d4cf9)
1 /* Scheme interface to architecture.
2 
3    Copyright (C) 2014-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 /* See README file in this directory for implementation notes, coding
21    conventions, et.al.  */
22 
23 #include "defs.h"
24 #include "arch-utils.h"
25 #include "disasm.h"
26 #include "dis-asm.h"
27 #include "gdbarch.h"
28 #include "gdbcore.h" /* Why is memory_error here? */
29 #include "guile-internal.h"
30 
31 static SCM port_keyword;
32 static SCM offset_keyword;
33 static SCM size_keyword;
34 static SCM count_keyword;
35 
36 static SCM address_symbol;
37 static SCM asm_symbol;
38 static SCM length_symbol;
39 
40 class gdbscm_disassembler : public gdb_disassembler
41 {
42 public:
43   gdbscm_disassembler (struct gdbarch *gdbarch,
44 		       struct ui_file *stream,
45 		       SCM port, ULONGEST offset);
46 
47   SCM port;
48   /* The offset of the address of the first instruction in PORT.  */
49   ULONGEST offset;
50 };
51 
52 /* Struct used to pass data from gdbscm_disasm_read_memory to
53    gdbscm_disasm_read_memory_worker.  */
54 
55 struct gdbscm_disasm_read_data
56 {
57   bfd_vma memaddr;
58   bfd_byte *myaddr;
59   unsigned int length;
60   gdbscm_disassembler *dinfo;
61 };
62 
63 /* Subroutine of gdbscm_arch_disassemble to simplify it.
64    Return the result for one instruction.  */
65 
66 static SCM
67 dascm_make_insn (CORE_ADDR pc, const char *assembly, int insn_len)
68 {
69   return scm_list_3 (scm_cons (address_symbol,
70 			       gdbscm_scm_from_ulongest (pc)),
71 		     scm_cons (asm_symbol,
72 			       gdbscm_scm_from_c_string (assembly)),
73 		     scm_cons (length_symbol,
74 			       scm_from_int (insn_len)));
75 }
76 
77 /* Helper function for gdbscm_disasm_read_memory to safely read from a
78    Scheme port.  Called via gdbscm_call_guile.
79    The result is a statically allocated error message or NULL if success.  */
80 
81 static const char *
82 gdbscm_disasm_read_memory_worker (void *datap)
83 {
84   struct gdbscm_disasm_read_data *data
85     = (struct gdbscm_disasm_read_data *) datap;
86   gdbscm_disassembler *dinfo = data->dinfo;
87   SCM seekto, newpos, port = dinfo->port;
88   size_t bytes_read;
89 
90   seekto = gdbscm_scm_from_ulongest (data->memaddr - dinfo->offset);
91   newpos = scm_seek (port, seekto, scm_from_int (SEEK_SET));
92   if (!scm_is_eq (seekto, newpos))
93     return "seek error";
94 
95   bytes_read = scm_c_read (port, data->myaddr, data->length);
96 
97   if (bytes_read != data->length)
98     return "short read";
99 
100   /* If we get here the read succeeded.  */
101   return NULL;
102 }
103 
104 /* disassemble_info.read_memory_func for gdbscm_print_insn_from_port.  */
105 
106 static int
107 gdbscm_disasm_read_memory (bfd_vma memaddr, bfd_byte *myaddr,
108 			   unsigned int length,
109 			   struct disassemble_info *dinfo) noexcept
110 {
111   gdbscm_disassembler *self
112     = static_cast<gdbscm_disassembler *> (dinfo->application_data);
113   struct gdbscm_disasm_read_data data;
114   const char *status;
115 
116   data.memaddr = memaddr;
117   data.myaddr = myaddr;
118   data.length = length;
119   data.dinfo = self;
120 
121   status = gdbscm_with_guile (gdbscm_disasm_read_memory_worker, &data);
122 
123   /* TODO: IWBN to distinguish problems reading target memory versus problems
124      with the port (e.g., EOF).  */
125   return status != NULL ? -1 : 0;
126 }
127 
128 gdbscm_disassembler::gdbscm_disassembler (struct gdbarch *gdbarch,
129 					  struct ui_file *stream,
130 					  SCM port_, ULONGEST offset_)
131   : gdb_disassembler (gdbarch, stream, gdbscm_disasm_read_memory),
132     port (port_), offset (offset_)
133 {
134 }
135 
136 /* Subroutine of gdbscm_arch_disassemble to simplify it.
137    Call gdbarch_print_insn using a port for input.
138    PORT must be seekable.
139    OFFSET is the offset in PORT from which addresses begin.
140    For example, when printing from a bytevector, addresses passed to the
141    bv seek routines must be in the range [0,size).  However, the bytevector
142    may represent an instruction at address 0x1234.  To handle this case pass
143    0x1234 for OFFSET.
144    This is based on gdb_print_insn, see it for details.  */
145 
146 static int
147 gdbscm_print_insn_from_port (struct gdbarch *gdbarch,
148 			     SCM port, ULONGEST offset, CORE_ADDR memaddr,
149 			     string_file *stream, int *branch_delay_insns)
150 {
151   gdbscm_disassembler di (gdbarch, stream, port, offset);
152 
153   return di.print_insn (memaddr, branch_delay_insns);
154 }
155 
156 /* (arch-disassemble <gdb:arch> address
157      [#:port port] [#:offset address] [#:size integer] [#:count integer])
158      -> list
159 
160    Returns a list of disassembled instructions.
161    If PORT is provided, read bytes from it.  Otherwise read target memory.
162    If PORT is #f, read target memory.
163    PORT must be seekable.  IWBN to remove this restriction, and a future
164    release may.  For now the restriction is in place because it's not clear
165    all disassemblers are strictly sequential.
166    If SIZE is provided, limit the number of bytes read to this amount.
167    If COUNT is provided, limit the number of instructions to this amount.
168 
169    Each instruction in the result is an alist:
170    (('address . address) ('asm . disassembly) ('length . length)).
171    We could use a hash table (dictionary) but there aren't that many fields. */
172 
173 static SCM
174 gdbscm_arch_disassemble (SCM self, SCM start_scm, SCM rest)
175 {
176   arch_smob *a_smob
177     = arscm_get_arch_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
178   struct gdbarch *gdbarch = arscm_get_gdbarch (a_smob);
179   const SCM keywords[] = {
180     port_keyword, offset_keyword, size_keyword, count_keyword, SCM_BOOL_F
181   };
182   int port_arg_pos = -1, offset_arg_pos = -1;
183   int size_arg_pos = -1, count_arg_pos = -1;
184   SCM port = SCM_BOOL_F;
185   ULONGEST offset = 0;
186   unsigned int count = 1;
187   unsigned int size;
188   ULONGEST start_arg;
189   CORE_ADDR start, end;
190   CORE_ADDR pc;
191   unsigned int i;
192   int using_port;
193   SCM result;
194 
195   gdbscm_parse_function_args (FUNC_NAME, SCM_ARG2, keywords, "U#OUuu",
196 			      start_scm, &start_arg, rest,
197 			      &port_arg_pos, &port,
198 			      &offset_arg_pos, &offset,
199 			      &size_arg_pos, &size,
200 			      &count_arg_pos, &count);
201   /* START is first stored in a ULONGEST because we don't have a format char
202      for CORE_ADDR, and it's not really worth it to have one yet.  */
203   start = start_arg;
204 
205   if (port_arg_pos > 0)
206     {
207       SCM_ASSERT_TYPE (gdbscm_is_false (port)
208 		       || gdbscm_is_true (scm_input_port_p (port)),
209 		       port, port_arg_pos, FUNC_NAME, _("input port"));
210     }
211   using_port = gdbscm_is_true (port);
212 
213   if (offset_arg_pos > 0
214       && (port_arg_pos < 0
215 	  || gdbscm_is_false (port)))
216     {
217       gdbscm_out_of_range_error (FUNC_NAME, offset_arg_pos,
218 				 gdbscm_scm_from_ulongest (offset),
219 				 _("offset provided but port is missing"));
220     }
221 
222   if (size_arg_pos > 0)
223     {
224       if (size == 0)
225 	return SCM_EOL;
226       /* For now be strict about start+size overflowing.  If it becomes
227 	 a nuisance we can relax things later.  */
228       if (start + size < start)
229 	{
230 	  gdbscm_out_of_range_error (FUNC_NAME, 0,
231 				scm_list_2 (gdbscm_scm_from_ulongest (start),
232 					    gdbscm_scm_from_ulongest (size)),
233 				     _("start+size overflows"));
234 	}
235       end = start + size - 1;
236     }
237   else
238     end = ~(CORE_ADDR) 0;
239 
240   if (count == 0)
241     return SCM_EOL;
242 
243   result = SCM_EOL;
244 
245   for (pc = start, i = 0; pc <= end && i < count; )
246     {
247       int insn_len = 0;
248       string_file buf;
249 
250       gdbscm_gdb_exception exc {};
251       try
252 	{
253 	  if (using_port)
254 	    {
255 	      insn_len = gdbscm_print_insn_from_port (gdbarch, port, offset,
256 						      pc, &buf, NULL);
257 	    }
258 	  else
259 	    insn_len = gdb_print_insn (gdbarch, pc, &buf, NULL);
260 	}
261       catch (const gdb_exception &except)
262 	{
263 	  exc = unpack (except);
264 	}
265 
266       GDBSCM_HANDLE_GDB_EXCEPTION (exc);
267       result = scm_cons (dascm_make_insn (pc, buf.c_str (), insn_len),
268 			 result);
269 
270       pc += insn_len;
271       i++;
272     }
273 
274   return scm_reverse_x (result, SCM_EOL);
275 }
276 
277 /* Initialize the Scheme architecture support.  */
278 
279 static const scheme_function disasm_functions[] =
280 {
281   { "arch-disassemble", 2, 0, 1, as_a_scm_t_subr (gdbscm_arch_disassemble),
282     "\
283 Return list of disassembled instructions in memory.\n\
284 \n\
285   Arguments: <gdb:arch> start-address\n\
286       [#:port port] [#:offset address]\n\
287       [#:size <integer>] [#:count <integer>]\n\
288     port: If non-#f, it is an input port to read bytes from.\n\
289     offset: Specifies the address offset of the first byte in the port.\n\
290       This is useful if the input is from something other than memory\n\
291       (e.g., a bytevector) and you want the result to be as if the bytes\n\
292       came from that address.  The value to pass for start-address is\n\
293       then also the desired disassembly address, not the offset in, e.g.,\n\
294       the bytevector.\n\
295     size: Limit the number of bytes read to this amount.\n\
296     count: Limit the number of instructions to this amount.\n\
297 \n\
298   Returns:\n\
299     Each instruction in the result is an alist:\n\
300       (('address . address) ('asm . disassembly) ('length . length))." },
301 
302   END_FUNCTIONS
303 };
304 
305 void
306 gdbscm_initialize_disasm (void)
307 {
308   gdbscm_define_functions (disasm_functions, 1);
309 
310   port_keyword = scm_from_latin1_keyword ("port");
311   offset_keyword = scm_from_latin1_keyword ("offset");
312   size_keyword = scm_from_latin1_keyword ("size");
313   count_keyword = scm_from_latin1_keyword ("count");
314 
315   address_symbol = scm_from_latin1_symbol ("address");
316   asm_symbol = scm_from_latin1_symbol ("asm");
317   length_symbol = scm_from_latin1_symbol ("length");
318 }
319