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