123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- /* Disassembly display.
- Copyright (C) 1998-2022 Free Software Foundation, Inc.
- Contributed by Hewlett-Packard Company.
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include "defs.h"
- #include "arch-utils.h"
- #include "symtab.h"
- #include "breakpoint.h"
- #include "frame.h"
- #include "value.h"
- #include "source.h"
- #include "disasm.h"
- #include "tui/tui.h"
- #include "tui/tui-command.h"
- #include "tui/tui-data.h"
- #include "tui/tui-win.h"
- #include "tui/tui-layout.h"
- #include "tui/tui-winsource.h"
- #include "tui/tui-stack.h"
- #include "tui/tui-file.h"
- #include "tui/tui-disasm.h"
- #include "tui/tui-source.h"
- #include "progspace.h"
- #include "objfiles.h"
- #include "cli/cli-style.h"
- #include "tui/tui-location.h"
- #include "gdb_curses.h"
- struct tui_asm_line
- {
- CORE_ADDR addr;
- std::string addr_string;
- size_t addr_size;
- std::string insn;
- };
- /* Helper function to find the number of characters in STR, skipping
- any ANSI escape sequences. */
- static size_t
- len_without_escapes (const std::string &str)
- {
- size_t len = 0;
- const char *ptr = str.c_str ();
- char c;
- while ((c = *ptr) != '\0')
- {
- if (c == '\033')
- {
- ui_file_style style;
- size_t n_read;
- if (style.parse (ptr, &n_read))
- ptr += n_read;
- else
- {
- /* Shouldn't happen, but just skip the ESC if it somehow
- does. */
- ++ptr;
- }
- }
- else
- {
- ++len;
- ++ptr;
- }
- }
- return len;
- }
- /* Function to disassemble up to COUNT instructions starting from address
- PC into the ASM_LINES vector (which will be emptied of any previous
- contents). Return the address of the COUNT'th instruction after pc.
- When ADDR_SIZE is non-null then place the maximum size of an address and
- label into the value pointed to by ADDR_SIZE, and set the addr_size
- field on each item in ASM_LINES, otherwise the addr_size fields within
- ASM_LINES are undefined.
- It is worth noting that ASM_LINES might not have COUNT entries when this
- function returns. If the disassembly is truncated for some other
- reason, for example, we hit invalid memory, then ASM_LINES can have
- fewer entries than requested. */
- 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)
- {
- bool term_out = source_styling && gdb_stdout->can_emit_style_escape ();
- string_file gdb_dis_out (term_out);
- /* Must start with an empty list. */
- asm_lines.clear ();
- /* Now construct each line. */
- for (int i = 0; i < count; ++i)
- {
- tui_asm_line tal;
- CORE_ADDR orig_pc = pc;
- try
- {
- pc = pc + gdb_print_insn (gdbarch, pc, &gdb_dis_out, NULL);
- }
- catch (const gdb_exception_error &except)
- {
- /* If PC points to an invalid address then we'll catch a
- MEMORY_ERROR here, this should stop the disassembly, but
- otherwise is fine. */
- if (except.error != MEMORY_ERROR)
- throw;
- return pc;
- }
- /* Capture the disassembled instruction. */
- tal.insn = gdb_dis_out.release ();
- /* And capture the address the instruction is at. */
- tal.addr = orig_pc;
- print_address (gdbarch, orig_pc, &gdb_dis_out);
- tal.addr_string = gdb_dis_out.release ();
- if (addr_size != nullptr)
- {
- size_t new_size;
- if (term_out)
- new_size = len_without_escapes (tal.addr_string);
- else
- new_size = tal.addr_string.size ();
- *addr_size = std::max (*addr_size, new_size);
- tal.addr_size = new_size;
- }
- asm_lines.push_back (std::move (tal));
- }
- return pc;
- }
- /* Look backward from ADDR for an address from which we can start
- disassembling, this needs to be something we can be reasonably
- confident will fall on an instruction boundary. We use msymbol
- addresses, or the start of a section. */
- static CORE_ADDR
- tui_find_backward_disassembly_start_address (CORE_ADDR addr)
- {
- struct bound_minimal_symbol msym, msym_prev;
- msym = lookup_minimal_symbol_by_pc_section (addr - 1, nullptr,
- lookup_msym_prefer::TEXT,
- &msym_prev);
- if (msym.minsym != nullptr)
- return BMSYMBOL_VALUE_ADDRESS (msym);
- else if (msym_prev.minsym != nullptr)
- return BMSYMBOL_VALUE_ADDRESS (msym_prev);
- /* Find the section that ADDR is in, and look for the start of the
- section. */
- struct obj_section *section = find_pc_section (addr);
- if (section != NULL)
- return section->addr ();
- return addr;
- }
- /* Find the disassembly address that corresponds to FROM lines above
- or below the PC. Variable sized instructions are taken into
- account by the algorithm. */
- static CORE_ADDR
- tui_find_disassembly_address (struct gdbarch *gdbarch, CORE_ADDR pc, int from)
- {
- CORE_ADDR new_low;
- int max_lines;
- max_lines = (from > 0) ? from : - from;
- if (max_lines == 0)
- return pc;
- std::vector<tui_asm_line> asm_lines;
- new_low = pc;
- if (from > 0)
- {
- /* Always disassemble 1 extra instruction here, then if the last
- instruction fails to disassemble we will take the address of the
- previous instruction that did disassemble as the result. */
- tui_disassemble (gdbarch, asm_lines, pc, max_lines + 1);
- new_low = asm_lines.back ().addr;
- }
- else
- {
- /* In order to disassemble backwards we need to find a suitable
- address to start disassembling from and then work forward until we
- re-find the address we're currently at. We can then figure out
- which address will be at the top of the TUI window after our
- backward scroll. During our backward disassemble we need to be
- able to distinguish between the case where the last address we
- _can_ disassemble is ADDR, and the case where the disassembly
- just happens to stop at ADDR, for this reason we increase
- MAX_LINES by one. */
- max_lines++;
- /* When we disassemble a series of instructions this will hold the
- address of the last instruction disassembled. */
- CORE_ADDR last_addr;
- /* And this will hold the address of the next instruction that would
- have been disassembled. */
- CORE_ADDR next_addr;
- /* As we search backward if we find an address that looks like a
- promising starting point then we record it in this structure. If
- the next address we try is not a suitable starting point then we
- will fall back to the address held here. */
- gdb::optional<CORE_ADDR> possible_new_low;
- /* The previous value of NEW_LOW so we know if the new value is
- different or not. */
- CORE_ADDR prev_low;
- do
- {
- /* Find an address from which we can start disassembling. */
- prev_low = new_low;
- new_low = tui_find_backward_disassembly_start_address (new_low);
- /* Disassemble forward. */
- next_addr = tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
- last_addr = asm_lines.back ().addr;
- /* If disassembling from the current value of NEW_LOW reached PC
- (or went past it) then this would do as a starting point if we
- can't find anything better, so remember it. */
- if (last_addr >= pc && new_low != prev_low
- && asm_lines.size () >= max_lines)
- possible_new_low.emplace (new_low);
- /* Continue searching until we find a value of NEW_LOW from which
- disassembling MAX_LINES instructions doesn't reach PC. We
- know this means we can find the required number of previous
- instructions then. */
- }
- while ((last_addr > pc
- || (last_addr == pc && asm_lines.size () < max_lines))
- && new_low != prev_low);
- /* If we failed to disassemble the required number of lines then the
- following walk forward is not going to work, it assumes that
- ASM_LINES contains exactly MAX_LINES entries. Instead we should
- consider falling back to a previous possible start address in
- POSSIBLE_NEW_LOW. */
- if (asm_lines.size () < max_lines)
- {
- if (!possible_new_low.has_value ())
- return new_low;
- /* Take the best possible match we have. */
- new_low = *possible_new_low;
- next_addr = tui_disassemble (gdbarch, asm_lines, new_low, max_lines);
- last_addr = asm_lines.back ().addr;
- gdb_assert (asm_lines.size () >= max_lines);
- }
- /* Scan forward disassembling one instruction at a time until
- the last visible instruction of the window matches the pc.
- We keep the disassembled instructions in the 'lines' window
- and shift it downward (increasing its addresses). */
- int pos = max_lines - 1;
- if (last_addr < pc)
- do
- {
- pos++;
- if (pos >= max_lines)
- pos = 0;
- CORE_ADDR old_next_addr = next_addr;
- std::vector<tui_asm_line> single_asm_line;
- next_addr = tui_disassemble (gdbarch, single_asm_line,
- next_addr, 1);
- /* If there are some problems while disassembling exit. */
- if (next_addr <= old_next_addr)
- return pc;
- gdb_assert (single_asm_line.size () == 1);
- asm_lines[pos] = single_asm_line[0];
- } while (next_addr <= pc);
- pos++;
- if (pos >= max_lines)
- pos = 0;
- new_low = asm_lines[pos].addr;
- /* When scrolling backward the addresses should move backward, or at
- the very least stay the same if we are at the first address that
- can be disassembled. */
- gdb_assert (new_low <= pc);
- }
- return new_low;
- }
- /* Function to set the disassembly window's content. */
- bool
- tui_disasm_window::set_contents (struct gdbarch *arch,
- const struct symtab_and_line &sal)
- {
- int i;
- int max_lines;
- CORE_ADDR cur_pc;
- int tab_len = tui_tab_width;
- int insn_pos;
- CORE_ADDR pc = sal.pc;
- if (pc == 0)
- return false;
- m_gdbarch = arch;
- m_start_line_or_addr.loa = LOA_ADDRESS;
- m_start_line_or_addr.u.addr = pc;
- cur_pc = tui_location.addr ();
- /* Window size, excluding highlight box. */
- max_lines = height - 2;
- /* Get temporary table that will hold all strings (addr & insn). */
- std::vector<tui_asm_line> asm_lines;
- size_t addr_size = 0;
- tui_disassemble (m_gdbarch, asm_lines, pc, max_lines, &addr_size);
- /* Align instructions to the same column. */
- insn_pos = (1 + (addr_size / tab_len)) * tab_len;
- /* Now construct each line. */
- m_content.resize (max_lines);
- m_max_length = -1;
- for (i = 0; i < max_lines; i++)
- {
- tui_source_element *src = &m_content[i];
- std::string line;
- CORE_ADDR addr;
- if (i < asm_lines.size ())
- {
- line
- = (asm_lines[i].addr_string
- + n_spaces (insn_pos - asm_lines[i].addr_size)
- + asm_lines[i].insn);
- addr = asm_lines[i].addr;
- }
- else
- {
- line = "";
- addr = 0;
- }
- const char *ptr = line.c_str ();
- int line_len;
- src->line = tui_copy_source_line (&ptr, &line_len);
- m_max_length = std::max (m_max_length, line_len);
- src->line_or_addr.loa = LOA_ADDRESS;
- src->line_or_addr.u.addr = addr;
- src->is_exec_point = (addr == cur_pc && line.size () > 0);
- }
- return true;
- }
- void
- tui_get_begin_asm_address (struct gdbarch **gdbarch_p, CORE_ADDR *addr_p)
- {
- struct gdbarch *gdbarch = get_current_arch ();
- CORE_ADDR addr = 0;
- if (tui_location.addr () == 0)
- {
- if (have_full_symbols () || have_partial_symbols ())
- {
- set_default_source_symtab_and_line ();
- struct symtab_and_line sal = get_current_source_symtab_and_line ();
- if (sal.symtab != nullptr)
- find_line_pc (sal.symtab, sal.line, &addr);
- }
- if (addr == 0)
- {
- struct bound_minimal_symbol main_symbol
- = lookup_minimal_symbol (main_name (), nullptr, nullptr);
- if (main_symbol.minsym != nullptr)
- addr = BMSYMBOL_VALUE_ADDRESS (main_symbol);
- }
- }
- else /* The target is executing. */
- {
- gdbarch = tui_location.gdbarch ();
- addr = tui_location.addr ();
- }
- *gdbarch_p = gdbarch;
- *addr_p = addr;
- }
- /* Determine what the low address will be to display in the TUI's
- disassembly window. This may or may not be the same as the low
- address input. */
- CORE_ADDR
- tui_get_low_disassembly_address (struct gdbarch *gdbarch,
- CORE_ADDR low, CORE_ADDR pc)
- {
- int pos;
- /* Determine where to start the disassembly so that the pc is about
- in the middle of the viewport. */
- if (TUI_DISASM_WIN != NULL)
- pos = TUI_DISASM_WIN->height;
- else if (TUI_CMD_WIN == NULL)
- pos = tui_term_height () / 2 - 2;
- else
- pos = tui_term_height () - TUI_CMD_WIN->height - 2;
- pos = (pos - 2) / 2;
- pc = tui_find_disassembly_address (gdbarch, pc, -pos);
- if (pc < low)
- pc = low;
- return pc;
- }
- /* Scroll the disassembly forward or backward vertically. */
- void
- tui_disasm_window::do_scroll_vertical (int num_to_scroll)
- {
- if (!m_content.empty ())
- {
- CORE_ADDR pc;
- pc = m_start_line_or_addr.u.addr;
- symtab_and_line sal {};
- sal.pspace = current_program_space;
- sal.pc = tui_find_disassembly_address (m_gdbarch, pc, num_to_scroll);
- update_source_window_as_is (m_gdbarch, sal);
- }
- }
- bool
- tui_disasm_window::location_matches_p (struct bp_location *loc, int line_no)
- {
- return (m_content[line_no].line_or_addr.loa == LOA_ADDRESS
- && m_content[line_no].line_or_addr.u.addr == loc->address);
- }
- bool
- tui_disasm_window::addr_is_displayed (CORE_ADDR addr) const
- {
- if (m_content.size () < SCROLL_THRESHOLD)
- return false;
- for (size_t i = 0; i < m_content.size () - SCROLL_THRESHOLD; ++i)
- {
- if (m_content[i].line_or_addr.loa == LOA_ADDRESS
- && m_content[i].line_or_addr.u.addr == addr)
- return true;
- }
- return false;
- }
- void
- tui_disasm_window::maybe_update (struct frame_info *fi, symtab_and_line sal)
- {
- CORE_ADDR low;
- struct gdbarch *frame_arch = get_frame_arch (fi);
- if (find_pc_partial_function (sal.pc, NULL, &low, NULL) == 0)
- {
- /* There is no symbol available for current PC. There is no
- safe way how to "disassemble backwards". */
- low = sal.pc;
- }
- else
- low = tui_get_low_disassembly_address (frame_arch, low, sal.pc);
- struct tui_line_or_address a;
- a.loa = LOA_ADDRESS;
- a.u.addr = low;
- if (!addr_is_displayed (sal.pc))
- {
- sal.pc = low;
- update_source_window (frame_arch, sal);
- }
- else
- {
- a.u.addr = sal.pc;
- set_is_exec_point_at (a);
- }
- }
- void
- tui_disasm_window::display_start_addr (struct gdbarch **gdbarch_p,
- CORE_ADDR *addr_p)
- {
- *gdbarch_p = m_gdbarch;
- *addr_p = m_start_line_or_addr.u.addr;
- }
|