diff options
Diffstat (limited to 'contrib/llvm/tools/lldb/source/Commands/CommandObjectDisassemble.cpp')
-rw-r--r-- | contrib/llvm/tools/lldb/source/Commands/CommandObjectDisassemble.cpp | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/contrib/llvm/tools/lldb/source/Commands/CommandObjectDisassemble.cpp b/contrib/llvm/tools/lldb/source/Commands/CommandObjectDisassemble.cpp new file mode 100644 index 000000000000..5d0f2417f992 --- /dev/null +++ b/contrib/llvm/tools/lldb/source/Commands/CommandObjectDisassemble.cpp @@ -0,0 +1,563 @@ +//===-- CommandObjectDisassemble.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#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/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; + +static OptionDefinition g_disassemble_options[] = { + // clang-format off + { LLDB_OPT_SET_ALL, false, "bytes", 'b', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Show opcode bytes when disassembling." }, + { LLDB_OPT_SET_ALL, false, "context", 'C', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNumLines, "Number of context lines of source to show." }, + { LLDB_OPT_SET_ALL, false, "mixed", 'm', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable mixed source and assembly display." }, + { LLDB_OPT_SET_ALL, false, "raw", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Print raw disassembly with no symbol information." }, + { LLDB_OPT_SET_ALL, false, "plugin", 'P', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePlugin, "Name of the disassembler plugin you want to use." }, + { LLDB_OPT_SET_ALL, false, "flavor", 'F', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeDisassemblyFlavor, "Name of the disassembly flavor you want to use. " + "Currently the only valid options are default, and for Intel " + "architectures, att and intel." }, + { LLDB_OPT_SET_ALL, false, "arch", 'A', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeArchitecture, "Specify the architecture to use from cross disassembly." }, + { LLDB_OPT_SET_1 | + LLDB_OPT_SET_2, true, "start-address", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Address at which to start disassembling." }, + { LLDB_OPT_SET_1, false, "end-address", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Address at which to end disassembling." }, + { LLDB_OPT_SET_2 | + LLDB_OPT_SET_3 | + LLDB_OPT_SET_4 | + LLDB_OPT_SET_5, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNumLines, "Number of instructions to display." }, + { LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, "Disassemble entire contents of the given function name." }, + { LLDB_OPT_SET_4, false, "frame", 'f', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Disassemble from the start of the current frame's function." }, + { LLDB_OPT_SET_5, false, "pc", 'p', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Disassemble around the current pc." }, + { LLDB_OPT_SET_6, false, "line", 'l', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Disassemble the current frame's current source line instructions if there is debug line " + "table information, else disassemble around the pc." }, + { LLDB_OPT_SET_7, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Disassemble function containing this address." }, + // clang-format on +}; + +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 = Args::StringToAddress(execution_context, option_arg, + LLDB_INVALID_ADDRESS, &error); + if (start_addr != LLDB_INVALID_ADDRESS) + some_location_specified = true; + } break; + case 'e': { + end_addr = Args::StringToAddress(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) { + auto target_sp = + execution_context ? execution_context->GetTargetSP() : TargetSP(); + auto platform_sp = target_sp ? target_sp->GetPlatform() : PlatformSP(); + if (!arch.SetTriple(option_arg, platform_sp.get())) + arch.SetTriple(option_arg); + } + break; + + case 'a': { + symbol_containing_addr = Args::StringToAddress( + execution_context, option_arg, LLDB_INVALID_ADDRESS, &error); + if (symbol_containing_addr != LLDB_INVALID_ADDRESS) { + some_location_specified = true; + } + } break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", + short_option); + break; + } + + 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>]"), + m_options() {} + +CommandObjectDisassemble::~CommandObjectDisassemble() = default; + +bool CommandObjectDisassemble::DoExecute(Args &command, + CommandReturnObject &result) { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == nullptr) { + result.AppendError("invalid target, create a debug target using the " + "'target create' command"); + result.SetStatus(eReturnStatusFailed); + return false; + } + 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( + m_interpreter.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( + m_interpreter.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( + m_interpreter.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", + m_options.start_addr); + result.SetStatus(eReturnStatusFailed); + } + if (print_sc_header) + result.AppendMessage("\n"); + } + } + } + + return result.Succeeded(); +} |