221e48e661ab58682b74090a5f35958a29fae649
[external/binutils.git] / gdb / tui / tui-disasm.c
1 /* Disassembly display.
2
3    Copyright (C) 1998-2019 Free Software Foundation, Inc.
4
5    Contributed by Hewlett-Packard Company.
6
7    This file is part of GDB.
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
21
22 #include "defs.h"
23 #include "arch-utils.h"
24 #include "symtab.h"
25 #include "breakpoint.h"
26 #include "frame.h"
27 #include "value.h"
28 #include "source.h"
29 #include "disasm.h"
30 #include "tui/tui.h"
31 #include "tui/tui-data.h"
32 #include "tui/tui-win.h"
33 #include "tui/tui-layout.h"
34 #include "tui/tui-winsource.h"
35 #include "tui/tui-stack.h"
36 #include "tui/tui-file.h"
37 #include "tui/tui-disasm.h"
38 #include "tui/tui-source.h"
39 #include "progspace.h"
40 #include "objfiles.h"
41
42 #include "gdb_curses.h"
43
44 struct tui_asm_line 
45 {
46   CORE_ADDR addr;
47   char *addr_string;
48   char *insn;
49 };
50
51 /* Function to set the disassembly window's content.
52    Disassemble count lines starting at pc.
53    Return address of the count'th instruction after pc.  */
54 static CORE_ADDR
55 tui_disassemble (struct gdbarch *gdbarch, struct tui_asm_line *asm_lines,
56                  CORE_ADDR pc, int count)
57 {
58   string_file gdb_dis_out;
59
60   /* Now construct each line.  */
61   for (; count > 0; count--, asm_lines++)
62     {
63       xfree (asm_lines->addr_string);
64       xfree (asm_lines->insn);
65       
66       print_address (gdbarch, pc, &gdb_dis_out);
67       asm_lines->addr = pc;
68       asm_lines->addr_string = xstrdup (gdb_dis_out.c_str ());
69
70       gdb_dis_out.clear ();
71
72       pc = pc + gdb_print_insn (gdbarch, pc, &gdb_dis_out, NULL);
73
74       asm_lines->insn = xstrdup (gdb_dis_out.c_str ());
75
76       /* Reset the buffer to empty.  */
77       gdb_dis_out.clear ();
78     }
79   return pc;
80 }
81
82 /* Find the disassembly address that corresponds to FROM lines above
83    or below the PC.  Variable sized instructions are taken into
84    account by the algorithm.  */
85 static CORE_ADDR
86 tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from)
87 {
88   CORE_ADDR new_low;
89   int max_lines;
90   int i;
91   struct tui_asm_line *asm_lines;
92
93   max_lines = (from > 0) ? from : - from;
94   if (max_lines <= 1)
95      return pc;
96
97   asm_lines = XALLOCAVEC (struct tui_asm_line, max_lines);
98   memset (asm_lines, 0, sizeof (struct tui_asm_line) * max_lines);
99
100   new_low = pc;
101   if (from > 0)
102     {
103       tui_disassemble (gdbarch, asm_lines, pc, max_lines);
104       new_low = asm_lines[max_lines - 1].addr;
105     }
106   else
107     {
108       CORE_ADDR last_addr;
109       int pos;
110       struct bound_minimal_symbol msymbol;
111               
112       /* Find backward an address which is a symbol and for which
113          disassembling from that address will fill completely the
114          window.  */
115       pos = max_lines - 1;
116       do {
117          new_low -= 1 * max_lines;
118          msymbol = lookup_minimal_symbol_by_pc_section (new_low, 0);
119
120          if (msymbol.minsym)
121             new_low = BMSYMBOL_VALUE_ADDRESS (msymbol);
122          else
123             new_low += 1 * max_lines;
124
125          tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
126          last_addr = asm_lines[pos].addr;
127       } while (last_addr > pc && msymbol.minsym);
128
129       /* Scan forward disassembling one instruction at a time until
130          the last visible instruction of the window matches the pc.
131          We keep the disassembled instructions in the 'lines' window
132          and shift it downward (increasing its addresses).  */
133       if (last_addr < pc)
134         do
135           {
136             CORE_ADDR next_addr;
137                  
138             pos++;
139             if (pos >= max_lines)
140               pos = 0;
141
142             next_addr = tui_disassemble (gdbarch, &asm_lines[pos],
143                                          last_addr, 1);
144
145             /* If there are some problems while disassembling exit.  */
146             if (next_addr <= last_addr)
147               break;
148             last_addr = next_addr;
149           } while (last_addr <= pc);
150       pos++;
151       if (pos >= max_lines)
152          pos = 0;
153       new_low = asm_lines[pos].addr;
154     }
155   for (i = 0; i < max_lines; i++)
156     {
157       xfree (asm_lines[i].addr_string);
158       xfree (asm_lines[i].insn);
159     }
160   return new_low;
161 }
162
163 /* Function to set the disassembly window's content.  */
164 enum tui_status
165 tui_set_disassem_content (tui_source_window_base *win_info,
166                           struct gdbarch *gdbarch, CORE_ADDR pc)
167 {
168   int i;
169   int offset = win_info->horizontal_offset;
170   int max_lines, line_width;
171   CORE_ADDR cur_pc;
172   struct tui_locator_window *locator = tui_locator_win_info_ptr ();
173   int tab_len = tui_tab_width;
174   struct tui_asm_line *asm_lines;
175   int insn_pos;
176   int addr_size, insn_size;
177   char *line;
178   
179   if (pc == 0)
180     return TUI_FAILURE;
181
182   win_info->gdbarch = gdbarch;
183   win_info->start_line_or_addr.loa = LOA_ADDRESS;
184   win_info->start_line_or_addr.u.addr = pc;
185   cur_pc = locator->addr;
186
187   /* Window size, excluding highlight box.  */
188   max_lines = win_info->height - 2;
189   line_width = win_info->width - 2;
190
191   /* Get temporary table that will hold all strings (addr & insn).  */
192   asm_lines = XALLOCAVEC (struct tui_asm_line, max_lines);
193   memset (asm_lines, 0, sizeof (struct tui_asm_line) * max_lines);
194
195   tui_disassemble (gdbarch, asm_lines, pc, max_lines);
196
197   /* Determine maximum address- and instruction lengths.  */
198   addr_size = 0;
199   insn_size = 0;
200   for (i = 0; i < max_lines; i++)
201     {
202       size_t len = strlen (asm_lines[i].addr_string);
203
204       if (len > addr_size)
205         addr_size = len;
206
207       len = strlen (asm_lines[i].insn);
208       if (len > insn_size)
209         insn_size = len;
210     }
211
212   /* Align instructions to the same column.  */
213   insn_pos = (1 + (addr_size / tab_len)) * tab_len;
214
215   /* Allocate memory to create each line.  */
216   line = (char*) alloca (insn_pos + insn_size + 1);
217
218   /* Now construct each line.  */
219   win_info->content.resize (max_lines);
220   for (i = 0; i < max_lines; i++)
221     {
222       int cur_len;
223
224       tui_source_element *src = &win_info->content[i];
225       strcpy (line, asm_lines[i].addr_string);
226       cur_len = strlen (line);
227       memset (line + cur_len, ' ', insn_pos - cur_len);
228       strcpy (line + insn_pos, asm_lines[i].insn);
229
230       /* Now copy the line taking the offset into account.  */
231       xfree (src->line);
232       if (strlen (line) > offset)
233         src->line = xstrndup (&line[offset], line_width);
234       else
235         src->line = xstrdup ("");
236
237       src->line_or_addr.loa = LOA_ADDRESS;
238       src->line_or_addr.u.addr = asm_lines[i].addr;
239       src->is_exec_point = asm_lines[i].addr == cur_pc;
240
241       xfree (asm_lines[i].addr_string);
242       xfree (asm_lines[i].insn);
243     }
244   return TUI_SUCCESS;
245 }
246
247
248 /* Function to display the disassembly window with disassembled code.  */
249 void
250 tui_show_disassem (struct gdbarch *gdbarch, CORE_ADDR start_addr)
251 {
252   struct symtab *s = find_pc_line_symtab (start_addr);
253   struct tui_win_info *win_with_focus = tui_win_with_focus ();
254   struct tui_line_or_address val;
255
256   gdb_assert (TUI_DISASM_WIN != nullptr && TUI_DISASM_WIN->is_visible ());
257
258   val.loa = LOA_ADDRESS;
259   val.u.addr = start_addr;
260   TUI_DISASM_WIN->update_source_window (gdbarch, s, val);
261
262   /* If the focus was in the src win, put it in the asm win, if the
263      source view isn't split.  */
264   if (tui_current_layout () != SRC_DISASSEM_COMMAND 
265       && win_with_focus == TUI_SRC_WIN)
266     tui_set_win_focus_to (TUI_DISASM_WIN);
267 }
268
269
270 /* Function to display the disassembly window.  */
271 void
272 tui_show_disassem_and_update_source (struct gdbarch *gdbarch,
273                                      CORE_ADDR start_addr)
274 {
275   struct symtab_and_line sal;
276
277   tui_show_disassem (gdbarch, start_addr);
278   if (tui_current_layout () == SRC_DISASSEM_COMMAND)
279     {
280       struct tui_line_or_address val;
281
282       /* Update what is in the source window if it is displayed too,
283          note that it follows what is in the disassembly window and
284          visa-versa.  */
285       sal = find_pc_line (start_addr, 0);
286       val.loa = LOA_LINE;
287       val.u.line_no = sal.line;
288       TUI_SRC_WIN->update_source_window (gdbarch, sal.symtab, val);
289       if (sal.symtab)
290         {
291           set_current_source_symtab_and_line (sal);
292           tui_update_locator_fullname (symtab_to_fullname (sal.symtab));
293         }
294       else
295         tui_update_locator_fullname ("?");
296     }
297 }
298
299 void
300 tui_get_begin_asm_address (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
301 {
302   struct tui_locator_window *locator;
303   struct gdbarch *gdbarch = get_current_arch ();
304   CORE_ADDR addr;
305
306   locator = tui_locator_win_info_ptr ();
307
308   if (locator->addr == 0)
309     {
310       struct bound_minimal_symbol main_symbol;
311
312       /* Find address of the start of program.
313          Note: this should be language specific.  */
314       main_symbol = lookup_minimal_symbol ("main", NULL, NULL);
315       if (main_symbol.minsym == 0)
316         main_symbol = lookup_minimal_symbol ("MAIN", NULL, NULL);
317       if (main_symbol.minsym == 0)
318         main_symbol = lookup_minimal_symbol ("_start", NULL, NULL);
319       if (main_symbol.minsym)
320         addr = BMSYMBOL_VALUE_ADDRESS (main_symbol);
321       else
322         addr = 0;
323     }
324   else                          /* The target is executing.  */
325     {
326       gdbarch = locator->gdbarch;
327       addr = locator->addr;
328     }
329
330   *gdbarch_p = gdbarch;
331   *addr_p = addr;
332 }
333
334 /* Determine what the low address will be to display in the TUI's
335    disassembly window.  This may or may not be the same as the low
336    address input.  */
337 CORE_ADDR
338 tui_get_low_disassembly_address (struct gdbarch *gdbarch,
339                                  CORE_ADDR low, CORE_ADDR pc)
340 {
341   int pos;
342
343   /* Determine where to start the disassembly so that the pc is about
344      in the middle of the viewport.  */
345   pos = tui_default_win_viewport_height (DISASSEM_WIN, DISASSEM_COMMAND) / 2;
346   pc = tui_find_disassembly_address (gdbarch, pc, -pos);
347
348   if (pc < low)
349     pc = low;
350   return pc;
351 }
352
353 /* Scroll the disassembly forward or backward vertically.  */
354 void
355 tui_disasm_window::do_scroll_vertical (int num_to_scroll)
356 {
357   if (!content.empty ())
358     {
359       CORE_ADDR pc;
360       struct tui_line_or_address val;
361
362       pc = content[0].line_or_addr.u.addr;
363       if (num_to_scroll >= 0)
364         num_to_scroll++;
365       else
366         --num_to_scroll;
367
368       val.loa = LOA_ADDRESS;
369       val.u.addr = tui_find_disassembly_address (gdbarch, pc, num_to_scroll);
370       update_source_window_as_is (gdbarch, NULL, val);
371     }
372 }
373
374 bool
375 tui_disasm_window::location_matches_p (struct bp_location *loc, int line_no)
376 {
377   return (content[line_no].line_or_addr.loa == LOA_ADDRESS
378           && content[line_no].line_or_addr.u.addr == loc->address);
379 }
380
381 bool
382 tui_disasm_window::addr_is_displayed (CORE_ADDR addr) const
383 {
384   bool is_displayed = false;
385   int threshold = SCROLL_THRESHOLD;
386
387   int i = 0;
388   while (i < content.size () - threshold && !is_displayed)
389     {
390       is_displayed
391         = (content[i].line_or_addr.loa == LOA_ADDRESS
392            && content[i].line_or_addr.u.addr == addr);
393       i++;
394     }
395
396   return is_displayed;
397 }
398
399 void
400 tui_disasm_window::maybe_update (struct frame_info *fi, symtab_and_line sal,
401                                  int line_no, CORE_ADDR addr)
402 {
403   CORE_ADDR low;
404
405   if (find_pc_partial_function (get_frame_pc (fi),
406                                 NULL, &low, NULL) == 0)
407     {
408       /* There is no symbol available for current PC.  There is no
409          safe way how to "disassemble backwards".  */
410       low = get_frame_pc (fi);
411     }
412   else
413     low = tui_get_low_disassembly_address (get_frame_arch (fi),
414                                            low, get_frame_pc (fi));
415
416   struct tui_line_or_address a;
417
418   a.loa = LOA_ADDRESS;
419   a.u.addr = low;
420   if (!addr_is_displayed (addr))
421     update_source_window (get_frame_arch (fi), sal.symtab, a);
422   else
423     {
424       a.u.addr = addr;
425       set_is_exec_point_at (a);
426     }
427 }