mirror of
https://github.com/intel/llvm.git
synced 2026-01-16 21:55:39 +08:00
Summary: Right now our CommandOptions.inc only generates the initializer for the options list but not the array declaration boilerplate around it. As the array definition is identical for all arrays, we might as well also let the CommandOptions.inc generate it alongside the initializers. This patch will also allow us to generate additional declarations related to that option list in the future (e.g. a enum class representing the specific options which would make our handling code less prone). This patch also fixes a few option tables that didn't follow our naming style. Reviewers: JDevlieghere Reviewed By: JDevlieghere Subscribers: abidh, lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D65331 llvm-svn: 367186
531 lines
19 KiB
C++
531 lines
19 KiB
C++
//===-- 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:
|
|
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 = 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(
|
|
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",
|
|
m_options.start_addr);
|
|
result.SetStatus(eReturnStatusFailed);
|
|
}
|
|
if (print_sc_header)
|
|
result.AppendMessage("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
return result.Succeeded();
|
|
}
|