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