]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/tui/tui-disasm.c
Update copyright year range in header of all files managed by GDB
[thirdparty/binutils-gdb.git] / gdb / tui / tui-disasm.c
CommitLineData
f377b406 1/* Disassembly display.
f33c6cbf 2
1d506c26 3 Copyright (C) 1998-2024 Free Software Foundation, Inc.
f33c6cbf 4
f377b406
SC
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
a9762ec7 11 the Free Software Foundation; either version 3 of the License, or
f377b406
SC
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
a9762ec7 20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
c906108c
SS
21
22#include "defs.h"
957b8b5a 23#include "arch-utils.h"
c906108c
SS
24#include "symtab.h"
25#include "breakpoint.h"
26#include "frame.h"
fd0407d6 27#include "value.h"
52575520 28#include "source.h"
f70a7d61 29#include "disasm.h"
d7b2e967 30#include "tui/tui.h"
6d7fd9aa 31#include "tui/tui-command.h"
d7b2e967
AC
32#include "tui/tui-data.h"
33#include "tui/tui-win.h"
34#include "tui/tui-layout.h"
35#include "tui/tui-winsource.h"
cf2ef009 36#include "tui/tui-status.h"
d7b2e967 37#include "tui/tui-file.h"
2c0b251b 38#include "tui/tui-disasm.h"
bfad4537 39#include "tui/tui-source.h"
6c95b8df 40#include "progspace.h"
77e371c0 41#include "objfiles.h"
1df2f9ef 42#include "cli/cli-style.h"
f237f998 43#include "tui/tui-location.h"
72535eb1
TV
44#include "gdbsupport/selftest.h"
45#include "inferior.h"
c906108c 46
6a83354a 47#include "gdb_curses.h"
96ec9981 48
a61b4f69 49struct tui_asm_line
aec2f747
SC
50{
51 CORE_ADDR addr;
6b915f7d 52 std::string addr_string;
825165c5 53 size_t addr_size;
6b915f7d 54 std::string insn;
aec2f747
SC
55};
56
1df2f9ef
TT
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
d811a7cf 66 while ((c = *ptr) != '\0')
1df2f9ef
TT
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
d811a7cf
TV
82 {
83 ++len;
84 ++ptr;
85 }
1df2f9ef
TT
86 }
87 return len;
88}
89
733d0a67
AB
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. */
aec2f747 102static CORE_ADDR
6b915f7d
TT
103tui_disassemble (struct gdbarch *gdbarch,
104 std::vector<tui_asm_line> &asm_lines,
733d0a67 105 CORE_ADDR pc, int count,
1df2f9ef 106 size_t *addr_size = nullptr)
aec2f747 107{
1df2f9ef
TT
108 bool term_out = source_styling && gdb_stdout->can_emit_style_escape ();
109 string_file gdb_dis_out (term_out);
aec2f747 110
733d0a67
AB
111 /* Must start with an empty list. */
112 asm_lines.clear ();
113
1cc6d956 114 /* Now construct each line. */
6b915f7d 115 for (int i = 0; i < count; ++i)
aec2f747 116 {
733d0a67
AB
117 tui_asm_line tal;
118 CORE_ADDR orig_pc = pc;
aec2f747 119
733d0a67
AB
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. */
5d10a204 135 tal.insn = gdb_dis_out.release ();
733d0a67
AB
136
137 /* And capture the address the instruction is at. */
138 tal.addr = orig_pc;
139 print_address (gdbarch, orig_pc, &gdb_dis_out);
12735d34 140 tal.addr_string = gdb_dis_out.release ();
aec2f747 141
1df2f9ef
TT
142 if (addr_size != nullptr)
143 {
144 size_t new_size;
145
146 if (term_out)
733d0a67 147 new_size = len_without_escapes (tal.addr_string);
1df2f9ef 148 else
733d0a67 149 new_size = tal.addr_string.size ();
1df2f9ef 150 *addr_size = std::max (*addr_size, new_size);
733d0a67 151 tal.addr_size = new_size;
1df2f9ef
TT
152 }
153
733d0a67 154 asm_lines.push_back (std::move (tal));
aec2f747 155 }
aec2f747
SC
156 return pc;
157}
158
733d0a67
AB
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
165tui_find_backward_disassembly_start_address (CORE_ADDR addr)
166{
167 struct bound_minimal_symbol msym, msym_prev;
168
169 msym = lookup_minimal_symbol_by_pc_section (addr - 1, nullptr,
170 lookup_msym_prefer::TEXT,
171 &msym_prev);
172 if (msym.minsym != nullptr)
4aeddc50 173 return msym.value_address ();
733d0a67 174 else if (msym_prev.minsym != nullptr)
4aeddc50 175 return msym_prev.value_address ();
733d0a67
AB
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)
0c1bcd23 181 return section->addr ();
733d0a67
AB
182
183 return addr;
184}
185
1cc6d956
MS
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. */
aec2f747 189static CORE_ADDR
13274fc3 190tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from)
aec2f747 191{
d02c80cd 192 CORE_ADDR new_low;
6ba8e26f 193 int max_lines;
aec2f747 194
6ba8e26f 195 max_lines = (from > 0) ? from : - from;
733d0a67 196 if (max_lines == 0)
6b915f7d 197 return pc;
aec2f747 198
733d0a67 199 std::vector<tui_asm_line> asm_lines;
aec2f747 200
6ba8e26f 201 new_low = pc;
aec2f747
SC
202 if (from > 0)
203 {
733d0a67
AB
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);
72535eb1
TV
208 if (asm_lines.empty ())
209 return pc;
733d0a67 210 new_low = asm_lines.back ().addr;
aec2f747
SC
211 }
212 else
213 {
733d0a67
AB
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. */
aec2f747 227 CORE_ADDR last_addr;
733d0a67
AB
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. */
6b09f134 237 std::optional<CORE_ADDR> possible_new_low;
733d0a67
AB
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;
247 new_low = tui_find_backward_disassembly_start_address (new_low);
248
249 /* Disassemble forward. */
250 next_addr = tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
72535eb1
TV
251 if (asm_lines.empty ())
252 break;
733d0a67
AB
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
aba9fa5f
TV
271 /* If we failed to disassemble the required number of lines, try to fall
272 back to a previous possible start address in POSSIBLE_NEW_LOW. */
733d0a67
AB
273 if (asm_lines.size () < max_lines)
274 {
275 if (!possible_new_low.has_value ())
42330a68 276 return new_low;
733d0a67
AB
277
278 /* Take the best possible match we have. */
279 new_low = *possible_new_low;
280 next_addr = tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
733d0a67 281 }
aec2f747 282
aba9fa5f
TV
283 /* The following walk forward assumes that ASM_LINES contains exactly
284 MAX_LINES entries. */
285 gdb_assert (asm_lines.size () == max_lines);
286
1cc6d956 287 /* Scan forward disassembling one instruction at a time until
dda83cd7
SM
288 the last visible instruction of the window matches the pc.
289 We keep the disassembled instructions in the 'lines' window
290 and shift it downward (increasing its addresses). */
733d0a67 291 int pos = max_lines - 1;
6b682bbf 292 last_addr = asm_lines.back ().addr;
aec2f747 293 if (last_addr < pc)
dda83cd7
SM
294 do
295 {
296 pos++;
297 if (pos >= max_lines)
298 pos = 0;
aec2f747 299
733d0a67
AB
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);
dda83cd7 304 /* If there are some problems while disassembling exit. */
733d0a67
AB
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);
aec2f747 310 pos++;
6ba8e26f 311 if (pos >= max_lines)
dda83cd7 312 pos = 0;
4cfcaf21 313 new_low = asm_lines[pos].addr;
733d0a67
AB
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);
aec2f747 319 }
6ba8e26f 320 return new_low;
aec2f747
SC
321}
322
323/* Function to set the disassembly window's content. */
61c33f10 324bool
81c82c4b 325tui_disasm_window::set_contents (struct gdbarch *arch,
9f7540a5 326 const struct symtab_and_line &sal)
c906108c 327{
d02c80cd 328 int i;
9e820dec 329 int max_lines;
aec2f747 330 CORE_ADDR cur_pc;
7806cea7 331 int tab_len = tui_tab_width;
aec2f747 332 int insn_pos;
1df2f9ef 333
9f7540a5 334 CORE_ADDR pc = sal.pc;
aec2f747 335 if (pc == 0)
61c33f10 336 return false;
aec2f747 337
432b5c40
TT
338 m_gdbarch = arch;
339 m_start_line_or_addr.loa = LOA_ADDRESS;
340 m_start_line_or_addr.u.addr = pc;
f237f998 341 cur_pc = tui_location.addr ();
aec2f747 342
0bb65f1e 343 /* Window size, excluding highlight box. */
ff3c86a8 344 max_lines = height - box_size ();
aec2f747
SC
345
346 /* Get temporary table that will hold all strings (addr & insn). */
733d0a67 347 std::vector<tui_asm_line> asm_lines;
1df2f9ef 348 size_t addr_size = 0;
432b5c40 349 tui_disassemble (m_gdbarch, asm_lines, pc, max_lines, &addr_size);
aec2f747 350
f5396833 351 /* Align instructions to the same column. */
aec2f747
SC
352 insn_pos = (1 + (addr_size / tab_len)) * tab_len;
353
1cc6d956 354 /* Now construct each line. */
432b5c40 355 m_content.resize (max_lines);
9e820dec 356 m_max_length = -1;
6ba8e26f 357 for (i = 0; i < max_lines; i++)
aec2f747 358 {
432b5c40 359 tui_source_element *src = &m_content[i];
6b915f7d 360
733d0a67
AB
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 }
aec2f747 377
1df2f9ef 378 const char *ptr = line.c_str ();
9e820dec
TT
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);
aec2f747 382
362c05fe 383 src->line_or_addr.loa = LOA_ADDRESS;
733d0a67
AB
384 src->line_or_addr.u.addr = addr;
385 src->is_exec_point = (addr == cur_pc && line.size () > 0);
aec2f747 386 }
61c33f10 387 return true;
aec2f747 388}
c906108c
SS
389
390
13274fc3
UW
391void
392tui_get_begin_asm_address (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
c906108c 393{
957b8b5a 394 struct gdbarch *gdbarch = get_current_arch ();
52469d76 395 CORE_ADDR addr = 0;
c906108c 396
f237f998 397 if (tui_location.addr () == 0)
c906108c 398 {
52469d76
TT
399 if (have_full_symbols () || have_partial_symbols ())
400 {
401 set_default_source_symtab_and_line ();
402 struct symtab_and_line sal = get_current_source_symtab_and_line ();
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)
4aeddc50 413 addr = main_symbol.value_address ();
52469d76 414 }
c906108c 415 }
1cc6d956 416 else /* The target is executing. */
13274fc3 417 {
f237f998
AB
418 gdbarch = tui_location.gdbarch ();
419 addr = tui_location.addr ();
13274fc3 420 }
c906108c 421
13274fc3
UW
422 *gdbarch_p = gdbarch;
423 *addr_p = addr;
65f05602 424}
c906108c 425
77cad3ba 426/* Determine what the low address will be to display in the TUI's
1cc6d956
MS
427 disassembly window. This may or may not be the same as the low
428 address input. */
77cad3ba 429CORE_ADDR
13274fc3
UW
430tui_get_low_disassembly_address (struct gdbarch *gdbarch,
431 CORE_ADDR low, CORE_ADDR pc)
77cad3ba
SC
432{
433 int pos;
434
1cc6d956
MS
435 /* Determine where to start the disassembly so that the pc is about
436 in the middle of the viewport. */
2a3d458b
TT
437 if (TUI_DISASM_WIN != NULL)
438 pos = TUI_DISASM_WIN->height;
6d7fd9aa
TT
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
13274fc3 445 pc = tui_find_disassembly_address (gdbarch, pc, -pos);
77cad3ba
SC
446
447 if (pc < low)
448 pc = low;
449 return pc;
450}
451
65f05602 452/* Scroll the disassembly forward or backward vertically. */
c906108c 453void
c3bd716f 454tui_disasm_window::do_scroll_vertical (int num_to_scroll)
c906108c 455{
432b5c40 456 if (!m_content.empty ())
c906108c 457 {
aec2f747 458 CORE_ADDR pc;
c906108c 459
432b5c40 460 pc = m_start_line_or_addr.u.addr;
c906108c 461
9f7540a5
TT
462 symtab_and_line sal {};
463 sal.pspace = current_program_space;
432b5c40
TT
464 sal.pc = tui_find_disassembly_address (m_gdbarch, pc, num_to_scroll);
465 update_source_window_as_is (m_gdbarch, sal);
aec2f747
SC
466 }
467}
c2cd8994
TT
468
469bool
470tui_disasm_window::location_matches_p (struct bp_location *loc, int line_no)
471{
432b5c40
TT
472 return (m_content[line_no].line_or_addr.loa == LOA_ADDRESS
473 && m_content[line_no].line_or_addr.u.addr == loc->address);
c2cd8994 474}
a54700c6 475
088f37dd
TT
476bool
477tui_disasm_window::addr_is_displayed (CORE_ADDR addr) const
478{
432b5c40 479 if (m_content.size () < SCROLL_THRESHOLD)
cbfa8581 480 return false;
088f37dd 481
432b5c40 482 for (size_t i = 0; i < m_content.size () - SCROLL_THRESHOLD; ++i)
088f37dd 483 {
432b5c40
TT
484 if (m_content[i].line_or_addr.loa == LOA_ADDRESS
485 && m_content[i].line_or_addr.u.addr == addr)
cbfa8581 486 return true;
088f37dd
TT
487 }
488
cbfa8581 489 return false;
088f37dd
TT
490}
491
a54700c6 492void
bd2b40ac 493tui_disasm_window::maybe_update (frame_info_ptr fi, symtab_and_line sal)
a54700c6
TT
494{
495 CORE_ADDR low;
496
1ae58f0c
TT
497 struct gdbarch *frame_arch = get_frame_arch (fi);
498
499 if (find_pc_partial_function (sal.pc, NULL, &low, NULL) == 0)
a54700c6
TT
500 {
501 /* There is no symbol available for current PC. There is no
502 safe way how to "disassemble backwards". */
1ae58f0c 503 low = sal.pc;
a54700c6
TT
504 }
505 else
1ae58f0c 506 low = tui_get_low_disassembly_address (frame_arch, low, sal.pc);
a54700c6
TT
507
508 struct tui_line_or_address a;
509
510 a.loa = LOA_ADDRESS;
511 a.u.addr = low;
1ae58f0c 512 if (!addr_is_displayed (sal.pc))
9f7540a5
TT
513 {
514 sal.pc = low;
515 update_source_window (frame_arch, sal);
516 }
a54700c6
TT
517 else
518 {
1ae58f0c 519 a.u.addr = sal.pc;
a54700c6
TT
520 set_is_exec_point_at (a);
521 }
522}
432b5c40
TT
523
524void
525tui_disasm_window::display_start_addr (struct gdbarch **gdbarch_p,
526 CORE_ADDR *addr_p)
527{
528 *gdbarch_p = m_gdbarch;
529 *addr_p = m_start_line_or_addr.u.addr;
530}
72535eb1
TV
531
532#if GDB_SELF_TEST
533namespace selftests {
534namespace tui {
535namespace disasm {
536
537static void
538run_tests ()
539{
540 if (current_inferior () != nullptr)
541 {
27b1f19f 542 gdbarch *gdbarch = current_inferior ()->arch ();
72535eb1
TV
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
556void _initialize_tui_disasm ();
557void
558_initialize_tui_disasm ()
559{
560#if GDB_SELF_TEST
561 selftests::register_test ("tui-disasm", selftests::tui::disasm::run_tests);
562#endif
563}