GDB (xrefs)
Loading...
Searching...
No Matches
tui-disasm.c
Go to the documentation of this file.
1/* Disassembly display.
2
3 Copyright (C) 1998-2023 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-command.h"
32#include "tui/tui-data.h"
33#include "tui/tui-win.h"
34#include "tui/tui-layout.h"
35#include "tui/tui-winsource.h"
36#include "tui/tui-stack.h"
37#include "tui/tui-file.h"
38#include "tui/tui-disasm.h"
39#include "tui/tui-source.h"
40#include "progspace.h"
41#include "objfiles.h"
42#include "cli/cli-style.h"
43#include "tui/tui-location.h"
44#include "gdbsupport/selftest.h"
45#include "inferior.h"
46
47#include "gdb_curses.h"
48
50{
51 CORE_ADDR addr;
52 std::string addr_string;
53 size_t addr_size;
54 std::string insn;
55};
56
57/* Helper function to find the number of characters in STR, skipping
58 any ANSI escape sequences. */
59static size_t
60len_without_escapes (const std::string &str)
61{
62 size_t len = 0;
63 const char *ptr = str.c_str ();
64 char c;
65
66 while ((c = *ptr) != '\0')
67 {
68 if (c == '\033')
69 {
70 ui_file_style style;
71 size_t n_read;
72 if (style.parse (ptr, &n_read))
73 ptr += n_read;
74 else
75 {
76 /* Shouldn't happen, but just skip the ESC if it somehow
77 does. */
78 ++ptr;
79 }
80 }
81 else
82 {
83 ++len;
84 ++ptr;
85 }
86 }
87 return len;
88}
89
90/* Function to disassemble up to COUNT instructions starting from address
91 PC into the ASM_LINES vector (which will be emptied of any previous
92 contents). Return the address of the COUNT'th instruction after pc.
93 When ADDR_SIZE is non-null then place the maximum size of an address and
94 label into the value pointed to by ADDR_SIZE, and set the addr_size
95 field on each item in ASM_LINES, otherwise the addr_size fields within
96 ASM_LINES are undefined.
97
98 It is worth noting that ASM_LINES might not have COUNT entries when this
99 function returns. If the disassembly is truncated for some other
100 reason, for example, we hit invalid memory, then ASM_LINES can have
101 fewer entries than requested. */
102static CORE_ADDR
104 std::vector<tui_asm_line> &asm_lines,
105 CORE_ADDR pc, int count,
106 size_t *addr_size = nullptr)
107{
108 bool term_out = source_styling && gdb_stdout->can_emit_style_escape ();
109 string_file gdb_dis_out (term_out);
110
111 /* Must start with an empty list. */
112 asm_lines.clear ();
113
114 /* Now construct each line. */
115 for (int i = 0; i < count; ++i)
116 {
117 tui_asm_line tal;
118 CORE_ADDR orig_pc = pc;
119
120 try
121 {
122 pc = pc + gdb_print_insn (gdbarch, pc, &gdb_dis_out, NULL);
123 }
124 catch (const gdb_exception_error &except)
125 {
126 /* If PC points to an invalid address then we'll catch a
127 MEMORY_ERROR here, this should stop the disassembly, but
128 otherwise is fine. */
129 if (except.error != MEMORY_ERROR)
130 throw;
131 return pc;
132 }
133
134 /* Capture the disassembled instruction. */
135 tal.insn = gdb_dis_out.release ();
136
137 /* And capture the address the instruction is at. */
138 tal.addr = orig_pc;
139 print_address (gdbarch, orig_pc, &gdb_dis_out);
140 tal.addr_string = gdb_dis_out.release ();
141
142 if (addr_size != nullptr)
143 {
144 size_t new_size;
145
146 if (term_out)
147 new_size = len_without_escapes (tal.addr_string);
148 else
149 new_size = tal.addr_string.size ();
150 *addr_size = std::max (*addr_size, new_size);
151 tal.addr_size = new_size;
152 }
153
154 asm_lines.push_back (std::move (tal));
155 }
156 return pc;
157}
158
159/* Look backward from ADDR for an address from which we can start
160 disassembling, this needs to be something we can be reasonably
161 confident will fall on an instruction boundary. We use msymbol
162 addresses, or the start of a section. */
163
164static CORE_ADDR
166{
167 struct bound_minimal_symbol msym, msym_prev;
168
169 msym = lookup_minimal_symbol_by_pc_section (addr - 1, nullptr,
171 &msym_prev);
172 if (msym.minsym != nullptr)
173 return msym.value_address ();
174 else if (msym_prev.minsym != nullptr)
175 return msym_prev.value_address ();
176
177 /* Find the section that ADDR is in, and look for the start of the
178 section. */
179 struct obj_section *section = find_pc_section (addr);
180 if (section != NULL)
181 return section->addr ();
182
183 return addr;
184}
185
186/* Find the disassembly address that corresponds to FROM lines above
187 or below the PC. Variable sized instructions are taken into
188 account by the algorithm. */
189static CORE_ADDR
190tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from)
191{
192 CORE_ADDR new_low;
193 int max_lines;
194
195 max_lines = (from > 0) ? from : - from;
196 if (max_lines == 0)
197 return pc;
198
199 std::vector<tui_asm_line> asm_lines;
200
201 new_low = pc;
202 if (from > 0)
203 {
204 /* Always disassemble 1 extra instruction here, then if the last
205 instruction fails to disassemble we will take the address of the
206 previous instruction that did disassemble as the result. */
207 tui_disassemble (gdbarch, asm_lines, pc, max_lines + 1);
208 if (asm_lines.empty ())
209 return pc;
210 new_low = asm_lines.back ().addr;
211 }
212 else
213 {
214 /* In order to disassemble backwards we need to find a suitable
215 address to start disassembling from and then work forward until we
216 re-find the address we're currently at. We can then figure out
217 which address will be at the top of the TUI window after our
218 backward scroll. During our backward disassemble we need to be
219 able to distinguish between the case where the last address we
220 _can_ disassemble is ADDR, and the case where the disassembly
221 just happens to stop at ADDR, for this reason we increase
222 MAX_LINES by one. */
223 max_lines++;
224
225 /* When we disassemble a series of instructions this will hold the
226 address of the last instruction disassembled. */
227 CORE_ADDR last_addr;
228
229 /* And this will hold the address of the next instruction that would
230 have been disassembled. */
231 CORE_ADDR next_addr;
232
233 /* As we search backward if we find an address that looks like a
234 promising starting point then we record it in this structure. If
235 the next address we try is not a suitable starting point then we
236 will fall back to the address held here. */
237 gdb::optional<CORE_ADDR> possible_new_low;
238
239 /* The previous value of NEW_LOW so we know if the new value is
240 different or not. */
241 CORE_ADDR prev_low;
242
243 do
244 {
245 /* Find an address from which we can start disassembling. */
246 prev_low = new_low;
248
249 /* Disassemble forward. */
250 next_addr = tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
251 if (asm_lines.empty ())
252 break;
253 last_addr = asm_lines.back ().addr;
254
255 /* If disassembling from the current value of NEW_LOW reached PC
256 (or went past it) then this would do as a starting point if we
257 can't find anything better, so remember it. */
258 if (last_addr >= pc && new_low != prev_low
259 && asm_lines.size () >= max_lines)
260 possible_new_low.emplace (new_low);
261
262 /* Continue searching until we find a value of NEW_LOW from which
263 disassembling MAX_LINES instructions doesn't reach PC. We
264 know this means we can find the required number of previous
265 instructions then. */
266 }
267 while ((last_addr > pc
268 || (last_addr == pc && asm_lines.size () < max_lines))
269 && new_low != prev_low);
270
271 /* If we failed to disassemble the required number of lines then the
272 following walk forward is not going to work, it assumes that
273 ASM_LINES contains exactly MAX_LINES entries. Instead we should
274 consider falling back to a previous possible start address in
275 POSSIBLE_NEW_LOW. */
276 if (asm_lines.size () < max_lines)
277 {
278 if (!possible_new_low.has_value ())
279 return new_low;
280
281 /* Take the best possible match we have. */
282 new_low = *possible_new_low;
283 next_addr = tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
284 last_addr = asm_lines.back ().addr;
285 gdb_assert (asm_lines.size () >= max_lines);
286 }
287
288 /* Scan forward disassembling one instruction at a time until
289 the last visible instruction of the window matches the pc.
290 We keep the disassembled instructions in the 'lines' window
291 and shift it downward (increasing its addresses). */
292 int pos = max_lines - 1;
293 if (last_addr < pc)
294 do
295 {
296 pos++;
297 if (pos >= max_lines)
298 pos = 0;
299
300 CORE_ADDR old_next_addr = next_addr;
301 std::vector<tui_asm_line> single_asm_line;
302 next_addr = tui_disassemble (gdbarch, single_asm_line,
303 next_addr, 1);
304 /* If there are some problems while disassembling exit. */
305 if (next_addr <= old_next_addr)
306 return pc;
307 gdb_assert (single_asm_line.size () == 1);
308 asm_lines[pos] = single_asm_line[0];
309 } while (next_addr <= pc);
310 pos++;
311 if (pos >= max_lines)
312 pos = 0;
313 new_low = asm_lines[pos].addr;
314
315 /* When scrolling backward the addresses should move backward, or at
316 the very least stay the same if we are at the first address that
317 can be disassembled. */
318 gdb_assert (new_low <= pc);
319 }
320 return new_low;
321}
322
323/* Function to set the disassembly window's content. */
324bool
326 const struct symtab_and_line &sal)
327{
328 int i;
329 int max_lines;
330 CORE_ADDR cur_pc;
331 int tab_len = tui_tab_width;
332 int insn_pos;
333
334 CORE_ADDR pc = sal.pc;
335 if (pc == 0)
336 return false;
337
338 m_gdbarch = arch;
341 cur_pc = tui_location.addr ();
342
343 /* Window size, excluding highlight box. */
344 max_lines = height - 2;
345
346 /* Get temporary table that will hold all strings (addr & insn). */
347 std::vector<tui_asm_line> asm_lines;
348 size_t addr_size = 0;
349 tui_disassemble (m_gdbarch, asm_lines, pc, max_lines, &addr_size);
350
351 /* Align instructions to the same column. */
352 insn_pos = (1 + (addr_size / tab_len)) * tab_len;
353
354 /* Now construct each line. */
355 m_content.resize (max_lines);
356 m_max_length = -1;
357 for (i = 0; i < max_lines; i++)
358 {
359 tui_source_element *src = &m_content[i];
360
361 std::string line;
362 CORE_ADDR addr;
363
364 if (i < asm_lines.size ())
365 {
366 line
367 = (asm_lines[i].addr_string
368 + n_spaces (insn_pos - asm_lines[i].addr_size)
369 + asm_lines[i].insn);
370 addr = asm_lines[i].addr;
371 }
372 else
373 {
374 line = "";
375 addr = 0;
376 }
377
378 const char *ptr = line.c_str ();
379 int line_len;
380 src->line = tui_copy_source_line (&ptr, &line_len);
381 m_max_length = std::max (m_max_length, line_len);
382
384 src->line_or_addr.u.addr = addr;
385 src->is_exec_point = (addr == cur_pc && line.size () > 0);
386 }
387 return true;
388}
389
390
391void
392tui_get_begin_asm_address (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
393{
394 struct gdbarch *gdbarch = get_current_arch ();
395 CORE_ADDR addr = 0;
396
397 if (tui_location.addr () == 0)
398 {
400 {
403
404 if (sal.symtab != nullptr)
405 find_line_pc (sal.symtab, sal.line, &addr);
406 }
407
408 if (addr == 0)
409 {
410 struct bound_minimal_symbol main_symbol
411 = lookup_minimal_symbol (main_name (), nullptr, nullptr);
412 if (main_symbol.minsym != nullptr)
413 addr = main_symbol.value_address ();
414 }
415 }
416 else /* The target is executing. */
417 {
419 addr = tui_location.addr ();
420 }
421
422 *gdbarch_p = gdbarch;
423 *addr_p = addr;
424}
425
426/* Determine what the low address will be to display in the TUI's
427 disassembly window. This may or may not be the same as the low
428 address input. */
429CORE_ADDR
431 CORE_ADDR low, CORE_ADDR pc)
432{
433 int pos;
434
435 /* Determine where to start the disassembly so that the pc is about
436 in the middle of the viewport. */
437 if (TUI_DISASM_WIN != NULL)
438 pos = TUI_DISASM_WIN->height;
439 else if (TUI_CMD_WIN == NULL)
440 pos = tui_term_height () / 2 - 2;
441 else
442 pos = tui_term_height () - TUI_CMD_WIN->height - 2;
443 pos = (pos - 2) / 2;
444
445 pc = tui_find_disassembly_address (gdbarch, pc, -pos);
446
447 if (pc < low)
448 pc = low;
449 return pc;
450}
451
452/* Scroll the disassembly forward or backward vertically. */
453void
455{
456 if (!m_content.empty ())
457 {
458 CORE_ADDR pc;
459
461
462 symtab_and_line sal {};
464 sal.pc = tui_find_disassembly_address (m_gdbarch, pc, num_to_scroll);
466 }
467}
468
469bool
471{
472 return (m_content[line_no].line_or_addr.loa == LOA_ADDRESS
473 && m_content[line_no].line_or_addr.u.addr == loc->address);
474}
475
476bool
478{
479 if (m_content.size () < SCROLL_THRESHOLD)
480 return false;
481
482 for (size_t i = 0; i < m_content.size () - SCROLL_THRESHOLD; ++i)
483 {
484 if (m_content[i].line_or_addr.loa == LOA_ADDRESS
485 && m_content[i].line_or_addr.u.addr == addr)
486 return true;
487 }
488
489 return false;
490}
491
492void
494{
495 CORE_ADDR low;
496
497 struct gdbarch *frame_arch = get_frame_arch (fi);
498
499 if (find_pc_partial_function (sal.pc, NULL, &low, NULL) == 0)
500 {
501 /* There is no symbol available for current PC. There is no
502 safe way how to "disassemble backwards". */
503 low = sal.pc;
504 }
505 else
506 low = tui_get_low_disassembly_address (frame_arch, low, sal.pc);
507
508 struct tui_line_or_address a;
509
510 a.loa = LOA_ADDRESS;
511 a.u.addr = low;
512 if (!addr_is_displayed (sal.pc))
513 {
514 sal.pc = low;
515 update_source_window (frame_arch, sal);
516 }
517 else
518 {
519 a.u.addr = sal.pc;
521 }
522}
523
524void
526 CORE_ADDR *addr_p)
527{
528 *gdbarch_p = m_gdbarch;
529 *addr_p = m_start_line_or_addr.u.addr;
530}
531
532#if GDB_SELF_TEST
533namespace selftests {
534namespace tui {
535namespace disasm {
536
537static void
538run_tests ()
539{
540 if (current_inferior () != nullptr)
541 {
543
544 /* Check that tui_find_disassembly_address robustly handles the case of
545 being passed a PC for which gdb_print_insn throws a MEMORY_ERROR. */
546 SELF_CHECK (tui_find_disassembly_address (gdbarch, 0, 1) == 0);
547 SELF_CHECK (tui_find_disassembly_address (gdbarch, 0, -1) == 0);
548 }
549}
550
551} /* namespace disasm */
552} /* namespace tui */
553} /* namespace selftests */
554#endif /* GDB_SELF_TEST */
555
557void
559{
560#if GDB_SELF_TEST
561 selftests::register_test ("tui-disasm", selftests::tui::disasm::run_tests);
562#endif
563}
struct gdbarch * get_current_arch(void)
Definition arch-utils.c:846
bool find_pc_partial_function(CORE_ADDR pc, const char **name, CORE_ADDR *address, CORE_ADDR *endaddr, const struct block **block)
Definition blockframe.c:373
struct gdbarch * gdbarch
Definition inferior.h:661
std::string release()
Definition ui-file.h:204
bool source_styling
Definition cli-style.c:39
void print_address(struct gdbarch *, CORE_ADDR, struct ui_file *)
Definition printcmd.c:739
int gdb_print_insn(struct gdbarch *gdbarch, CORE_ADDR memaddr, struct ui_file *stream, int *branch_delay_insns)
Definition disasm.c:1217
struct gdbarch * get_frame_arch(frame_info_ptr this_frame)
Definition frame.c:3027
struct inferior * current_inferior(void)
Definition inferior.c:55
bound_minimal_symbol lookup_minimal_symbol_by_pc_section(CORE_ADDR pc_in, struct obj_section *section, lookup_msym_prefer prefer, bound_minimal_symbol *previous)
Definition minsyms.c:746
struct bound_minimal_symbol lookup_minimal_symbol(const char *name, const char *sfile, struct objfile *objf)
Definition minsyms.c:363
struct obj_section * find_pc_section(CORE_ADDR pc)
Definition objfiles.c:1128
int have_partial_symbols(void)
Definition objfiles.c:763
int have_full_symbols(void)
Definition objfiles.c:778
struct program_space * current_program_space
Definition progspace.c:40
struct symtab_and_line get_current_source_symtab_and_line(void)
Definition source.c:239
void set_default_source_symtab_and_line(void)
Definition source.c:262
CORE_ADDR value_address() const
Definition minsyms.h:41
struct minimal_symbol * minsym
Definition minsyms.h:49
CORE_ADDR addr() const
Definition objfiles.h:385
struct symtab * symtab
Definition symtab.h:2328
CORE_ADDR pc
Definition symtab.h:2337
struct program_space * pspace
Definition symtab.h:2326
std::string addr_string
Definition tui-disasm.c:52
std::string insn
Definition tui-disasm.c:54
size_t addr_size
Definition tui-disasm.c:53
CORE_ADDR addr
Definition tui-disasm.c:51
bool set_contents(struct gdbarch *gdbarch, const struct symtab_and_line &sal) override
Definition tui-disasm.c:325
bool location_matches_p(struct bp_location *loc, int line_no) override
Definition tui-disasm.c:470
void maybe_update(frame_info_ptr fi, symtab_and_line sal) override
Definition tui-disasm.c:493
void do_scroll_vertical(int num_to_scroll) override
Definition tui-disasm.c:454
void display_start_addr(struct gdbarch **gdbarch_p, CORE_ADDR *addr_p) override
Definition tui-disasm.c:525
bool addr_is_displayed(CORE_ADDR addr) const
Definition tui-disasm.c:477
enum tui_line_or_address_kind loa
union tui_line_or_address::@192 u
struct gdbarch * gdbarch() const
CORE_ADDR addr() const
struct tui_line_or_address line_or_addr
struct tui_line_or_address m_start_line_or_addr
void update_source_window(struct gdbarch *gdbarch, const struct symtab_and_line &sal)
struct gdbarch * m_gdbarch
std::vector< tui_source_element > m_content
void set_is_exec_point_at(struct tui_line_or_address l)
void update_source_window_as_is(struct gdbarch *gdbarch, const struct symtab_and_line &sal)
bool find_line_pc(struct symtab *symtab, int line, CORE_ADDR *pc)
Definition symtab.c:3472
const char * main_name()
Definition symtab.c:6322
int tui_term_height(void)
Definition tui-data.c:78
#define TUI_DISASM_WIN
Definition tui-data.h:196
unsigned int tui_tab_width
Definition tui-win.c:807
#define TUI_CMD_WIN
Definition tui-data.h:198
void _initialize_tui_disasm()
Definition tui-disasm.c:558
static CORE_ADDR tui_disassemble(struct gdbarch *gdbarch, std::vector< tui_asm_line > &asm_lines, CORE_ADDR pc, int count, size_t *addr_size=nullptr)
Definition tui-disasm.c:103
static CORE_ADDR tui_find_disassembly_address(struct gdbarch *gdbarch, CORE_ADDR pc, int from)
Definition tui-disasm.c:190
static CORE_ADDR tui_find_backward_disassembly_start_address(CORE_ADDR addr)
Definition tui-disasm.c:165
static size_t len_without_escapes(const std::string &str)
Definition tui-disasm.c:60
CORE_ADDR tui_get_low_disassembly_address(struct gdbarch *gdbarch, CORE_ADDR low, CORE_ADDR pc)
Definition tui-disasm.c:430
void tui_get_begin_asm_address(struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
Definition tui-disasm.c:392
tui_location_tracker tui_location
std::string tui_copy_source_line(const char **ptr, int *length)
@ LOA_ADDRESS
#define SCROLL_THRESHOLD
const char * n_spaces(int n)
Definition utils.c:1947
#define gdb_stdout
Definition utils.h:182