xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/guile/scm-disasm.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /* Scheme interface to architecture.
2 
3    Copyright (C) 2014-2016 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 /* Struct used to pass "application data" in disassemble_info.  */
41 
42 struct gdbscm_disasm_data
43 {
44   struct gdbarch *gdbarch;
45   SCM port;
46   /* The offset of the address of the first instruction in PORT.  */
47   ULONGEST offset;
48 };
49 
50 /* Struct used to pass data from gdbscm_disasm_read_memory to
51    gdbscm_disasm_read_memory_worker.  */
52 
53 struct gdbscm_disasm_read_data
54 {
55   bfd_vma memaddr;
56   bfd_byte *myaddr;
57   unsigned int length;
58   struct disassemble_info *dinfo;
59 };
60 
61 /* Subroutine of gdbscm_arch_disassemble to simplify it.
62    Return the result for one instruction.  */
63 
64 static SCM
65 dascm_make_insn (CORE_ADDR pc, const char *assembly, int insn_len)
66 {
67   return scm_list_3 (scm_cons (address_symbol,
68 			       gdbscm_scm_from_ulongest (pc)),
69 		     scm_cons (asm_symbol,
70 			       gdbscm_scm_from_c_string (assembly)),
71 		     scm_cons (length_symbol,
72 			       scm_from_int (insn_len)));
73 }
74 
75 /* Helper function for gdbscm_disasm_read_memory to safely read from a
76    Scheme port.  Called via gdbscm_call_guile.
77    The result is a statically allocated error message or NULL if success.  */
78 
79 static const char *
80 gdbscm_disasm_read_memory_worker (void *datap)
81 {
82   struct gdbscm_disasm_read_data *data
83     = (struct gdbscm_disasm_read_data *) datap;
84   struct disassemble_info *dinfo = data->dinfo;
85   struct gdbscm_disasm_data *disasm_data
86     = (struct gdbscm_disasm_data *) dinfo->application_data;
87   SCM seekto, newpos, port = disasm_data->port;
88   size_t bytes_read;
89 
90   seekto = gdbscm_scm_from_ulongest (data->memaddr - disasm_data->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)
110 {
111   struct gdbscm_disasm_read_data data;
112   const char *status;
113 
114   data.memaddr = memaddr;
115   data.myaddr = myaddr;
116   data.length = length;
117   data.dinfo = dinfo;
118 
119   status = gdbscm_with_guile (gdbscm_disasm_read_memory_worker, &data);
120 
121   /* TODO: IWBN to distinguish problems reading target memory versus problems
122      with the port (e.g., EOF).  */
123   return status != NULL ? -1 : 0;
124 }
125 
126 /* disassemble_info.memory_error_func for gdbscm_print_insn_from_port.
127    Technically speaking, we don't need our own memory_error_func,
128    but to not provide one would leave a subtle dependency in the code.
129    This function exists to keep a clear boundary.  */
130 
131 static void
132 gdbscm_disasm_memory_error (int status, bfd_vma memaddr,
133 			    struct disassemble_info *info)
134 {
135   memory_error (TARGET_XFER_E_IO, memaddr);
136 }
137 
138 /* disassemble_info.print_address_func for gdbscm_print_insn_from_port.
139    Since we need to use our own application_data value, we need to supply
140    this routine as well.  */
141 
142 static void
143 gdbscm_disasm_print_address (bfd_vma addr, struct disassemble_info *info)
144 {
145   struct gdbscm_disasm_data *data
146     = (struct gdbscm_disasm_data *) info->application_data;
147   struct gdbarch *gdbarch = data->gdbarch;
148 
149   print_address (gdbarch, addr, (struct ui_file *) info->stream);
150 }
151 
152 /* Subroutine of gdbscm_arch_disassemble to simplify it.
153    Call gdbarch_print_insn using a port for input.
154    PORT must be seekable.
155    OFFSET is the offset in PORT from which addresses begin.
156    For example, when printing from a bytevector, addresses passed to the
157    bv seek routines must be in the range [0,size).  However, the bytevector
158    may represent an instruction at address 0x1234.  To handle this case pass
159    0x1234 for OFFSET.
160    This is based on gdb_print_insn, see it for details.  */
161 
162 static int
163 gdbscm_print_insn_from_port (struct gdbarch *gdbarch,
164 			     SCM port, ULONGEST offset, CORE_ADDR memaddr,
165 			     struct ui_file *stream, int *branch_delay_insns)
166 {
167   struct disassemble_info di;
168   int length;
169   struct gdbscm_disasm_data data;
170 
171   di = gdb_disassemble_info (gdbarch, stream);
172   data.gdbarch = gdbarch;
173   data.port = port;
174   data.offset = offset;
175   di.application_data = &data;
176   di.read_memory_func = gdbscm_disasm_read_memory;
177   di.memory_error_func = gdbscm_disasm_memory_error;
178   di.print_address_func = gdbscm_disasm_print_address;
179 
180   length = gdbarch_print_insn (gdbarch, memaddr, &di);
181 
182   if (branch_delay_insns)
183     {
184       if (di.insn_info_valid)
185 	*branch_delay_insns = di.branch_delay_insns;
186       else
187 	*branch_delay_insns = 0;
188     }
189 
190   return length;
191 }
192 
193 /* (arch-disassemble <gdb:arch> address
194      [#:port port] [#:offset address] [#:size integer] [#:count integer])
195      -> list
196 
197    Returns a list of disassembled instructions.
198    If PORT is provided, read bytes from it.  Otherwise read target memory.
199    If PORT is #f, read target memory.
200    PORT must be seekable.  IWBN to remove this restriction, and a future
201    release may.  For now the restriction is in place because it's not clear
202    all disassemblers are strictly sequential.
203    If SIZE is provided, limit the number of bytes read to this amount.
204    If COUNT is provided, limit the number of instructions to this amount.
205 
206    Each instruction in the result is an alist:
207    (('address . address) ('asm . disassembly) ('length . length)).
208    We could use a hash table (dictionary) but there aren't that many fields. */
209 
210 static SCM
211 gdbscm_arch_disassemble (SCM self, SCM start_scm, SCM rest)
212 {
213   arch_smob *a_smob
214     = arscm_get_arch_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
215   struct gdbarch *gdbarch = arscm_get_gdbarch (a_smob);
216   const SCM keywords[] = {
217     port_keyword, offset_keyword, size_keyword, count_keyword, SCM_BOOL_F
218   };
219   int port_arg_pos = -1, offset_arg_pos = -1;
220   int size_arg_pos = -1, count_arg_pos = -1;
221   SCM port = SCM_BOOL_F;
222   ULONGEST offset = 0;
223   unsigned int count = 1;
224   unsigned int size;
225   ULONGEST start_arg;
226   CORE_ADDR start, end;
227   CORE_ADDR pc;
228   unsigned int i;
229   int using_port;
230   SCM result;
231 
232   gdbscm_parse_function_args (FUNC_NAME, SCM_ARG2, keywords, "U#OUuu",
233 			      start_scm, &start_arg, rest,
234 			      &port_arg_pos, &port,
235 			      &offset_arg_pos, &offset,
236 			      &size_arg_pos, &size,
237 			      &count_arg_pos, &count);
238   /* START is first stored in a ULONGEST because we don't have a format char
239      for CORE_ADDR, and it's not really worth it to have one yet.  */
240   start = start_arg;
241 
242   if (port_arg_pos > 0)
243     {
244       SCM_ASSERT_TYPE (gdbscm_is_false (port)
245 		       || gdbscm_is_true (scm_input_port_p (port)),
246 		       port, port_arg_pos, FUNC_NAME, _("input port"));
247     }
248   using_port = gdbscm_is_true (port);
249 
250   if (offset_arg_pos > 0
251       && (port_arg_pos < 0
252 	  || gdbscm_is_false (port)))
253     {
254       gdbscm_out_of_range_error (FUNC_NAME, offset_arg_pos,
255 				 gdbscm_scm_from_ulongest (offset),
256 				 _("offset provided but port is missing"));
257     }
258 
259   if (size_arg_pos > 0)
260     {
261       if (size == 0)
262 	return SCM_EOL;
263       /* For now be strict about start+size overflowing.  If it becomes
264 	 a nuisance we can relax things later.  */
265       if (start + size < start)
266 	{
267 	  gdbscm_out_of_range_error (FUNC_NAME, 0,
268 				scm_list_2 (gdbscm_scm_from_ulongest (start),
269 					    gdbscm_scm_from_ulongest (size)),
270 				     _("start+size overflows"));
271 	}
272       end = start + size - 1;
273     }
274   else
275     end = ~(CORE_ADDR) 0;
276 
277   if (count == 0)
278     return SCM_EOL;
279 
280   result = SCM_EOL;
281 
282   for (pc = start, i = 0; pc <= end && i < count; )
283     {
284       int insn_len = 0;
285       char *as = NULL;
286       struct ui_file *memfile = mem_fileopen ();
287       struct cleanup *cleanups = make_cleanup_ui_file_delete (memfile);
288 
289       TRY
290 	{
291 	  if (using_port)
292 	    {
293 	      insn_len = gdbscm_print_insn_from_port (gdbarch, port, offset,
294 						      pc, memfile, NULL);
295 	    }
296 	  else
297 	    insn_len = gdb_print_insn (gdbarch, pc, memfile, NULL);
298 	}
299       CATCH (except, RETURN_MASK_ALL)
300 	{
301 	  GDBSCM_HANDLE_GDB_EXCEPTION_WITH_CLEANUPS (except, cleanups);
302 	}
303       END_CATCH
304 
305       as = ui_file_xstrdup (memfile, NULL);
306 
307       result = scm_cons (dascm_make_insn (pc, as, insn_len),
308 			 result);
309 
310       pc += insn_len;
311       i++;
312       do_cleanups (cleanups);
313       xfree (as);
314     }
315 
316   return scm_reverse_x (result, SCM_EOL);
317 }
318 
319 /* Initialize the Scheme architecture support.  */
320 
321 static const scheme_function disasm_functions[] =
322 {
323   { "arch-disassemble", 2, 0, 1, as_a_scm_t_subr (gdbscm_arch_disassemble),
324     "\
325 Return list of disassembled instructions in memory.\n\
326 \n\
327   Arguments: <gdb:arch> start-address\n\
328       [#:port port] [#:offset address]\n\
329       [#:size <integer>] [#:count <integer>]\n\
330     port: If non-#f, it is an input port to read bytes from.\n\
331     offset: Specifies the address offset of the first byte in the port.\n\
332       This is useful if the input is from something other than memory\n\
333       (e.g., a bytevector) and you want the result to be as if the bytes\n\
334       came from that address.  The value to pass for start-address is\n\
335       then also the desired disassembly address, not the offset in, e.g.,\n\
336       the bytevector.\n\
337     size: Limit the number of bytes read to this amount.\n\
338     count: Limit the number of instructions to this amount.\n\
339 \n\
340   Returns:\n\
341     Each instruction in the result is an alist:\n\
342       (('address . address) ('asm . disassembly) ('length . length))." },
343 
344   END_FUNCTIONS
345 };
346 
347 void
348 gdbscm_initialize_disasm (void)
349 {
350   gdbscm_define_functions (disasm_functions, 1);
351 
352   port_keyword = scm_from_latin1_keyword ("port");
353   offset_keyword = scm_from_latin1_keyword ("offset");
354   size_keyword = scm_from_latin1_keyword ("size");
355   count_keyword = scm_from_latin1_keyword ("count");
356 
357   address_symbol = scm_from_latin1_symbol ("address");
358   asm_symbol = scm_from_latin1_symbol ("asm");
359   length_symbol = scm_from_latin1_symbol ("length");
360 }
361