summaryrefslogtreecommitdiff
path: root/lldb/source/Commands/CommandObjectDisassemble.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Commands/CommandObjectDisassemble.cpp')
-rw-r--r--lldb/source/Commands/CommandObjectDisassemble.cpp523
1 files changed, 523 insertions, 0 deletions
diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp
new file mode 100644
index 000000000000..69e2d757b5fe
--- /dev/null
+++ b/lldb/source/Commands/CommandObjectDisassemble.cpp
@@ -0,0 +1,523 @@
+//===-- CommandObjectDisassemble.cpp ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectDisassemble.h"
+#include "lldb/Core/AddressRange.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/Options.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Target.h"
+
+#define DEFAULT_DISASM_BYTE_SIZE 32
+#define DEFAULT_DISASM_NUM_INS 4
+
+using namespace lldb;
+using namespace lldb_private;
+
+#define LLDB_OPTIONS_disassemble
+#include "CommandOptions.inc"
+
+CommandObjectDisassemble::CommandOptions::CommandOptions()
+ : Options(), num_lines_context(0), num_instructions(0), func_name(),
+ current_function(false), start_addr(), end_addr(), at_pc(false),
+ frame_line(false), plugin_name(), flavor_string(), arch(),
+ some_location_specified(false), symbol_containing_addr() {
+ OptionParsingStarting(nullptr);
+}
+
+CommandObjectDisassemble::CommandOptions::~CommandOptions() = default;
+
+Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
+ uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option) {
+ case 'm':
+ show_mixed = true;
+ break;
+
+ case 'C':
+ if (option_arg.getAsInteger(0, num_lines_context))
+ error.SetErrorStringWithFormat("invalid num context lines string: \"%s\"",
+ option_arg.str().c_str());
+ break;
+
+ case 'c':
+ if (option_arg.getAsInteger(0, num_instructions))
+ error.SetErrorStringWithFormat(
+ "invalid num of instructions string: \"%s\"",
+ option_arg.str().c_str());
+ break;
+
+ case 'b':
+ show_bytes = true;
+ break;
+
+ case 's': {
+ start_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ if (start_addr != LLDB_INVALID_ADDRESS)
+ some_location_specified = true;
+ } break;
+ case 'e': {
+ end_addr = OptionArgParser::ToAddress(execution_context, option_arg,
+ LLDB_INVALID_ADDRESS, &error);
+ if (end_addr != LLDB_INVALID_ADDRESS)
+ some_location_specified = true;
+ } break;
+
+ case 'n':
+ func_name.assign(option_arg);
+ some_location_specified = true;
+ break;
+
+ case 'p':
+ at_pc = true;
+ some_location_specified = true;
+ break;
+
+ case 'l':
+ frame_line = true;
+ // Disassemble the current source line kind of implies showing mixed source
+ // code context.
+ show_mixed = true;
+ some_location_specified = true;
+ break;
+
+ case 'P':
+ plugin_name.assign(option_arg);
+ break;
+
+ case 'F': {
+ TargetSP target_sp =
+ execution_context ? execution_context->GetTargetSP() : TargetSP();
+ if (target_sp && (target_sp->GetArchitecture().GetTriple().getArch() ==
+ llvm::Triple::x86 ||
+ target_sp->GetArchitecture().GetTriple().getArch() ==
+ llvm::Triple::x86_64)) {
+ flavor_string.assign(option_arg);
+ } else
+ error.SetErrorStringWithFormat("Disassembler flavors are currently only "
+ "supported for x86 and x86_64 targets.");
+ break;
+ }
+
+ case 'r':
+ raw = true;
+ break;
+
+ case 'f':
+ current_function = true;
+ some_location_specified = true;
+ break;
+
+ case 'A':
+ if (execution_context) {
+ const auto &target_sp = execution_context->GetTargetSP();
+ auto platform_ptr = target_sp ? target_sp->GetPlatform().get() : nullptr;
+ arch = Platform::GetAugmentedArchSpec(platform_ptr, option_arg);
+ }
+ break;
+
+ case 'a': {
+ symbol_containing_addr = OptionArgParser::ToAddress(
+ execution_context, option_arg, LLDB_INVALID_ADDRESS, &error);
+ if (symbol_containing_addr != LLDB_INVALID_ADDRESS) {
+ some_location_specified = true;
+ }
+ } break;
+
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+
+ return error;
+}
+
+void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
+ ExecutionContext *execution_context) {
+ show_mixed = false;
+ show_bytes = false;
+ num_lines_context = 0;
+ num_instructions = 0;
+ func_name.clear();
+ current_function = false;
+ at_pc = false;
+ frame_line = false;
+ start_addr = LLDB_INVALID_ADDRESS;
+ end_addr = LLDB_INVALID_ADDRESS;
+ symbol_containing_addr = LLDB_INVALID_ADDRESS;
+ raw = false;
+ plugin_name.clear();
+
+ Target *target =
+ execution_context ? execution_context->GetTargetPtr() : nullptr;
+
+ // This is a hack till we get the ability to specify features based on
+ // architecture. For now GetDisassemblyFlavor is really only valid for x86
+ // (and for the llvm assembler plugin, but I'm papering over that since that
+ // is the only disassembler plugin we have...
+ if (target) {
+ if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 ||
+ target->GetArchitecture().GetTriple().getArch() ==
+ llvm::Triple::x86_64) {
+ flavor_string.assign(target->GetDisassemblyFlavor());
+ } else
+ flavor_string.assign("default");
+
+ } else
+ flavor_string.assign("default");
+
+ arch.Clear();
+ some_location_specified = false;
+}
+
+Status CommandObjectDisassemble::CommandOptions::OptionParsingFinished(
+ ExecutionContext *execution_context) {
+ if (!some_location_specified)
+ current_function = true;
+ return Status();
+}
+
+llvm::ArrayRef<OptionDefinition>
+CommandObjectDisassemble::CommandOptions::GetDefinitions() {
+ return llvm::makeArrayRef(g_disassemble_options);
+}
+
+// CommandObjectDisassemble
+
+CommandObjectDisassemble::CommandObjectDisassemble(
+ CommandInterpreter &interpreter)
+ : CommandObjectParsed(
+ interpreter, "disassemble",
+ "Disassemble specified instructions in the current target. "
+ "Defaults to the current function for the current thread and "
+ "stack frame.",
+ "disassemble [<cmd-options>]", eCommandRequiresTarget),
+ m_options() {}
+
+CommandObjectDisassemble::~CommandObjectDisassemble() = default;
+
+bool CommandObjectDisassemble::DoExecute(Args &command,
+ CommandReturnObject &result) {
+ Target *target = &GetSelectedTarget();
+
+ if (!m_options.arch.IsValid())
+ m_options.arch = target->GetArchitecture();
+
+ if (!m_options.arch.IsValid()) {
+ result.AppendError(
+ "use the --arch option or set the target architecture to disassemble");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ const char *plugin_name = m_options.GetPluginName();
+ const char *flavor_string = m_options.GetFlavorString();
+
+ DisassemblerSP disassembler =
+ Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name);
+
+ if (!disassembler) {
+ if (plugin_name) {
+ result.AppendErrorWithFormat(
+ "Unable to find Disassembler plug-in named '%s' that supports the "
+ "'%s' architecture.\n",
+ plugin_name, m_options.arch.GetArchitectureName());
+ } else
+ result.AppendErrorWithFormat(
+ "Unable to find Disassembler plug-in for the '%s' architecture.\n",
+ m_options.arch.GetArchitectureName());
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ } else if (flavor_string != nullptr &&
+ !disassembler->FlavorValidForArchSpec(m_options.arch,
+ flavor_string))
+ result.AppendWarningWithFormat(
+ "invalid disassembler flavor \"%s\", using default.\n", flavor_string);
+
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+
+ if (!command.empty()) {
+ result.AppendErrorWithFormat(
+ "\"disassemble\" arguments are specified as options.\n");
+ const int terminal_width =
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth();
+ GetOptions()->GenerateOptionUsage(result.GetErrorStream(), this,
+ terminal_width);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+
+ if (m_options.show_mixed && m_options.num_lines_context == 0)
+ m_options.num_lines_context = 2;
+
+ // Always show the PC in the disassembly
+ uint32_t options = Disassembler::eOptionMarkPCAddress;
+
+ // Mark the source line for the current PC only if we are doing mixed source
+ // and assembly
+ if (m_options.show_mixed)
+ options |= Disassembler::eOptionMarkPCSourceLine;
+
+ if (m_options.show_bytes)
+ options |= Disassembler::eOptionShowBytes;
+
+ if (m_options.raw)
+ options |= Disassembler::eOptionRawOuput;
+
+ if (!m_options.func_name.empty()) {
+ ConstString name(m_options.func_name.c_str());
+
+ if (Disassembler::Disassemble(
+ GetDebugger(), m_options.arch, plugin_name, flavor_string,
+ m_exe_ctx, name,
+ nullptr, // Module *
+ m_options.num_instructions, m_options.show_mixed,
+ m_options.show_mixed ? m_options.num_lines_context : 0, options,
+ result.GetOutputStream())) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat("Unable to find symbol with name '%s'.\n",
+ name.GetCString());
+ result.SetStatus(eReturnStatusFailed);
+ }
+ } else {
+ std::vector<AddressRange> ranges;
+ AddressRange range;
+ StackFrame *frame = m_exe_ctx.GetFramePtr();
+ if (m_options.frame_line) {
+ if (frame == nullptr) {
+ result.AppendError("Cannot disassemble around the current line without "
+ "a selected frame.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ LineEntry pc_line_entry(
+ frame->GetSymbolContext(eSymbolContextLineEntry).line_entry);
+ if (pc_line_entry.IsValid()) {
+ range = pc_line_entry.range;
+ } else {
+ m_options.at_pc =
+ true; // No line entry, so just disassemble around the current pc
+ m_options.show_mixed = false;
+ }
+ } else if (m_options.current_function) {
+ if (frame == nullptr) {
+ result.AppendError("Cannot disassemble around the current function "
+ "without a selected frame.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ Symbol *symbol = frame->GetSymbolContext(eSymbolContextSymbol).symbol;
+ if (symbol) {
+ range.GetBaseAddress() = symbol->GetAddress();
+ range.SetByteSize(symbol->GetByteSize());
+ }
+ }
+
+ // Did the "m_options.frame_line" find a valid range already? If so skip
+ // the rest...
+ if (range.GetByteSize() == 0) {
+ if (m_options.at_pc) {
+ if (frame == nullptr) {
+ result.AppendError("Cannot disassemble around the current PC without "
+ "a selected frame.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ range.GetBaseAddress() = frame->GetFrameCodeAddress();
+ if (m_options.num_instructions == 0) {
+ // Disassembling at the PC always disassembles some number of
+ // instructions (not the whole function).
+ m_options.num_instructions = DEFAULT_DISASM_NUM_INS;
+ }
+ ranges.push_back(range);
+ } else {
+ range.GetBaseAddress().SetOffset(m_options.start_addr);
+ if (range.GetBaseAddress().IsValid()) {
+ if (m_options.end_addr != LLDB_INVALID_ADDRESS) {
+ if (m_options.end_addr <= m_options.start_addr) {
+ result.AppendErrorWithFormat(
+ "End address before start address.\n");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ range.SetByteSize(m_options.end_addr - m_options.start_addr);
+ }
+ ranges.push_back(range);
+ } else {
+ if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS &&
+ target) {
+ if (!target->GetSectionLoadList().IsEmpty()) {
+ bool failed = false;
+ Address symbol_containing_address;
+ if (target->GetSectionLoadList().ResolveLoadAddress(
+ m_options.symbol_containing_addr,
+ symbol_containing_address)) {
+ ModuleSP module_sp(symbol_containing_address.GetModule());
+ SymbolContext sc;
+ bool resolve_tail_call_address = true; // PC can be one past the
+ // address range of the
+ // function.
+ module_sp->ResolveSymbolContextForAddress(
+ symbol_containing_address, eSymbolContextEverything, sc,
+ resolve_tail_call_address);
+ if (sc.function || sc.symbol) {
+ sc.GetAddressRange(eSymbolContextFunction |
+ eSymbolContextSymbol,
+ 0, false, range);
+ } else {
+ failed = true;
+ }
+ } else {
+ failed = true;
+ }
+ if (failed) {
+ result.AppendErrorWithFormat(
+ "Could not find function bounds for address 0x%" PRIx64
+ "\n",
+ m_options.symbol_containing_addr);
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ ranges.push_back(range);
+ } else {
+ for (lldb::ModuleSP module_sp : target->GetImages().Modules()) {
+ lldb::addr_t file_addr = m_options.symbol_containing_addr;
+ Address file_address;
+ if (module_sp->ResolveFileAddress(file_addr, file_address)) {
+ SymbolContext sc;
+ bool resolve_tail_call_address = true; // PC can be one past
+ // the address range of
+ // the function.
+ module_sp->ResolveSymbolContextForAddress(
+ file_address, eSymbolContextEverything, sc,
+ resolve_tail_call_address);
+ if (sc.function || sc.symbol) {
+ sc.GetAddressRange(eSymbolContextFunction |
+ eSymbolContextSymbol,
+ 0, false, range);
+ ranges.push_back(range);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else
+ ranges.push_back(range);
+
+ if (m_options.num_instructions != 0) {
+ if (ranges.empty()) {
+ // The default action is to disassemble the current frame function.
+ if (frame) {
+ SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
+ eSymbolContextSymbol));
+ if (sc.function)
+ range.GetBaseAddress() =
+ sc.function->GetAddressRange().GetBaseAddress();
+ else if (sc.symbol && sc.symbol->ValueIsAddress())
+ range.GetBaseAddress() = sc.symbol->GetAddress();
+ else
+ range.GetBaseAddress() = frame->GetFrameCodeAddress();
+ }
+
+ if (!range.GetBaseAddress().IsValid()) {
+ result.AppendError("invalid frame");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ }
+
+ bool print_sc_header = ranges.size() > 1;
+ for (AddressRange cur_range : ranges) {
+ if (Disassembler::Disassemble(
+ GetDebugger(), m_options.arch, plugin_name, flavor_string,
+ m_exe_ctx, cur_range.GetBaseAddress(),
+ m_options.num_instructions, m_options.show_mixed,
+ m_options.show_mixed ? m_options.num_lines_context : 0, options,
+ result.GetOutputStream())) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ if (m_options.start_addr != LLDB_INVALID_ADDRESS)
+ result.AppendErrorWithFormat(
+ "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
+ m_options.start_addr);
+ else if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS)
+ result.AppendErrorWithFormat(
+ "Failed to disassemble memory in function at 0x%8.8" PRIx64
+ ".\n",
+ m_options.symbol_containing_addr);
+ result.SetStatus(eReturnStatusFailed);
+ }
+ }
+ if (print_sc_header)
+ result.AppendMessage("\n");
+ } else {
+ if (ranges.empty()) {
+ // The default action is to disassemble the current frame function.
+ if (frame) {
+ SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
+ eSymbolContextSymbol));
+ if (sc.function)
+ range = sc.function->GetAddressRange();
+ else if (sc.symbol && sc.symbol->ValueIsAddress()) {
+ range.GetBaseAddress() = sc.symbol->GetAddress();
+ range.SetByteSize(sc.symbol->GetByteSize());
+ } else
+ range.GetBaseAddress() = frame->GetFrameCodeAddress();
+ } else {
+ result.AppendError("invalid frame");
+ result.SetStatus(eReturnStatusFailed);
+ return false;
+ }
+ ranges.push_back(range);
+ }
+
+ bool print_sc_header = ranges.size() > 1;
+ for (AddressRange cur_range : ranges) {
+ if (cur_range.GetByteSize() == 0)
+ cur_range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE);
+
+ if (Disassembler::Disassemble(
+ GetDebugger(), m_options.arch, plugin_name, flavor_string,
+ m_exe_ctx, cur_range, m_options.num_instructions,
+ m_options.show_mixed,
+ m_options.show_mixed ? m_options.num_lines_context : 0, options,
+ result.GetOutputStream())) {
+ result.SetStatus(eReturnStatusSuccessFinishResult);
+ } else {
+ result.AppendErrorWithFormat(
+ "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
+ cur_range.GetBaseAddress().GetLoadAddress(target));
+ result.SetStatus(eReturnStatusFailed);
+ }
+ if (print_sc_header)
+ result.AppendMessage("\n");
+ }
+ }
+ }
+
+ return result.Succeeded();
+}