Skip to content

Commit

Permalink
[lldb] Support overriding the disassembly CPU & features (#115382)
Browse files Browse the repository at this point in the history
Add the ability to override the disassembly CPU and CPU features through
a target setting (`target.disassembly-cpu` and
`target.disassembly-features`) and a `disassemble` command option
(`--cpu` and `--features`).

This is especially relevant for architectures like RISC-V which relies
heavily on CPU extensions.

The majority of this patch is plumbing the options through. I recommend
looking at DisassemblerLLVMC and the test for the observable change in
behavior.
  • Loading branch information
JDevlieghere authored Nov 12, 2024
1 parent c280522 commit f109517
Show file tree
Hide file tree
Showing 34 changed files with 493 additions and 152 deletions.
32 changes: 17 additions & 15 deletions lldb/include/lldb/Core/Disassembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -409,35 +409,37 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
// flavor string gets set wrong. Instead, if you get a flavor string you
// don't understand, use the default. Folks who care to check can use the
// FlavorValidForArchSpec method on the disassembler they got back.
static lldb::DisassemblerSP
FindPlugin(const ArchSpec &arch, const char *flavor, const char *plugin_name);
static lldb::DisassemblerSP FindPlugin(const ArchSpec &arch,
const char *flavor, const char *cpu,
const char *features,
const char *plugin_name);

// This version will use the value in the Target settings if flavor is NULL;
static lldb::DisassemblerSP FindPluginForTarget(const Target &target,
const ArchSpec &arch,
const char *flavor,
const char *plugin_name);
static lldb::DisassemblerSP
FindPluginForTarget(const Target &target, const ArchSpec &arch,
const char *flavor, const char *cpu, const char *features,
const char *plugin_name);

struct Limit {
enum { Bytes, Instructions } kind;
lldb::addr_t value;
};

static lldb::DisassemblerSP DisassembleRange(const ArchSpec &arch,
const char *plugin_name,
const char *flavor,
Target &target,
const AddressRange &disasm_range,
bool force_live_memory = false);
static lldb::DisassemblerSP
DisassembleRange(const ArchSpec &arch, const char *plugin_name,
const char *flavor, const char *cpu, const char *features,
Target &target, const AddressRange &disasm_range,
bool force_live_memory = false);

static lldb::DisassemblerSP
DisassembleBytes(const ArchSpec &arch, const char *plugin_name,
const char *flavor, const Address &start, const void *bytes,
size_t length, uint32_t max_num_instructions,
bool data_from_file);
const char *flavor, const char *cpu, const char *features,
const Address &start, const void *bytes, size_t length,
uint32_t max_num_instructions, bool data_from_file);

static bool Disassemble(Debugger &debugger, const ArchSpec &arch,
const char *plugin_name, const char *flavor,
const char *cpu, const char *features,
const ExecutionContext &exe_ctx, const Address &start,
Limit limit, bool mixed_source_and_assembly,
uint32_t num_mixed_context_lines, uint32_t options,
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,8 @@ static constexpr CommandObject::ArgumentTableEntry g_argument_table[] = {
{ lldb::eArgTypeRemotePath, "remote-path", lldb::CompletionType::eRemoteDiskFileCompletion, {}, { nullptr, false }, "A path on the system managed by the current platform." },
{ lldb::eArgTypeRemoteFilename, "remote-filename", lldb::CompletionType::eRemoteDiskFileCompletion, {}, { nullptr, false }, "A file on the system managed by the current platform." },
{ lldb::eArgTypeModule, "module", lldb::CompletionType::eModuleCompletion, {}, { nullptr, false }, "The name of a module loaded into the current target." },
{ lldb::eArgTypeCPUName, "cpu-name", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "The name of a CPU." },
{ lldb::eArgTypeCPUFeatures, "cpu-features", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "The CPU feature string." },
// clang-format on
};

Expand Down
4 changes: 4 additions & 0 deletions lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ class TargetProperties : public Properties {

const char *GetDisassemblyFlavor() const;

const char *GetDisassemblyCPU() const;

const char *GetDisassemblyFeatures() const;

InlineStrategy GetInlineStrategy() const;

RealpathPrefixes GetSourceRealpathPrefixes() const;
Expand Down
2 changes: 2 additions & 0 deletions lldb/include/lldb/lldb-enumerations.h
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,8 @@ enum CommandArgumentType {
eArgTypeRemotePath,
eArgTypeRemoteFilename,
eArgTypeModule,
eArgTypeCPUName,
eArgTypeCPUFeatures,
eArgTypeLastArg // Always keep this entry as the last entry in this
// enumeration!!
};
Expand Down
5 changes: 3 additions & 2 deletions lldb/include/lldb/lldb-private-interfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ typedef lldb::ABISP (*ABICreateInstance)(lldb::ProcessSP process_sp,
const ArchSpec &arch);
typedef std::unique_ptr<Architecture> (*ArchitectureCreateInstance)(
const ArchSpec &arch);
typedef lldb::DisassemblerSP (*DisassemblerCreateInstance)(const ArchSpec &arch,
const char *flavor);
typedef lldb::DisassemblerSP (*DisassemblerCreateInstance)(
const ArchSpec &arch, const char *flavor, const char *cpu,
const char *features);
typedef DynamicLoader *(*DynamicLoaderCreateInstance)(Process *process,
bool force);
typedef lldb::JITLoaderSP (*JITLoaderCreateInstance)(Process *process,
Expand Down
5 changes: 3 additions & 2 deletions lldb/source/API/SBFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,9 @@ SBInstructionList SBFunction::GetInstructions(SBTarget target,
lock = std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
const bool force_live_memory = true;
sb_instructions.SetDisassembler(Disassembler::DisassembleRange(
module_sp->GetArchitecture(), nullptr, flavor, *target_sp,
m_opaque_ptr->GetAddressRange(), force_live_memory));
module_sp->GetArchitecture(), nullptr, flavor,
target_sp->GetDisassemblyCPU(), target_sp->GetDisassemblyFeatures(),
*target_sp, m_opaque_ptr->GetAddressRange(), force_live_memory));
}
}
return sb_instructions;
Expand Down
5 changes: 3 additions & 2 deletions lldb/source/API/SBSymbol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ SBInstructionList SBSymbol::GetInstructions(SBTarget target,
AddressRange symbol_range(symbol_addr, m_opaque_ptr->GetByteSize());
const bool force_live_memory = true;
sb_instructions.SetDisassembler(Disassembler::DisassembleRange(
module_sp->GetArchitecture(), nullptr, flavor_string, *target_sp,
symbol_range, force_live_memory));
module_sp->GetArchitecture(), nullptr, flavor_string,
target_sp->GetDisassemblyCPU(), target_sp->GetDisassemblyFeatures(),
*target_sp, symbol_range, force_live_memory));
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions lldb/source/API/SBTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2013,7 +2013,8 @@ lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress base_addr,
error, force_live_memory, &load_addr);
const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS;
sb_instructions.SetDisassembler(Disassembler::DisassembleBytes(
target_sp->GetArchitecture(), nullptr, flavor_string, *addr_ptr,
target_sp->GetArchitecture(), nullptr, target_sp->GetDisassemblyCPU(),
target_sp->GetDisassemblyFeatures(), flavor_string, *addr_ptr,
data.GetBytes(), bytes_read, count, data_from_file));
}
}
Expand All @@ -2038,8 +2039,9 @@ lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress start_addr,
AddressRange range(start_load_addr, size);
const bool force_live_memory = true;
sb_instructions.SetDisassembler(Disassembler::DisassembleRange(
target_sp->GetArchitecture(), nullptr, flavor_string, *target_sp,
range, force_live_memory));
target_sp->GetArchitecture(), nullptr, flavor_string,
target_sp->GetDisassemblyCPU(), target_sp->GetDisassemblyFeatures(),
*target_sp, range, force_live_memory));
}
}
return sb_instructions;
Expand Down Expand Up @@ -2071,8 +2073,9 @@ SBTarget::GetInstructionsWithFlavor(lldb::SBAddress base_addr,
const bool data_from_file = true;

sb_instructions.SetDisassembler(Disassembler::DisassembleBytes(
target_sp->GetArchitecture(), nullptr, flavor_string, addr, buf, size,
UINT32_MAX, data_from_file));
target_sp->GetArchitecture(), nullptr, flavor_string,
target_sp->GetDisassemblyCPU(), target_sp->GetDisassemblyFeatures(),
addr, buf, size, UINT32_MAX, data_from_file));
}

return sb_instructions;
Expand Down
38 changes: 28 additions & 10 deletions lldb/source/Commands/CommandObjectDisassemble.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
break;
}

case 'X':
cpu_string = std::string(option_arg);
break;

case 'Y':
features_string = std::string(option_arg);
break;

case 'r':
raw = true;
break;
Expand Down Expand Up @@ -176,20 +184,27 @@ void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
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) {
// 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->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 ||
target->GetArchitecture().GetTriple().getArch() ==
llvm::Triple::x86_64) {
flavor_string.assign(target->GetDisassemblyFlavor());
} else
} else {
flavor_string.assign("default");

} else
}
if (const char *cpu = target->GetDisassemblyCPU())
cpu_string.assign(cpu);
if (const char *features = target->GetDisassemblyFeatures())
features_string.assign(features);
} else {
flavor_string.assign("default");
cpu_string.assign("default");
features_string.assign("default");
}

arch.Clear();
some_location_specified = false;
Expand Down Expand Up @@ -453,9 +468,11 @@ void CommandObjectDisassemble::DoExecute(Args &command,

const char *plugin_name = m_options.GetPluginName();
const char *flavor_string = m_options.GetFlavorString();
const char *cpu_string = m_options.GetCPUString();
const char *features_string = m_options.GetFeaturesString();

DisassemblerSP disassembler =
Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name);
DisassemblerSP disassembler = Disassembler::FindPlugin(
m_options.arch, flavor_string, cpu_string, features_string, plugin_name);

if (!disassembler) {
if (plugin_name) {
Expand Down Expand Up @@ -524,7 +541,8 @@ void CommandObjectDisassemble::DoExecute(Args &command,
}
if (Disassembler::Disassemble(
GetDebugger(), m_options.arch, plugin_name, flavor_string,
m_exe_ctx, cur_range.GetBaseAddress(), limit, m_options.show_mixed,
cpu_string, features_string, m_exe_ctx, cur_range.GetBaseAddress(),
limit, m_options.show_mixed,
m_options.show_mixed ? m_options.num_lines_context : 0, options,
result.GetOutputStream())) {
result.SetStatus(eReturnStatusSuccessFinishResult);
Expand Down
14 changes: 14 additions & 0 deletions lldb/source/Commands/CommandObjectDisassemble.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ class CommandObjectDisassemble : public CommandObjectParsed {
return flavor_string.c_str();
}

const char *GetCPUString() {
if (cpu_string.empty() || cpu_string == "default")
return nullptr;
return cpu_string.c_str();
}

const char *GetFeaturesString() {
if (features_string.empty() || features_string == "default")
return nullptr;
return features_string.c_str();
}

Status OptionParsingFinished(ExecutionContext *execution_context) override;

bool show_mixed; // Show mixed source/assembly
Expand All @@ -58,6 +70,8 @@ class CommandObjectDisassemble : public CommandObjectParsed {
bool frame_line = false;
std::string plugin_name;
std::string flavor_string;
std::string cpu_string;
std::string features_string;
ArchSpec arch;
bool some_location_specified = false; // If no location was specified, we'll
// select "at_pc". This should be set
Expand Down
4 changes: 4 additions & 0 deletions lldb/source/Commands/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ let Command = "disassemble" in {
Arg<"DisassemblyFlavor">, Desc<"Name of the disassembly flavor you want to "
"use. Currently the only valid options are default, and for Intel "
"architectures, att and intel.">;
def disassemble_options_cpu : Option<"cpu", "X">, Arg<"CPUName">,
Desc<"Override the CPU for disassembling.">;
def disassemble_options_features : Option<"features", "Y">, Arg<"CPUFeatures">,
Desc<"Specify additional CPU features for disassembling.">;
def disassemble_options_arch : Option<"arch", "A">, Arg<"Architecture">,
Desc<"Specify the architecture to use from cross disassembly.">;
def disassemble_options_start_address : Option<"start-address", "s">,
Expand Down
42 changes: 25 additions & 17 deletions lldb/source/Core/Disassembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ using namespace lldb;
using namespace lldb_private;

DisassemblerSP Disassembler::FindPlugin(const ArchSpec &arch,
const char *flavor,
const char *flavor, const char *cpu,
const char *features,
const char *plugin_name) {
LLDB_SCOPED_TIMERF("Disassembler::FindPlugin (arch = %s, plugin_name = %s)",
arch.GetArchitectureName(), plugin_name);
Expand All @@ -67,34 +68,38 @@ DisassemblerSP Disassembler::FindPlugin(const ArchSpec &arch,
create_callback =
PluginManager::GetDisassemblerCreateCallbackForPluginName(plugin_name);
if (create_callback) {
if (auto disasm_sp = create_callback(arch, flavor))
if (auto disasm_sp = create_callback(arch, flavor, cpu, features))
return disasm_sp;
}
} else {
for (uint32_t idx = 0;
(create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex(
idx)) != nullptr;
++idx) {
if (auto disasm_sp = create_callback(arch, flavor))
if (auto disasm_sp = create_callback(arch, flavor, cpu, features))
return disasm_sp;
}
}
return DisassemblerSP();
}

DisassemblerSP Disassembler::FindPluginForTarget(const Target &target,
const ArchSpec &arch,
const char *flavor,
const char *plugin_name) {
if (flavor == nullptr) {
DisassemblerSP Disassembler::FindPluginForTarget(
const Target &target, const ArchSpec &arch, const char *flavor,
const char *cpu, const char *features, const char *plugin_name) {
if (!flavor) {
// FIXME - we don't have the mechanism in place to do per-architecture
// settings. But since we know that for now we only support flavors on x86
// & x86_64,
if (arch.GetTriple().getArch() == llvm::Triple::x86 ||
arch.GetTriple().getArch() == llvm::Triple::x86_64)
flavor = target.GetDisassemblyFlavor();
}
return FindPlugin(arch, flavor, plugin_name);
if (!cpu)
cpu = target.GetDisassemblyCPU();
if (!features)
features = target.GetDisassemblyFeatures();

return FindPlugin(arch, flavor, cpu, features, plugin_name);
}

static Address ResolveAddress(Target &target, const Address &addr) {
Expand All @@ -117,15 +122,16 @@ static Address ResolveAddress(Target &target, const Address &addr) {

lldb::DisassemblerSP Disassembler::DisassembleRange(
const ArchSpec &arch, const char *plugin_name, const char *flavor,
Target &target, const AddressRange &range, bool force_live_memory) {
const char *cpu, const char *features, Target &target,
const AddressRange &range, bool force_live_memory) {
if (range.GetByteSize() <= 0)
return {};

if (!range.GetBaseAddress().IsValid())
return {};

lldb::DisassemblerSP disasm_sp =
Disassembler::FindPluginForTarget(target, arch, flavor, plugin_name);
lldb::DisassemblerSP disasm_sp = Disassembler::FindPluginForTarget(
target, arch, flavor, cpu, features, plugin_name);

if (!disasm_sp)
return {};
Expand All @@ -141,14 +147,15 @@ lldb::DisassemblerSP Disassembler::DisassembleRange(

lldb::DisassemblerSP
Disassembler::DisassembleBytes(const ArchSpec &arch, const char *plugin_name,
const char *flavor, const Address &start,
const char *flavor, const char *cpu,
const char *features, const Address &start,
const void *src, size_t src_len,
uint32_t num_instructions, bool data_from_file) {
if (!src)
return {};

lldb::DisassemblerSP disasm_sp =
Disassembler::FindPlugin(arch, flavor, plugin_name);
Disassembler::FindPlugin(arch, flavor, cpu, features, plugin_name);

if (!disasm_sp)
return {};
Expand All @@ -163,6 +170,7 @@ Disassembler::DisassembleBytes(const ArchSpec &arch, const char *plugin_name,

bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
const char *plugin_name, const char *flavor,
const char *cpu, const char *features,
const ExecutionContext &exe_ctx,
const Address &address, Limit limit,
bool mixed_source_and_assembly,
Expand All @@ -172,7 +180,7 @@ bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
return false;

lldb::DisassemblerSP disasm_sp(Disassembler::FindPluginForTarget(
exe_ctx.GetTargetRef(), arch, flavor, plugin_name));
exe_ctx.GetTargetRef(), arch, flavor, cpu, features, plugin_name));
if (!disasm_sp)
return false;

Expand Down Expand Up @@ -559,8 +567,8 @@ bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
if (limit.value == 0)
limit.value = DEFAULT_DISASM_BYTE_SIZE;

return Disassemble(debugger, arch, nullptr, nullptr, frame,
range.GetBaseAddress(), limit, false, 0, 0, strm);
return Disassemble(debugger, arch, nullptr, nullptr, nullptr, nullptr,
frame, range.GetBaseAddress(), limit, false, 0, 0, strm);
}

Instruction::Instruction(const Address &address, AddressClass addr_class)
Expand Down
Loading

0 comments on commit f109517

Please sign in to comment.