llvm-project/lldb/source/Commands/CommandObjectDisassemble.cpp
Jason Molenda f23bf7432c Add a new base class, Frame. It is a pure virtual function which
defines a protocol that all subclasses will implement.  StackFrame
is currently the only subclass and the methods that Frame vends are
nearly identical to StackFrame's old methods.

Update all callers to use Frame*/Frame& instead of pointers to
StackFrames.

This is almost entirely a mechanical change that touches a lot of
the code base so I'm committing it alone.  No new functionality is
added with this patch, no new subclasses of Frame exist yet.

I'll probably need to tweak some of the separation, possibly moving
some of StackFrame's methods up in to Frame, but this is a good
starting point.

<rdar://problem/15314068>

llvm-svn: 193907
2013-11-02 02:23:02 +00:00

577 lines
23 KiB
C++

//===-- CommandObjectDisassemble.cpp ----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/lldb-python.h"
#include "CommandObjectDisassemble.h"
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/AddressRange.h"
#include "lldb/Core/Disassembler.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/SourceManager.h"
#include "lldb/Interpreter/Args.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/Frame.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;
CommandObjectDisassemble::CommandOptions::CommandOptions (CommandInterpreter &interpreter) :
Options(interpreter),
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();
}
CommandObjectDisassemble::CommandOptions::~CommandOptions ()
{
}
Error
CommandObjectDisassemble::CommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg)
{
Error error;
const int short_option = m_getopt_table[option_idx].val;
bool success;
switch (short_option)
{
case 'm':
show_mixed = true;
break;
case 'C':
num_lines_context = Args::StringToUInt32(option_arg, 0, 0, &success);
if (!success)
error.SetErrorStringWithFormat ("invalid num context lines string: \"%s\"", option_arg);
break;
case 'c':
num_instructions = Args::StringToUInt32(option_arg, 0, 0, &success);
if (!success)
error.SetErrorStringWithFormat ("invalid num of instructions string: \"%s\"", option_arg);
break;
case 'b':
show_bytes = true;
break;
case 's':
{
ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
start_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error);
if (start_addr != LLDB_INVALID_ADDRESS)
some_location_specified = true;
}
break;
case 'e':
{
ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
end_addr = Args::StringToAddress(&exe_ctx, 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':
{
Target *target = m_interpreter.GetExecutionContext().GetTargetPtr();
if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86
|| target->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 (!arch.SetTriple (option_arg, m_interpreter.GetPlatform (true).get()))
arch.SetTriple (option_arg);
break;
case 'a':
{
ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
symbol_containing_addr = Args::StringToAddress(&exe_ctx, 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 ()
{
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 = m_interpreter.GetExecutionContext().GetTargetPtr();
// 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;
}
Error
CommandObjectDisassemble::CommandOptions::OptionParsingFinished ()
{
if (!some_location_specified)
current_function = true;
return Error();
}
const OptionDefinition*
CommandObjectDisassemble::CommandOptions::GetDefinitions ()
{
return g_option_table;
}
OptionDefinition
CommandObjectDisassemble::CommandOptions::g_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "bytes" , 'b', OptionParser::eNoArgument , NULL, 0, eArgTypeNone, "Show opcode bytes when disassembling."},
{ LLDB_OPT_SET_ALL, false, "context" , 'C', OptionParser::eRequiredArgument , NULL, 0, eArgTypeNumLines, "Number of context lines of source to show."},
{ LLDB_OPT_SET_ALL, false, "mixed" , 'm', OptionParser::eNoArgument , NULL, 0, eArgTypeNone, "Enable mixed source and assembly display."},
{ LLDB_OPT_SET_ALL, false, "raw" , 'r', OptionParser::eNoArgument , NULL, 0, eArgTypeNone, "Print raw disassembly with no symbol information."},
{ LLDB_OPT_SET_ALL, false, "plugin" , 'P', OptionParser::eRequiredArgument , NULL, 0, eArgTypePlugin, "Name of the disassembler plugin you want to use."},
{ LLDB_OPT_SET_ALL, false, "flavor" , 'F', OptionParser::eRequiredArgument , NULL, 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 , NULL, 0, eArgTypeArchitecture,"Specify the architecture to use from cross disassembly."},
{ LLDB_OPT_SET_1 |
LLDB_OPT_SET_2 , true , "start-address", 's', OptionParser::eRequiredArgument , NULL, 0, eArgTypeAddressOrExpression,"Address at which to start disassembling."},
{ LLDB_OPT_SET_1 , false, "end-address" , 'e', OptionParser::eRequiredArgument , NULL, 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 , NULL, 0, eArgTypeNumLines, "Number of instructions to display."},
{ LLDB_OPT_SET_3 , false, "name" , 'n', OptionParser::eRequiredArgument , NULL, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName,
"Disassemble entire contents of the given function name."},
{ LLDB_OPT_SET_4 , false, "frame" , 'f', OptionParser::eNoArgument , NULL, 0, eArgTypeNone, "Disassemble from the start of the current frame's function."},
{ LLDB_OPT_SET_5 , false, "pc" , 'p', OptionParser::eNoArgument , NULL, 0, eArgTypeNone, "Disassemble around the current pc."},
{ LLDB_OPT_SET_6 , false, "line" , 'l', OptionParser::eNoArgument , NULL, 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 , NULL, 0, eArgTypeAddressOrExpression, "Disassemble function containing this address."},
{ 0 , false, NULL , 0, 0 , NULL, 0, eArgTypeNone, NULL }
};
//-------------------------------------------------------------------------
// CommandObjectDisassemble
//-------------------------------------------------------------------------
CommandObjectDisassemble::CommandObjectDisassemble (CommandInterpreter &interpreter) :
CommandObjectParsed (interpreter,
"disassemble",
"Disassemble bytes in the current function, or elsewhere in the executable program as specified by the user.",
"disassemble [<cmd-options>]"),
m_options (interpreter)
{
}
CommandObjectDisassemble::~CommandObjectDisassemble()
{
}
bool
CommandObjectDisassemble::DoExecute (Args& command, CommandReturnObject &result)
{
Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
if (target == NULL)
{
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 architecure 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 != NULL && !disassembler->FlavorValidForArchSpec(m_options.arch, flavor_string))
result.AppendWarningWithFormat("invalid disassembler flavor \"%s\", using default.\n", flavor_string);
result.SetStatus (eReturnStatusSuccessFinishResult);
if (command.GetArgumentCount() != 0)
{
result.AppendErrorWithFormat ("\"disassemble\" arguments are specified as options.\n");
GetOptions()->GenerateOptionUsage (result.GetErrorStream(), this);
result.SetStatus (eReturnStatusFailed);
return false;
}
if (m_options.show_mixed && m_options.num_lines_context == 0)
m_options.num_lines_context = 1;
// 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,
NULL, // Module *
m_options.num_instructions,
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
{
AddressRange range;
Frame *frame = m_exe_ctx.GetFramePtr();
if (m_options.frame_line)
{
if (frame == NULL)
{
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 == NULL)
{
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 == NULL)
{
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;
}
}
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);
}
}
else
{
if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS
&& target
&& !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;
}
}
}
}
}
if (m_options.num_instructions != 0)
{
if (!range.GetBaseAddress().IsValid())
{
// 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;
}
}
if (Disassembler::Disassemble (m_interpreter.GetDebugger(),
m_options.arch,
plugin_name,
flavor_string,
m_exe_ctx,
range.GetBaseAddress(),
m_options.num_instructions,
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);
}
}
else
{
if (!range.GetBaseAddress().IsValid())
{
// 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;
}
}
if (range.GetByteSize() == 0)
range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE);
if (Disassembler::Disassemble (m_interpreter.GetDebugger(),
m_options.arch,
plugin_name,
flavor_string,
m_exe_ctx,
range,
m_options.num_instructions,
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);
}
}
}
return result.Succeeded();
}