4899b1d03cf0a11aa9df59ebb9b21d61e0cb4aaf
[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 "progspace.h"
39 #include "objfiles.h"
40
41 #include "gdb_curses.h"
42
43 struct tui_asm_line 
44 {
45   CORE_ADDR addr;
46   char *addr_string;
47   char *insn;
48 };
49
50 /* Function to set the disassembly window's content.
51    Disassemble count lines starting at pc.
52    Return address of the count'th instruction after pc.  */
53 static CORE_ADDR
54 tui_disassemble (struct gdbarch *gdbarch, struct tui_asm_line *asm_lines,
55                  CORE_ADDR pc, int count)
56 {
57   string_file gdb_dis_out;
58
59   /* Now construct each line.  */
60   for (; count > 0; count--, asm_lines++)
61     {
62       xfree (asm_lines->addr_string);
63       xfree (asm_lines->insn);
64       
65       print_address (gdbarch, pc, &gdb_dis_out);
66       asm_lines->addr = pc;
67       asm_lines->addr_string = xstrdup (gdb_dis_out.c_str ());
68
69       gdb_dis_out.clear ();
70
71       pc = pc + gdb_print_insn (gdbarch, pc, &gdb_dis_out, NULL);
72
73       asm_lines->insn = xstrdup (gdb_dis_out.c_str ());
74
75       /* Reset the buffer to empty.  */
76       gdb_dis_out.clear ();
77     }
78   return pc;
79 }
80
81 /* Find the disassembly address that corresponds to FROM lines above
82    or below the PC.  Variable sized instructions are taken into
83    account by the algorithm.  */
84 static CORE_ADDR
85 tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from)
86 {
87   CORE_ADDR new_low;
88   int max_lines;
89   int i;
90   struct tui_asm_line *asm_lines;
91
92   max_lines = (from > 0) ? from : - from;
93   if (max_lines <= 1)
94      return pc;
95
96   asm_lines = XALLOCAVEC (struct tui_asm_line, max_lines);
97   memset (asm_lines, 0, sizeof (struct tui_asm_line) * max_lines);
98
99   new_low = pc;
100   if (from > 0)
101     {
102       tui_disassemble (gdbarch, asm_lines, pc, max_lines);
103       new_low = asm_lines[max_lines - 1].addr;
104     }
105   else
106     {
107       CORE_ADDR last_addr;
108       int pos;
109       struct bound_minimal_symbol msymbol;
110               
111       /* Find backward an address which is a symbol and for which
112          disassembling from that address will fill completely the
113          window.  */
114       pos = max_lines - 1;
115       do {
116          new_low -= 1 * max_lines;
117          msymbol = lookup_minimal_symbol_by_pc_section (new_low, 0);
118
119          if (msymbol.minsym)
120             new_low = BMSYMBOL_VALUE_ADDRESS (msymbol);
121          else
122             new_low += 1 * max_lines;
123
124          tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
125          last_addr = asm_lines[pos].addr;
126       } while (last_addr > pc && msymbol.minsym);
127
128       /* Scan forward disassembling one instruction at a time until
129          the last visible instruction of the window matches the pc.
130          We keep the disassembled instructions in the 'lines' window
131          and shift it downward (increasing its addresses).  */
132       if (last_addr < pc)
133         do
134           {
135             CORE_ADDR next_addr;
136                  
137             pos++;
138             if (pos >= max_lines)
139               pos = 0;
140
141             next_addr = tui_disassemble (gdbarch, &asm_lines[pos],
142                                          last_addr, 1);
143
144             /* If there are some problems while disassembling exit.  */
145             if (next_addr <= last_addr)
146               break;
147             last_addr = next_addr;
148           } while (last_addr <= pc);
149       pos++;
150       if (pos >= max_lines)
151          pos = 0;
152       new_low = asm_lines[pos].addr;
153     }
154   for (i = 0; i < max_lines; i++)
155     {
156       xfree (asm_lines[i].addr_string);
157       xfree (asm_lines[i].insn);
158     }
159   return new_low;
160 }
161
162 /* Function to set the disassembly window's content.  */
163 enum tui_status
164 tui_set_disassem_content (struct gdbarch *gdbarch, CORE_ADDR pc)
165 {
166   int i;
167   int offset = TUI_DISASM_WIN->horizontal_offset;
168   int max_lines, line_width;
169   CORE_ADDR cur_pc;
170   struct tui_locator_window *locator = tui_locator_win_info_ptr ();
171   int tab_len = tui_tab_width;
172   struct tui_asm_line *asm_lines;
173   int insn_pos;
174   int addr_size, insn_size;
175   char *line;
176   
177   if (pc == 0)
178     return TUI_FAILURE;
179
180   tui_alloc_source_buffer (TUI_DISASM_WIN);
181
182   tui_source_window_base *base = TUI_DISASM_WIN;
183   base->gdbarch = gdbarch;
184   base->start_line_or_addr.loa = LOA_ADDRESS;
185   base->start_line_or_addr.u.addr = pc;
186   cur_pc = locator->addr;
187
188   /* Window size, excluding highlight box.  */
189   max_lines = TUI_DISASM_WIN->height - 2;
190   line_width = TUI_DISASM_WIN->width - 2;
191
192   /* Get temporary table that will hold all strings (addr & insn).  */
193   asm_lines = XALLOCAVEC (struct tui_asm_line, max_lines);
194   memset (asm_lines, 0, sizeof (struct tui_asm_line) * max_lines);
195
196   tui_disassemble (gdbarch, asm_lines, pc, max_lines);
197
198   /* Determine maximum address- and instruction lengths.  */
199   addr_size = 0;
200   insn_size = 0;
201   for (i = 0; i < max_lines; i++)
202     {
203       size_t len = strlen (asm_lines[i].addr_string);
204
205       if (len > addr_size)
206         addr_size = len;
207
208       len = strlen (asm_lines[i].insn);
209       if (len > insn_size)
210         insn_size = len;
211     }
212
213   /* Align instructions to the same column.  */
214   insn_pos = (1 + (addr_size / tab_len)) * tab_len;
215
216   /* Allocate memory to create each line.  */
217   line = (char*) alloca (insn_pos + insn_size + 1);
218
219   /* Now construct each line.  */
220   TUI_DISASM_WIN->content.resize (max_lines);
221   for (i = 0; i < max_lines; i++)
222     {
223       int cur_len;
224
225       tui_source_element *src = &TUI_DISASM_WIN->content[i];
226       strcpy (line, asm_lines[i].addr_string);
227       cur_len = strlen (line);
228       memset (line + cur_len, ' ', insn_pos - cur_len);
229       strcpy (line + insn_pos, asm_lines[i].insn);
230
231       /* Now copy the line taking the offset into account.  */
232       xfree (src->line);
233       if (strlen (line) > offset)
234         src->line = xstrndup (&line[offset], line_width);
235       else
236         src->line = xstrdup ("");
237
238       src->line_or_addr.loa = LOA_ADDRESS;
239       src->line_or_addr.u.addr = asm_lines[i].addr;
240       src->is_exec_point = asm_lines[i].addr == cur_pc;
241
242       /* See whether there is a breakpoint installed.  */
243       src->has_break = (!src->is_exec_point
244                         && breakpoint_here_p (current_program_space->aspace,
245                                               pc)
246                         != no_breakpoint_here);
247
248       xfree (asm_lines[i].addr_string);
249       xfree (asm_lines[i].insn);
250     }
251   return TUI_SUCCESS;
252 }
253
254
255 /* Function to display the disassembly window with disassembled code.  */
256 void
257 tui_show_disassem (struct gdbarch *gdbarch, CORE_ADDR start_addr)
258 {
259   struct symtab *s = find_pc_line_symtab (start_addr);
260   struct tui_win_info *win_with_focus = tui_win_with_focus ();
261   struct tui_line_or_address val;
262
263   val.loa = LOA_ADDRESS;
264   val.u.addr = start_addr;
265   tui_add_win_to_layout (DISASSEM_WIN);
266   tui_update_source_window (TUI_DISASM_WIN, gdbarch, s, val, FALSE);
267
268   /* If the focus was in the src win, put it in the asm win, if the
269      source view isn't split.  */
270   if (tui_current_layout () != SRC_DISASSEM_COMMAND 
271       && win_with_focus == TUI_SRC_WIN)
272     tui_set_win_focus_to (TUI_DISASM_WIN);
273
274   return;
275 }
276
277
278 /* Function to display the disassembly window.  */
279 void
280 tui_show_disassem_and_update_source (struct gdbarch *gdbarch,
281                                      CORE_ADDR start_addr)
282 {
283   struct symtab_and_line sal;
284
285   tui_show_disassem (gdbarch, start_addr);
286   if (tui_current_layout () == SRC_DISASSEM_COMMAND)
287     {
288       struct tui_line_or_address val;
289
290       /* Update what is in the source window if it is displayed too,
291          note that it follows what is in the disassembly window and
292          visa-versa.  */
293       sal = find_pc_line (start_addr, 0);
294       val.loa = LOA_LINE;
295       val.u.line_no = sal.line;
296       tui_update_source_window (TUI_SRC_WIN, gdbarch, sal.symtab, val, TRUE);
297       if (sal.symtab)
298         {
299           set_current_source_symtab_and_line (sal);
300           tui_update_locator_fullname (symtab_to_fullname (sal.symtab));
301         }
302       else
303         tui_update_locator_fullname ("?");
304     }
305
306   return;
307 }
308
309 void
310 tui_get_begin_asm_address (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
311 {
312   struct tui_locator_window *locator;
313   struct gdbarch *gdbarch = get_current_arch ();
314   CORE_ADDR addr;
315
316   locator = tui_locator_win_info_ptr ();
317
318   if (locator->addr == 0)
319     {
320       struct bound_minimal_symbol main_symbol;
321
322       /* Find address of the start of program.
323          Note: this should be language specific.  */
324       main_symbol = lookup_minimal_symbol ("main", NULL, NULL);
325       if (main_symbol.minsym == 0)
326         main_symbol = lookup_minimal_symbol ("MAIN", NULL, NULL);
327       if (main_symbol.minsym == 0)
328         main_symbol = lookup_minimal_symbol ("_start", NULL, NULL);
329       if (main_symbol.minsym)
330         addr = BMSYMBOL_VALUE_ADDRESS (main_symbol);
331       else
332         addr = 0;
333     }
334   else                          /* The target is executing.  */
335     {
336       gdbarch = locator->gdbarch;
337       addr = locator->addr;
338     }
339
340   *gdbarch_p = gdbarch;
341   *addr_p = addr;
342 }
343
344 /* Determine what the low address will be to display in the TUI's
345    disassembly window.  This may or may not be the same as the low
346    address input.  */
347 CORE_ADDR
348 tui_get_low_disassembly_address (struct gdbarch *gdbarch,
349                                  CORE_ADDR low, CORE_ADDR pc)
350 {
351   int pos;
352
353   /* Determine where to start the disassembly so that the pc is about
354      in the middle of the viewport.  */
355   pos = tui_default_win_viewport_height (DISASSEM_WIN, DISASSEM_COMMAND) / 2;
356   pc = tui_find_disassembly_address (gdbarch, pc, -pos);
357
358   if (pc < low)
359     pc = low;
360   return pc;
361 }
362
363 /* Scroll the disassembly forward or backward vertically.  */
364 void
365 tui_disasm_window::do_scroll_vertical (int num_to_scroll)
366 {
367   if (!content.empty ())
368     {
369       CORE_ADDR pc;
370       struct tui_line_or_address val;
371
372       pc = content[0].line_or_addr.u.addr;
373       if (num_to_scroll >= 0)
374         num_to_scroll++;
375       else
376         --num_to_scroll;
377
378       val.loa = LOA_ADDRESS;
379       val.u.addr = tui_find_disassembly_address (gdbarch, pc, num_to_scroll);
380       tui_update_source_window_as_is (this, gdbarch,
381                                       NULL, val, FALSE);
382     }
383 }