12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280 |
- /* Disassemble support for GDB.
- Copyright (C) 2000-2022 Free Software Foundation, Inc.
- 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 "target.h"
- #include "value.h"
- #include "ui-out.h"
- #include "disasm.h"
- #include "gdbcore.h"
- #include "gdbcmd.h"
- #include "dis-asm.h"
- #include "source.h"
- #include "safe-ctype.h"
- #include <algorithm>
- #include "gdbsupport/gdb_optional.h"
- #include "valprint.h"
- #include "cli/cli-style.h"
- /* Disassemble functions.
- FIXME: We should get rid of all the duplicate code in gdb that does
- the same thing: disassemble_command() and the gdbtk variation. */
- /* This variable is used to hold the prospective disassembler_options value
- which is set by the "set disassembler_options" command. */
- static std::string prospective_options;
- /* This structure is used to store line number information for the
- deprecated /m option.
- We need a different sort of line table from the normal one cuz we can't
- depend upon implicit line-end pc's for lines to do the
- reordering in this function. */
- struct deprecated_dis_line_entry
- {
- int line;
- CORE_ADDR start_pc;
- CORE_ADDR end_pc;
- };
- /* This Structure is used to store line number information.
- We need a different sort of line table from the normal one cuz we can't
- depend upon implicit line-end pc's for lines to do the
- reordering in this function. */
- struct dis_line_entry
- {
- struct symtab *symtab;
- int line;
- };
- /* Hash function for dis_line_entry. */
- static hashval_t
- hash_dis_line_entry (const void *item)
- {
- const struct dis_line_entry *dle = (const struct dis_line_entry *) item;
- return htab_hash_pointer (dle->symtab) + dle->line;
- }
- /* Equal function for dis_line_entry. */
- static int
- eq_dis_line_entry (const void *item_lhs, const void *item_rhs)
- {
- const struct dis_line_entry *lhs = (const struct dis_line_entry *) item_lhs;
- const struct dis_line_entry *rhs = (const struct dis_line_entry *) item_rhs;
- return (lhs->symtab == rhs->symtab
- && lhs->line == rhs->line);
- }
- /* Create the table to manage lines for mixed source/disassembly. */
- static htab_t
- allocate_dis_line_table (void)
- {
- return htab_create_alloc (41,
- hash_dis_line_entry, eq_dis_line_entry,
- xfree, xcalloc, xfree);
- }
- /* Add a new dis_line_entry containing SYMTAB and LINE to TABLE. */
- static void
- add_dis_line_entry (htab_t table, struct symtab *symtab, int line)
- {
- void **slot;
- struct dis_line_entry dle, *dlep;
- dle.symtab = symtab;
- dle.line = line;
- slot = htab_find_slot (table, &dle, INSERT);
- if (*slot == NULL)
- {
- dlep = XNEW (struct dis_line_entry);
- dlep->symtab = symtab;
- dlep->line = line;
- *slot = dlep;
- }
- }
- /* Return non-zero if SYMTAB, LINE are in TABLE. */
- static int
- line_has_code_p (htab_t table, struct symtab *symtab, int line)
- {
- struct dis_line_entry dle;
- dle.symtab = symtab;
- dle.line = line;
- return htab_find (table, &dle) != NULL;
- }
- /* Wrapper of target_read_code. */
- int
- gdb_disassembler::dis_asm_read_memory (bfd_vma memaddr, gdb_byte *myaddr,
- unsigned int len,
- struct disassemble_info *info)
- {
- return target_read_code (memaddr, myaddr, len);
- }
- /* Wrapper of memory_error. */
- void
- gdb_disassembler::dis_asm_memory_error (int err, bfd_vma memaddr,
- struct disassemble_info *info)
- {
- gdb_disassembler *self
- = static_cast<gdb_disassembler *>(info->application_data);
- self->m_err_memaddr.emplace (memaddr);
- }
- /* Wrapper of print_address. */
- void
- gdb_disassembler::dis_asm_print_address (bfd_vma addr,
- struct disassemble_info *info)
- {
- gdb_disassembler *self
- = static_cast<gdb_disassembler *>(info->application_data);
- print_address (self->arch (), addr, self->stream ());
- }
- /* Format disassembler output to STREAM. */
- int
- gdb_disassembler::dis_asm_fprintf (void *stream, const char *format, ...)
- {
- va_list args;
- va_start (args, format);
- gdb_vprintf ((struct ui_file *) stream, format, args);
- va_end (args);
- /* Something non -ve. */
- return 0;
- }
- /* See disasm.h. */
- int
- gdb_disassembler::dis_asm_styled_fprintf (void *stream,
- enum disassembler_style style,
- const char *format, ...)
- {
- va_list args;
- va_start (args, format);
- gdb_vprintf ((struct ui_file *) stream, format, args);
- va_end (args);
- /* Something non -ve. */
- return 0;
- }
- static bool
- line_is_less_than (const deprecated_dis_line_entry &mle1,
- const deprecated_dis_line_entry &mle2)
- {
- bool val;
- /* End of sequence markers have a line number of 0 but don't want to
- be sorted to the head of the list, instead sort by PC. */
- if (mle1.line == 0 || mle2.line == 0)
- {
- if (mle1.start_pc != mle2.start_pc)
- val = mle1.start_pc < mle2.start_pc;
- else
- val = mle1.line < mle2.line;
- }
- else
- {
- if (mle1.line != mle2.line)
- val = mle1.line < mle2.line;
- else
- val = mle1.start_pc < mle2.start_pc;
- }
- return val;
- }
- /* See disasm.h. */
- int
- gdb_pretty_print_disassembler::pretty_print_insn (const struct disasm_insn *insn,
- gdb_disassembly_flags flags)
- {
- /* parts of the symbolic representation of the address */
- int unmapped;
- int offset;
- int line;
- int size;
- CORE_ADDR pc;
- struct gdbarch *gdbarch = arch ();
- {
- ui_out_emit_tuple tuple_emitter (m_uiout, NULL);
- pc = insn->addr;
- if (insn->number != 0)
- {
- m_uiout->field_unsigned ("insn-number", insn->number);
- m_uiout->text ("\t");
- }
- if ((flags & DISASSEMBLY_SPECULATIVE) != 0)
- {
- if (insn->is_speculative)
- {
- m_uiout->field_string ("is-speculative", "?");
- /* The speculative execution indication overwrites the first
- character of the PC prefix.
- We assume a PC prefix length of 3 characters. */
- if ((flags & DISASSEMBLY_OMIT_PC) == 0)
- m_uiout->text (pc_prefix (pc) + 1);
- else
- m_uiout->text (" ");
- }
- else if ((flags & DISASSEMBLY_OMIT_PC) == 0)
- m_uiout->text (pc_prefix (pc));
- else
- m_uiout->text (" ");
- }
- else if ((flags & DISASSEMBLY_OMIT_PC) == 0)
- m_uiout->text (pc_prefix (pc));
- m_uiout->field_core_addr ("address", gdbarch, pc);
- std::string name, filename;
- bool omit_fname = ((flags & DISASSEMBLY_OMIT_FNAME) != 0);
- if (!build_address_symbolic (gdbarch, pc, false, omit_fname, &name,
- &offset, &filename, &line, &unmapped))
- {
- /* We don't care now about line, filename and unmapped. But we might in
- the future. */
- m_uiout->text (" <");
- if (!omit_fname)
- m_uiout->field_string ("func-name", name,
- function_name_style.style ());
- /* For negative offsets, avoid displaying them as +-N; the sign of
- the offset takes the place of the "+" here. */
- if (offset >= 0)
- m_uiout->text ("+");
- m_uiout->field_signed ("offset", offset);
- m_uiout->text (">:\t");
- }
- else
- m_uiout->text (":\t");
- /* Clear the buffer into which we will disassemble the instruction. */
- m_insn_stb.clear ();
- /* A helper function to write the M_INSN_STB buffer, followed by a
- newline. This can be called in a couple of situations. */
- auto write_out_insn_buffer = [&] ()
- {
- m_uiout->field_stream ("inst", m_insn_stb);
- m_uiout->text ("\n");
- };
- try
- {
- /* Now we can disassemble the instruction. If the disassembler
- returns a negative value this indicates an error and is handled
- within the print_insn call, resulting in an exception being
- thrown. Returning zero makes no sense, as this indicates we
- disassembled something successfully, but it was something of no
- size? */
- size = m_di.print_insn (pc);
- gdb_assert (size > 0);
- }
- catch (const gdb_exception &ex)
- {
- /* An exception was thrown while disassembling the instruction.
- However, the disassembler might still have written something
- out, so ensure that we flush the instruction buffer before
- rethrowing the exception. We can't perform this write from an
- object destructor as the write itself might throw an exception
- if the pager kicks in, and the user selects quit. */
- write_out_insn_buffer ();
- throw ex;
- }
- if (flags & DISASSEMBLY_RAW_INSN)
- {
- CORE_ADDR end_pc;
- bfd_byte data;
- const char *spacer = "";
- /* Build the opcodes using a temporary stream so we can
- write them out in a single go for the MI. */
- m_opcode_stb.clear ();
- end_pc = pc + size;
- for (;pc < end_pc; ++pc)
- {
- read_code (pc, &data, 1);
- m_opcode_stb.printf ("%s%02x", spacer, (unsigned) data);
- spacer = " ";
- }
- m_uiout->field_stream ("opcodes", m_opcode_stb);
- m_uiout->text ("\t");
- }
- /* Disassembly was a success, write out the instruction buffer. */
- write_out_insn_buffer ();
- }
- return size;
- }
- static int
- dump_insns (struct gdbarch *gdbarch,
- struct ui_out *uiout, CORE_ADDR low, CORE_ADDR high,
- int how_many, gdb_disassembly_flags flags, CORE_ADDR *end_pc)
- {
- struct disasm_insn insn;
- int num_displayed = 0;
- memset (&insn, 0, sizeof (insn));
- insn.addr = low;
- gdb_pretty_print_disassembler disasm (gdbarch, uiout);
- while (insn.addr < high && (how_many < 0 || num_displayed < how_many))
- {
- int size;
- size = disasm.pretty_print_insn (&insn, flags);
- if (size <= 0)
- break;
- ++num_displayed;
- insn.addr += size;
- /* Allow user to bail out with ^C. */
- QUIT;
- }
- if (end_pc != NULL)
- *end_pc = insn.addr;
- return num_displayed;
- }
- /* The idea here is to present a source-O-centric view of a
- function to the user. This means that things are presented
- in source order, with (possibly) out of order assembly
- immediately following.
- N.B. This view is deprecated. */
- static void
- do_mixed_source_and_assembly_deprecated
- (struct gdbarch *gdbarch, struct ui_out *uiout,
- struct symtab *symtab,
- CORE_ADDR low, CORE_ADDR high,
- int how_many, gdb_disassembly_flags flags)
- {
- int newlines = 0;
- int nlines;
- struct linetable_entry *le;
- struct deprecated_dis_line_entry *mle;
- struct symtab_and_line sal;
- int i;
- int out_of_order = 0;
- int next_line = 0;
- int num_displayed = 0;
- print_source_lines_flags psl_flags = 0;
- gdb_assert (symtab != nullptr && symtab->linetable () != nullptr);
- nlines = symtab->linetable ()->nitems;
- le = symtab->linetable ()->item;
- if (flags & DISASSEMBLY_FILENAME)
- psl_flags |= PRINT_SOURCE_LINES_FILENAME;
- mle = (struct deprecated_dis_line_entry *)
- alloca (nlines * sizeof (struct deprecated_dis_line_entry));
- /* Copy linetable entries for this function into our data
- structure, creating end_pc's and setting out_of_order as
- appropriate. */
- /* First, skip all the preceding functions. */
- for (i = 0; i < nlines - 1 && le[i].pc < low; i++);
- /* Now, copy all entries before the end of this function. */
- for (; i < nlines - 1 && le[i].pc < high; i++)
- {
- if (le[i].line == le[i + 1].line && le[i].pc == le[i + 1].pc)
- continue; /* Ignore duplicates. */
- /* Skip any end-of-function markers. */
- if (le[i].line == 0)
- continue;
- mle[newlines].line = le[i].line;
- if (le[i].line > le[i + 1].line)
- out_of_order = 1;
- mle[newlines].start_pc = le[i].pc;
- mle[newlines].end_pc = le[i + 1].pc;
- newlines++;
- }
- /* If we're on the last line, and it's part of the function,
- then we need to get the end pc in a special way. */
- if (i == nlines - 1 && le[i].pc < high)
- {
- mle[newlines].line = le[i].line;
- mle[newlines].start_pc = le[i].pc;
- sal = find_pc_line (le[i].pc, 0);
- mle[newlines].end_pc = sal.end;
- newlines++;
- }
- /* Now, sort mle by line #s (and, then by addresses within lines). */
- if (out_of_order)
- std::sort (mle, mle + newlines, line_is_less_than);
- /* Now, for each line entry, emit the specified lines (unless
- they have been emitted before), followed by the assembly code
- for that line. */
- ui_out_emit_list asm_insns_list (uiout, "asm_insns");
- gdb::optional<ui_out_emit_tuple> outer_tuple_emitter;
- gdb::optional<ui_out_emit_list> inner_list_emitter;
- for (i = 0; i < newlines; i++)
- {
- /* Print out everything from next_line to the current line. */
- if (mle[i].line >= next_line)
- {
- if (next_line != 0)
- {
- /* Just one line to print. */
- if (next_line == mle[i].line)
- {
- outer_tuple_emitter.emplace (uiout, "src_and_asm_line");
- print_source_lines (symtab, next_line, mle[i].line + 1, psl_flags);
- }
- else
- {
- /* Several source lines w/o asm instructions associated. */
- for (; next_line < mle[i].line; next_line++)
- {
- ui_out_emit_tuple tuple_emitter (uiout,
- "src_and_asm_line");
- print_source_lines (symtab, next_line, next_line + 1,
- psl_flags);
- ui_out_emit_list temp_list_emitter (uiout,
- "line_asm_insn");
- }
- /* Print the last line and leave list open for
- asm instructions to be added. */
- outer_tuple_emitter.emplace (uiout, "src_and_asm_line");
- print_source_lines (symtab, next_line, mle[i].line + 1, psl_flags);
- }
- }
- else
- {
- outer_tuple_emitter.emplace (uiout, "src_and_asm_line");
- print_source_lines (symtab, mle[i].line, mle[i].line + 1, psl_flags);
- }
- next_line = mle[i].line + 1;
- inner_list_emitter.emplace (uiout, "line_asm_insn");
- }
- num_displayed += dump_insns (gdbarch, uiout,
- mle[i].start_pc, mle[i].end_pc,
- how_many, flags, NULL);
- /* When we've reached the end of the mle array, or we've seen the last
- assembly range for this source line, close out the list/tuple. */
- if (i == (newlines - 1) || mle[i + 1].line > mle[i].line)
- {
- inner_list_emitter.reset ();
- outer_tuple_emitter.reset ();
- uiout->text ("\n");
- }
- if (how_many >= 0 && num_displayed >= how_many)
- break;
- }
- }
- /* The idea here is to present a source-O-centric view of a
- function to the user. This means that things are presented
- in source order, with (possibly) out of order assembly
- immediately following. */
- static void
- do_mixed_source_and_assembly (struct gdbarch *gdbarch,
- struct ui_out *uiout,
- struct symtab *main_symtab,
- CORE_ADDR low, CORE_ADDR high,
- int how_many, gdb_disassembly_flags flags)
- {
- const struct linetable_entry *le, *first_le;
- int i, nlines;
- int num_displayed = 0;
- print_source_lines_flags psl_flags = 0;
- CORE_ADDR pc;
- struct symtab *last_symtab;
- int last_line;
- gdb_assert (main_symtab != NULL && main_symtab->linetable () != NULL);
- /* First pass: collect the list of all source files and lines.
- We do this so that we can only print lines containing code once.
- We try to print the source text leading up to the next instruction,
- but if that text is for code that will be disassembled later, then
- we'll want to defer printing it until later with its associated code. */
- htab_up dis_line_table (allocate_dis_line_table ());
- pc = low;
- /* The prologue may be empty, but there may still be a line number entry
- for the opening brace which is distinct from the first line of code.
- If the prologue has been eliminated find_pc_line may return the source
- line after the opening brace. We still want to print this opening brace.
- first_le is used to implement this. */
- nlines = main_symtab->linetable ()->nitems;
- le = main_symtab->linetable ()->item;
- first_le = NULL;
- /* Skip all the preceding functions. */
- for (i = 0; i < nlines && le[i].pc < low; i++)
- continue;
- if (i < nlines && le[i].pc < high)
- first_le = &le[i];
- /* Add lines for every pc value. */
- while (pc < high)
- {
- struct symtab_and_line sal;
- int length;
- sal = find_pc_line (pc, 0);
- length = gdb_insn_length (gdbarch, pc);
- pc += length;
- if (sal.symtab != NULL)
- add_dis_line_entry (dis_line_table.get (), sal.symtab, sal.line);
- }
- /* Second pass: print the disassembly.
- Output format, from an MI perspective:
- The result is a ui_out list, field name "asm_insns", where elements have
- name "src_and_asm_line".
- Each element is a tuple of source line specs (field names line, file,
- fullname), and field "line_asm_insn" which contains the disassembly.
- Field "line_asm_insn" is a list of tuples: address, func-name, offset,
- opcodes, inst.
- CLI output works on top of this because MI ignores ui_out_text output,
- which is where we put file name and source line contents output.
- Emitter usage:
- asm_insns_emitter
- Handles the outer "asm_insns" list.
- tuple_emitter
- The tuples for each group of consecutive disassemblies.
- list_emitter
- List of consecutive source lines or disassembled insns. */
- if (flags & DISASSEMBLY_FILENAME)
- psl_flags |= PRINT_SOURCE_LINES_FILENAME;
- ui_out_emit_list asm_insns_emitter (uiout, "asm_insns");
- gdb::optional<ui_out_emit_tuple> tuple_emitter;
- gdb::optional<ui_out_emit_list> list_emitter;
- last_symtab = NULL;
- last_line = 0;
- pc = low;
- while (pc < high)
- {
- struct symtab_and_line sal;
- CORE_ADDR end_pc;
- int start_preceding_line_to_display = 0;
- int end_preceding_line_to_display = 0;
- int new_source_line = 0;
- sal = find_pc_line (pc, 0);
- if (sal.symtab != last_symtab)
- {
- /* New source file. */
- new_source_line = 1;
- /* If this is the first line of output, check for any preceding
- lines. */
- if (last_line == 0
- && first_le != NULL
- && first_le->line < sal.line)
- {
- start_preceding_line_to_display = first_le->line;
- end_preceding_line_to_display = sal.line;
- }
- }
- else
- {
- /* Same source file as last time. */
- if (sal.symtab != NULL)
- {
- if (sal.line > last_line + 1 && last_line != 0)
- {
- int l;
- /* Several preceding source lines. Print the trailing ones
- not associated with code that we'll print later. */
- for (l = sal.line - 1; l > last_line; --l)
- {
- if (line_has_code_p (dis_line_table.get (),
- sal.symtab, l))
- break;
- }
- if (l < sal.line - 1)
- {
- start_preceding_line_to_display = l + 1;
- end_preceding_line_to_display = sal.line;
- }
- }
- if (sal.line != last_line)
- new_source_line = 1;
- else
- {
- /* Same source line as last time. This can happen, depending
- on the debug info. */
- }
- }
- }
- if (new_source_line)
- {
- /* Skip the newline if this is the first instruction. */
- if (pc > low)
- uiout->text ("\n");
- if (tuple_emitter.has_value ())
- {
- gdb_assert (list_emitter.has_value ());
- list_emitter.reset ();
- tuple_emitter.reset ();
- }
- if (sal.symtab != last_symtab
- && !(flags & DISASSEMBLY_FILENAME))
- {
- /* Remember MI ignores ui_out_text.
- We don't have to do anything here for MI because MI
- output includes the source specs for each line. */
- if (sal.symtab != NULL)
- {
- uiout->text (symtab_to_filename_for_display (sal.symtab));
- }
- else
- uiout->text ("unknown");
- uiout->text (":\n");
- }
- if (start_preceding_line_to_display > 0)
- {
- /* Several source lines w/o asm instructions associated.
- We need to preserve the structure of the output, so output
- a bunch of line tuples with no asm entries. */
- int l;
- gdb_assert (sal.symtab != NULL);
- for (l = start_preceding_line_to_display;
- l < end_preceding_line_to_display;
- ++l)
- {
- ui_out_emit_tuple line_tuple_emitter (uiout,
- "src_and_asm_line");
- print_source_lines (sal.symtab, l, l + 1, psl_flags);
- ui_out_emit_list chain_line_emitter (uiout, "line_asm_insn");
- }
- }
- tuple_emitter.emplace (uiout, "src_and_asm_line");
- if (sal.symtab != NULL)
- print_source_lines (sal.symtab, sal.line, sal.line + 1, psl_flags);
- else
- uiout->text (_("--- no source info for this pc ---\n"));
- list_emitter.emplace (uiout, "line_asm_insn");
- }
- else
- {
- /* Here we're appending instructions to an existing line.
- By construction the very first insn will have a symtab
- and follow the new_source_line path above. */
- gdb_assert (tuple_emitter.has_value ());
- gdb_assert (list_emitter.has_value ());
- }
- if (sal.end != 0)
- end_pc = std::min (sal.end, high);
- else
- end_pc = pc + 1;
- num_displayed += dump_insns (gdbarch, uiout, pc, end_pc,
- how_many, flags, &end_pc);
- pc = end_pc;
- if (how_many >= 0 && num_displayed >= how_many)
- break;
- last_symtab = sal.symtab;
- last_line = sal.line;
- }
- }
- static void
- do_assembly_only (struct gdbarch *gdbarch, struct ui_out *uiout,
- CORE_ADDR low, CORE_ADDR high,
- int how_many, gdb_disassembly_flags flags)
- {
- ui_out_emit_list list_emitter (uiout, "asm_insns");
- dump_insns (gdbarch, uiout, low, high, how_many, flags, NULL);
- }
- /* Combine implicit and user disassembler options and return them
- in a newly-created string. */
- static std::string
- get_all_disassembler_options (struct gdbarch *gdbarch)
- {
- const char *implicit = gdbarch_disassembler_options_implicit (gdbarch);
- const char *options = get_disassembler_options (gdbarch);
- const char *comma = ",";
- if (implicit == nullptr)
- {
- implicit = "";
- comma = "";
- }
- if (options == nullptr)
- {
- options = "";
- comma = "";
- }
- return string_printf ("%s%s%s", implicit, comma, options);
- }
- gdb_disassembler::gdb_disassembler (struct gdbarch *gdbarch,
- struct ui_file *file,
- di_read_memory_ftype read_memory_func)
- : m_gdbarch (gdbarch),
- m_buffer (!use_ext_lang_colorization_p && disassembler_styling
- && file->can_emit_style_escape ()),
- m_dest (file)
- {
- init_disassemble_info (&m_di, &m_buffer, dis_asm_fprintf,
- dis_asm_styled_fprintf);
- m_di.flavour = bfd_target_unknown_flavour;
- m_di.memory_error_func = dis_asm_memory_error;
- m_di.print_address_func = dis_asm_print_address;
- /* NOTE: cagney/2003-04-28: The original code, from the old Insight
- disassembler had a local optimization here. By default it would
- access the executable file, instead of the target memory (there
- was a growing list of exceptions though). Unfortunately, the
- heuristic was flawed. Commands like "disassemble &variable"
- didn't work as they relied on the access going to the target.
- Further, it has been superseeded by trust-read-only-sections
- (although that should be superseeded by target_trust..._p()). */
- m_di.read_memory_func = read_memory_func;
- m_di.arch = gdbarch_bfd_arch_info (gdbarch)->arch;
- m_di.mach = gdbarch_bfd_arch_info (gdbarch)->mach;
- m_di.endian = gdbarch_byte_order (gdbarch);
- m_di.endian_code = gdbarch_byte_order_for_code (gdbarch);
- m_di.application_data = this;
- m_disassembler_options_holder = get_all_disassembler_options (gdbarch);
- if (!m_disassembler_options_holder.empty ())
- m_di.disassembler_options = m_disassembler_options_holder.c_str ();
- disassemble_init_for_target (&m_di);
- }
- gdb_disassembler::~gdb_disassembler ()
- {
- disassemble_free_target (&m_di);
- }
- /* See disasm.h. */
- bool gdb_disassembler::use_ext_lang_colorization_p = true;
- /* See disasm.h. */
- int
- gdb_disassembler::print_insn (CORE_ADDR memaddr,
- int *branch_delay_insns)
- {
- m_err_memaddr.reset ();
- m_buffer.clear ();
- int length = gdbarch_print_insn (arch (), memaddr, &m_di);
- /* If we have successfully disassembled an instruction, styling is on, we
- think that the extension language might be able to perform styling for
- us, and the destination can support styling, then lets call into the
- extension languages in order to style this output. */
- if (length > 0 && disassembler_styling
- && use_ext_lang_colorization_p
- && m_dest->can_emit_style_escape ())
- {
- gdb::optional<std::string> ext_contents;
- ext_contents = ext_lang_colorize_disasm (m_buffer.string (), arch ());
- if (ext_contents.has_value ())
- m_buffer = std::move (*ext_contents);
- else
- {
- /* The extension language failed to add styling to the
- disassembly output. Set the static flag so that next time we
- disassemble we don't even bother attempting to use the
- extension language for styling. */
- use_ext_lang_colorization_p = false;
- /* The instruction we just disassembled, and the extension
- languages failed to style, might have otherwise had some
- minimal styling applied by GDB. To regain that styling we
- need to recreate m_buffer, but this time with styling support.
- To do this we perform an in-place new, but this time turn on
- the styling support, then we can re-disassembly the
- instruction, and gain any minimal styling GDB might add. */
- gdb_static_assert ((std::is_same<decltype (m_buffer),
- string_file>::value));
- gdb_assert (!m_buffer.term_out ());
- m_buffer.~string_file ();
- new (&m_buffer) string_file (true);
- length = gdbarch_print_insn (arch (), memaddr, &m_di);
- gdb_assert (length > 0);
- }
- }
- /* Push any disassemble output to the real destination stream. We do
- this even if the disassembler reported failure (-1) as the
- disassembler may have printed something to its output stream. */
- m_di.fprintf_func (m_dest, "%s", m_buffer.c_str ());
- /* If the disassembler failed then report an appropriate error. */
- if (length < 0)
- {
- if (m_err_memaddr.has_value ())
- memory_error (TARGET_XFER_E_IO, *m_err_memaddr);
- else
- error (_("unknown disassembler error (error = %d)"), length);
- }
- if (branch_delay_insns != NULL)
- {
- if (m_di.insn_info_valid)
- *branch_delay_insns = m_di.branch_delay_insns;
- else
- *branch_delay_insns = 0;
- }
- return length;
- }
- void
- gdb_disassembly (struct gdbarch *gdbarch, struct ui_out *uiout,
- gdb_disassembly_flags flags, int how_many,
- CORE_ADDR low, CORE_ADDR high)
- {
- struct symtab *symtab;
- int nlines = -1;
- /* Assume symtab is valid for whole PC range. */
- symtab = find_pc_line_symtab (low);
- if (symtab != NULL && symtab->linetable () != NULL)
- nlines = symtab->linetable ()->nitems;
- if (!(flags & (DISASSEMBLY_SOURCE_DEPRECATED | DISASSEMBLY_SOURCE))
- || nlines <= 0)
- do_assembly_only (gdbarch, uiout, low, high, how_many, flags);
- else if (flags & DISASSEMBLY_SOURCE)
- do_mixed_source_and_assembly (gdbarch, uiout, symtab, low, high,
- how_many, flags);
- else if (flags & DISASSEMBLY_SOURCE_DEPRECATED)
- do_mixed_source_and_assembly_deprecated (gdbarch, uiout, symtab,
- low, high, how_many, flags);
- gdb_flush (gdb_stdout);
- }
- /* Print the instruction at address MEMADDR in debugged memory,
- on STREAM. Returns the length of the instruction, in bytes,
- and, if requested, the number of branch delay slot instructions. */
- int
- gdb_print_insn (struct gdbarch *gdbarch, CORE_ADDR memaddr,
- struct ui_file *stream, int *branch_delay_insns)
- {
- gdb_disassembler di (gdbarch, stream);
- return di.print_insn (memaddr, branch_delay_insns);
- }
- /* Return the length in bytes of the instruction at address MEMADDR in
- debugged memory. */
- int
- gdb_insn_length (struct gdbarch *gdbarch, CORE_ADDR addr)
- {
- return gdb_print_insn (gdbarch, addr, &null_stream, NULL);
- }
- /* An fprintf-function for use by the disassembler when we know we don't
- want to print anything. Always returns success. */
- static int ATTRIBUTE_PRINTF (2, 3)
- gdb_disasm_null_printf (void *stream, const char *format, ...)
- {
- return 0;
- }
- /* An fprintf-function for use by the disassembler when we know we don't
- want to print anything, and the disassembler is using style. Always
- returns success. */
- static int ATTRIBUTE_PRINTF (3, 4)
- gdb_disasm_null_styled_printf (void *stream,
- enum disassembler_style style,
- const char *format, ...)
- {
- return 0;
- }
- /* See disasm.h. */
- void
- init_disassemble_info_for_no_printing (struct disassemble_info *dinfo)
- {
- init_disassemble_info (dinfo, nullptr, gdb_disasm_null_printf,
- gdb_disasm_null_styled_printf);
- }
- /* Initialize a struct disassemble_info for gdb_buffered_insn_length.
- Upon return, *DISASSEMBLER_OPTIONS_HOLDER owns the string pointed
- to by DI.DISASSEMBLER_OPTIONS. */
- static void
- gdb_buffered_insn_length_init_dis (struct gdbarch *gdbarch,
- struct disassemble_info *di,
- const gdb_byte *insn, int max_len,
- CORE_ADDR addr,
- std::string *disassembler_options_holder)
- {
- init_disassemble_info_for_no_printing (di);
- /* init_disassemble_info installs buffer_read_memory, etc.
- so we don't need to do that here.
- The cast is necessary until disassemble_info is const-ified. */
- di->buffer = (gdb_byte *) insn;
- di->buffer_length = max_len;
- di->buffer_vma = addr;
- di->arch = gdbarch_bfd_arch_info (gdbarch)->arch;
- di->mach = gdbarch_bfd_arch_info (gdbarch)->mach;
- di->endian = gdbarch_byte_order (gdbarch);
- di->endian_code = gdbarch_byte_order_for_code (gdbarch);
- *disassembler_options_holder = get_all_disassembler_options (gdbarch);
- if (!disassembler_options_holder->empty ())
- di->disassembler_options = disassembler_options_holder->c_str ();
- disassemble_init_for_target (di);
- }
- /* Return the length in bytes of INSN. MAX_LEN is the size of the
- buffer containing INSN. */
- int
- gdb_buffered_insn_length (struct gdbarch *gdbarch,
- const gdb_byte *insn, int max_len, CORE_ADDR addr)
- {
- struct disassemble_info di;
- std::string disassembler_options_holder;
- gdb_buffered_insn_length_init_dis (gdbarch, &di, insn, max_len, addr,
- &disassembler_options_holder);
- int result = gdbarch_print_insn (gdbarch, addr, &di);
- disassemble_free_target (&di);
- return result;
- }
- char *
- get_disassembler_options (struct gdbarch *gdbarch)
- {
- char **disassembler_options = gdbarch_disassembler_options (gdbarch);
- if (disassembler_options == NULL)
- return NULL;
- return *disassembler_options;
- }
- void
- set_disassembler_options (const char *prospective_options)
- {
- struct gdbarch *gdbarch = get_current_arch ();
- char **disassembler_options = gdbarch_disassembler_options (gdbarch);
- const disasm_options_and_args_t *valid_options_and_args;
- const disasm_options_t *valid_options;
- gdb::unique_xmalloc_ptr<char> prospective_options_local
- = make_unique_xstrdup (prospective_options);
- char *options = remove_whitespace_and_extra_commas
- (prospective_options_local.get ());
- const char *opt;
- /* Allow all architectures, even ones that do not support 'set disassembler',
- to reset their disassembler options to NULL. */
- if (options == NULL)
- {
- if (disassembler_options != NULL)
- {
- free (*disassembler_options);
- *disassembler_options = NULL;
- }
- return;
- }
- valid_options_and_args = gdbarch_valid_disassembler_options (gdbarch);
- if (valid_options_and_args == NULL)
- {
- gdb_printf (gdb_stderr, _("\
- 'set disassembler-options ...' is not supported on this architecture.\n"));
- return;
- }
- valid_options = &valid_options_and_args->options;
- /* Verify we have valid disassembler options. */
- FOR_EACH_DISASSEMBLER_OPTION (opt, options)
- {
- size_t i;
- for (i = 0; valid_options->name[i] != NULL; i++)
- if (valid_options->arg != NULL && valid_options->arg[i] != NULL)
- {
- size_t len = strlen (valid_options->name[i]);
- bool found = false;
- const char *arg;
- size_t j;
- if (memcmp (opt, valid_options->name[i], len) != 0)
- continue;
- arg = opt + len;
- for (j = 0; valid_options->arg[i]->values[j] != NULL; j++)
- if (disassembler_options_cmp
- (arg, valid_options->arg[i]->values[j]) == 0)
- {
- found = true;
- break;
- }
- if (found)
- break;
- }
- else if (disassembler_options_cmp (opt, valid_options->name[i]) == 0)
- break;
- if (valid_options->name[i] == NULL)
- {
- gdb_printf (gdb_stderr,
- _("Invalid disassembler option value: '%s'.\n"),
- opt);
- return;
- }
- }
- free (*disassembler_options);
- *disassembler_options = xstrdup (options);
- }
- static void
- set_disassembler_options_sfunc (const char *args, int from_tty,
- struct cmd_list_element *c)
- {
- set_disassembler_options (prospective_options.c_str ());
- }
- static void
- show_disassembler_options_sfunc (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
- {
- struct gdbarch *gdbarch = get_current_arch ();
- const disasm_options_and_args_t *valid_options_and_args;
- const disasm_option_arg_t *valid_args;
- const disasm_options_t *valid_options;
- const char *options = get_disassembler_options (gdbarch);
- if (options == NULL)
- options = "";
- gdb_printf (file, _("The current disassembler options are '%s'\n\n"),
- options);
- valid_options_and_args = gdbarch_valid_disassembler_options (gdbarch);
- if (valid_options_and_args == NULL)
- {
- gdb_puts (_("There are no disassembler options available "
- "for this architecture.\n"),
- file);
- return;
- }
- valid_options = &valid_options_and_args->options;
- gdb_printf (file, _("\
- The following disassembler options are supported for use with the\n\
- 'set disassembler-options OPTION [,OPTION]...' command:\n"));
- if (valid_options->description != NULL)
- {
- size_t i, max_len = 0;
- gdb_printf (file, "\n");
- /* Compute the length of the longest option name. */
- for (i = 0; valid_options->name[i] != NULL; i++)
- {
- size_t len = strlen (valid_options->name[i]);
- if (valid_options->arg != NULL && valid_options->arg[i] != NULL)
- len += strlen (valid_options->arg[i]->name);
- if (max_len < len)
- max_len = len;
- }
- for (i = 0, max_len++; valid_options->name[i] != NULL; i++)
- {
- gdb_printf (file, " %s", valid_options->name[i]);
- if (valid_options->arg != NULL && valid_options->arg[i] != NULL)
- gdb_printf (file, "%s", valid_options->arg[i]->name);
- if (valid_options->description[i] != NULL)
- {
- size_t len = strlen (valid_options->name[i]);
- if (valid_options->arg != NULL && valid_options->arg[i] != NULL)
- len += strlen (valid_options->arg[i]->name);
- gdb_printf (file, "%*c %s", (int) (max_len - len), ' ',
- valid_options->description[i]);
- }
- gdb_printf (file, "\n");
- }
- }
- else
- {
- size_t i;
- gdb_printf (file, " ");
- for (i = 0; valid_options->name[i] != NULL; i++)
- {
- gdb_printf (file, "%s", valid_options->name[i]);
- if (valid_options->arg != NULL && valid_options->arg[i] != NULL)
- gdb_printf (file, "%s", valid_options->arg[i]->name);
- if (valid_options->name[i + 1] != NULL)
- gdb_printf (file, ", ");
- file->wrap_here (2);
- }
- gdb_printf (file, "\n");
- }
- valid_args = valid_options_and_args->args;
- if (valid_args != NULL)
- {
- size_t i, j;
- for (i = 0; valid_args[i].name != NULL; i++)
- {
- gdb_printf (file, _("\n\
- For the options above, the following values are supported for \"%s\":\n "),
- valid_args[i].name);
- for (j = 0; valid_args[i].values[j] != NULL; j++)
- {
- gdb_printf (file, " %s", valid_args[i].values[j]);
- file->wrap_here (3);
- }
- gdb_printf (file, "\n");
- }
- }
- }
- /* A completion function for "set disassembler". */
- static void
- disassembler_options_completer (struct cmd_list_element *ignore,
- completion_tracker &tracker,
- const char *text, const char *word)
- {
- struct gdbarch *gdbarch = get_current_arch ();
- const disasm_options_and_args_t *opts_and_args
- = gdbarch_valid_disassembler_options (gdbarch);
- if (opts_and_args != NULL)
- {
- const disasm_options_t *opts = &opts_and_args->options;
- /* Only attempt to complete on the last option text. */
- const char *separator = strrchr (text, ',');
- if (separator != NULL)
- text = separator + 1;
- text = skip_spaces (text);
- complete_on_enum (tracker, opts->name, text, word);
- }
- }
- /* Initialization code. */
- void _initialize_disasm ();
- void
- _initialize_disasm ()
- {
- /* Add the command that controls the disassembler options. */
- set_show_commands set_show_disas_opts
- = add_setshow_string_noescape_cmd ("disassembler-options", no_class,
- &prospective_options, _("\
- Set the disassembler options.\n\
- Usage: set disassembler-options OPTION [,OPTION]...\n\n\
- See: 'show disassembler-options' for valid option values."), _("\
- Show the disassembler options."), NULL,
- set_disassembler_options_sfunc,
- show_disassembler_options_sfunc,
- &setlist, &showlist);
- set_cmd_completer (set_show_disas_opts.set, disassembler_options_completer);
- }
|