diff options
Diffstat (limited to 'lldb/source/Commands/CommandObjectDisassemble.cpp')
| -rw-r--r-- | lldb/source/Commands/CommandObjectDisassemble.cpp | 523 | 
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(); +}  | 
