diff options
Diffstat (limited to 'lldb/source/Commands/CommandObjectRegister.cpp')
| -rw-r--r-- | lldb/source/Commands/CommandObjectRegister.cpp | 396 | 
1 files changed, 396 insertions, 0 deletions
| diff --git a/lldb/source/Commands/CommandObjectRegister.cpp b/lldb/source/Commands/CommandObjectRegister.cpp new file mode 100644 index 0000000000000..13266f8fce353 --- /dev/null +++ b/lldb/source/Commands/CommandObjectRegister.cpp @@ -0,0 +1,396 @@ +//===-- CommandObjectRegister.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 "CommandObjectRegister.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/DumpRegisterValue.h" +#include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueBoolean.h" +#include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/Args.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Scalar.h" +#include "llvm/Support/Errno.h" + +using namespace lldb; +using namespace lldb_private; + +// "register read" +#define LLDB_OPTIONS_register_read +#include "CommandOptions.inc" + +class CommandObjectRegisterRead : public CommandObjectParsed { +public: +  CommandObjectRegisterRead(CommandInterpreter &interpreter) +      : CommandObjectParsed( +            interpreter, "register read", +            "Dump the contents of one or more register values from the current " +            "frame.  If no register is specified, dumps them all.", +            nullptr, +            eCommandRequiresFrame | eCommandRequiresRegContext | +                eCommandProcessMustBeLaunched | eCommandProcessMustBePaused), +        m_option_group(), m_format_options(eFormatDefault), +        m_command_options() { +    CommandArgumentEntry arg; +    CommandArgumentData register_arg; + +    // Define the first (and only) variant of this arg. +    register_arg.arg_type = eArgTypeRegisterName; +    register_arg.arg_repetition = eArgRepeatStar; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg.push_back(register_arg); + +    // Push the data for the first argument into the m_arguments vector. +    m_arguments.push_back(arg); + +    // Add the "--format" +    m_option_group.Append(&m_format_options, +                          OptionGroupFormat::OPTION_GROUP_FORMAT | +                              OptionGroupFormat::OPTION_GROUP_GDB_FMT, +                          LLDB_OPT_SET_ALL); +    m_option_group.Append(&m_command_options); +    m_option_group.Finalize(); +  } + +  ~CommandObjectRegisterRead() override = default; + +  Options *GetOptions() override { return &m_option_group; } + +  bool DumpRegister(const ExecutionContext &exe_ctx, Stream &strm, +                    RegisterContext *reg_ctx, const RegisterInfo *reg_info) { +    if (reg_info) { +      RegisterValue reg_value; + +      if (reg_ctx->ReadRegister(reg_info, reg_value)) { +        strm.Indent(); + +        bool prefix_with_altname = (bool)m_command_options.alternate_name; +        bool prefix_with_name = !prefix_with_altname; +        DumpRegisterValue(reg_value, &strm, reg_info, prefix_with_name, +                          prefix_with_altname, m_format_options.GetFormat(), 8); +        if ((reg_info->encoding == eEncodingUint) || +            (reg_info->encoding == eEncodingSint)) { +          Process *process = exe_ctx.GetProcessPtr(); +          if (process && reg_info->byte_size == process->GetAddressByteSize()) { +            addr_t reg_addr = reg_value.GetAsUInt64(LLDB_INVALID_ADDRESS); +            if (reg_addr != LLDB_INVALID_ADDRESS) { +              Address so_reg_addr; +              if (exe_ctx.GetTargetRef() +                      .GetSectionLoadList() +                      .ResolveLoadAddress(reg_addr, so_reg_addr)) { +                strm.PutCString("  "); +                so_reg_addr.Dump(&strm, exe_ctx.GetBestExecutionContextScope(), +                                 Address::DumpStyleResolvedDescription); +              } +            } +          } +        } +        strm.EOL(); +        return true; +      } +    } +    return false; +  } + +  bool DumpRegisterSet(const ExecutionContext &exe_ctx, Stream &strm, +                       RegisterContext *reg_ctx, size_t set_idx, +                       bool primitive_only = false) { +    uint32_t unavailable_count = 0; +    uint32_t available_count = 0; + +    if (!reg_ctx) +      return false; // thread has no registers (i.e. core files are corrupt, +                    // incomplete crash logs...) + +    const RegisterSet *const reg_set = reg_ctx->GetRegisterSet(set_idx); +    if (reg_set) { +      strm.Printf("%s:\n", (reg_set->name ? reg_set->name : "unknown")); +      strm.IndentMore(); +      const size_t num_registers = reg_set->num_registers; +      for (size_t reg_idx = 0; reg_idx < num_registers; ++reg_idx) { +        const uint32_t reg = reg_set->registers[reg_idx]; +        const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg); +        // Skip the dumping of derived register if primitive_only is true. +        if (primitive_only && reg_info && reg_info->value_regs) +          continue; + +        if (DumpRegister(exe_ctx, strm, reg_ctx, reg_info)) +          ++available_count; +        else +          ++unavailable_count; +      } +      strm.IndentLess(); +      if (unavailable_count) { +        strm.Indent(); +        strm.Printf("%u registers were unavailable.\n", unavailable_count); +      } +      strm.EOL(); +    } +    return available_count > 0; +  } + +protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    Stream &strm = result.GetOutputStream(); +    RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext(); + +    const RegisterInfo *reg_info = nullptr; +    if (command.GetArgumentCount() == 0) { +      size_t set_idx; + +      size_t num_register_sets = 1; +      const size_t set_array_size = m_command_options.set_indexes.GetSize(); +      if (set_array_size > 0) { +        for (size_t i = 0; i < set_array_size; ++i) { +          set_idx = m_command_options.set_indexes[i]->GetUInt64Value(UINT32_MAX, +                                                                     nullptr); +          if (set_idx < reg_ctx->GetRegisterSetCount()) { +            if (!DumpRegisterSet(m_exe_ctx, strm, reg_ctx, set_idx)) { +              if (errno) +                result.AppendErrorWithFormatv("register read failed: {0}\n", +                                              llvm::sys::StrError()); +              else +                result.AppendError("unknown error while reading registers.\n"); +              result.SetStatus(eReturnStatusFailed); +              break; +            } +          } else { +            result.AppendErrorWithFormat( +                "invalid register set index: %" PRIu64 "\n", (uint64_t)set_idx); +            result.SetStatus(eReturnStatusFailed); +            break; +          } +        } +      } else { +        if (m_command_options.dump_all_sets) +          num_register_sets = reg_ctx->GetRegisterSetCount(); + +        for (set_idx = 0; set_idx < num_register_sets; ++set_idx) { +          // When dump_all_sets option is set, dump primitive as well as +          // derived registers. +          DumpRegisterSet(m_exe_ctx, strm, reg_ctx, set_idx, +                          !m_command_options.dump_all_sets.GetCurrentValue()); +        } +      } +    } else { +      if (m_command_options.dump_all_sets) { +        result.AppendError("the --all option can't be used when registers " +                           "names are supplied as arguments\n"); +        result.SetStatus(eReturnStatusFailed); +      } else if (m_command_options.set_indexes.GetSize() > 0) { +        result.AppendError("the --set <set> option can't be used when " +                           "registers names are supplied as arguments\n"); +        result.SetStatus(eReturnStatusFailed); +      } else { +        for (auto &entry : command) { +          // in most LLDB commands we accept $rbx as the name for register RBX +          // - and here we would reject it and non-existant. we should be more +          // consistent towards the user and allow them to say reg read $rbx - +          // internally, however, we should be strict and not allow ourselves +          // to call our registers $rbx in our own API +          auto arg_str = entry.ref(); +          arg_str.consume_front("$"); + +          reg_info = reg_ctx->GetRegisterInfoByName(arg_str); + +          if (reg_info) { +            if (!DumpRegister(m_exe_ctx, strm, reg_ctx, reg_info)) +              strm.Printf("%-12s = error: unavailable\n", reg_info->name); +          } else { +            result.AppendErrorWithFormat("Invalid register name '%s'.\n", +                                         arg_str.str().c_str()); +          } +        } +      } +    } +    return result.Succeeded(); +  } + +  class CommandOptions : public OptionGroup { +  public: +    CommandOptions() +        : OptionGroup(), +          set_indexes(OptionValue::ConvertTypeToMask(OptionValue::eTypeUInt64)), +          dump_all_sets(false, false), // Initial and default values are false +          alternate_name(false, false) {} + +    ~CommandOptions() override = default; + +    llvm::ArrayRef<OptionDefinition> GetDefinitions() override { +      return llvm::makeArrayRef(g_register_read_options); +    } + +    void OptionParsingStarting(ExecutionContext *execution_context) override { +      set_indexes.Clear(); +      dump_all_sets.Clear(); +      alternate_name.Clear(); +    } + +    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_value, +                          ExecutionContext *execution_context) override { +      Status error; +      const int short_option = GetDefinitions()[option_idx].short_option; +      switch (short_option) { +      case 's': { +        OptionValueSP value_sp(OptionValueUInt64::Create(option_value, error)); +        if (value_sp) +          set_indexes.AppendValue(value_sp); +      } break; + +      case 'a': +        // When we don't use OptionValue::SetValueFromCString(const char *) to +        // set an option value, it won't be marked as being set in the options +        // so we make a call to let users know the value was set via option +        dump_all_sets.SetCurrentValue(true); +        dump_all_sets.SetOptionWasSet(); +        break; + +      case 'A': +        // When we don't use OptionValue::SetValueFromCString(const char *) to +        // set an option value, it won't be marked as being set in the options +        // so we make a call to let users know the value was set via option +        alternate_name.SetCurrentValue(true); +        dump_all_sets.SetOptionWasSet(); +        break; + +      default: +        llvm_unreachable("Unimplemented option"); +      } +      return error; +    } + +    // Instance variables to hold the values for command options. +    OptionValueArray set_indexes; +    OptionValueBoolean dump_all_sets; +    OptionValueBoolean alternate_name; +  }; + +  OptionGroupOptions m_option_group; +  OptionGroupFormat m_format_options; +  CommandOptions m_command_options; +}; + +// "register write" +class CommandObjectRegisterWrite : public CommandObjectParsed { +public: +  CommandObjectRegisterWrite(CommandInterpreter &interpreter) +      : CommandObjectParsed(interpreter, "register write", +                            "Modify a single register value.", nullptr, +                            eCommandRequiresFrame | eCommandRequiresRegContext | +                                eCommandProcessMustBeLaunched | +                                eCommandProcessMustBePaused) { +    CommandArgumentEntry arg1; +    CommandArgumentEntry arg2; +    CommandArgumentData register_arg; +    CommandArgumentData value_arg; + +    // Define the first (and only) variant of this arg. +    register_arg.arg_type = eArgTypeRegisterName; +    register_arg.arg_repetition = eArgRepeatPlain; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg1.push_back(register_arg); + +    // Define the first (and only) variant of this arg. +    value_arg.arg_type = eArgTypeValue; +    value_arg.arg_repetition = eArgRepeatPlain; + +    // There is only one variant this argument could be; put it into the +    // argument entry. +    arg2.push_back(value_arg); + +    // Push the data for the first argument into the m_arguments vector. +    m_arguments.push_back(arg1); +    m_arguments.push_back(arg2); +  } + +  ~CommandObjectRegisterWrite() override = default; + +protected: +  bool DoExecute(Args &command, CommandReturnObject &result) override { +    DataExtractor reg_data; +    RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext(); + +    if (command.GetArgumentCount() != 2) { +      result.AppendError( +          "register write takes exactly 2 arguments: <reg-name> <value>"); +      result.SetStatus(eReturnStatusFailed); +    } else { +      auto reg_name = command[0].ref(); +      auto value_str = command[1].ref(); + +      // in most LLDB commands we accept $rbx as the name for register RBX - +      // and here we would reject it and non-existant. we should be more +      // consistent towards the user and allow them to say reg write $rbx - +      // internally, however, we should be strict and not allow ourselves to +      // call our registers $rbx in our own API +      reg_name.consume_front("$"); + +      const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); + +      if (reg_info) { +        RegisterValue reg_value; + +        Status error(reg_value.SetValueFromString(reg_info, value_str)); +        if (error.Success()) { +          if (reg_ctx->WriteRegister(reg_info, reg_value)) { +            // Toss all frames and anything else in the thread after a register +            // has been written. +            m_exe_ctx.GetThreadRef().Flush(); +            result.SetStatus(eReturnStatusSuccessFinishNoResult); +            return true; +          } +        } +        if (error.AsCString()) { +          result.AppendErrorWithFormat( +              "Failed to write register '%s' with value '%s': %s\n", +              reg_name.str().c_str(), value_str.str().c_str(), +              error.AsCString()); +        } else { +          result.AppendErrorWithFormat( +              "Failed to write register '%s' with value '%s'", +              reg_name.str().c_str(), value_str.str().c_str()); +        } +        result.SetStatus(eReturnStatusFailed); +      } else { +        result.AppendErrorWithFormat("Register not found for '%s'.\n", +                                     reg_name.str().c_str()); +        result.SetStatus(eReturnStatusFailed); +      } +    } +    return result.Succeeded(); +  } +}; + +// CommandObjectRegister constructor +CommandObjectRegister::CommandObjectRegister(CommandInterpreter &interpreter) +    : CommandObjectMultiword(interpreter, "register", +                             "Commands to access registers for the current " +                             "thread and stack frame.", +                             "register [read|write] ...") { +  LoadSubCommand("read", +                 CommandObjectSP(new CommandObjectRegisterRead(interpreter))); +  LoadSubCommand("write", +                 CommandObjectSP(new CommandObjectRegisterWrite(interpreter))); +} + +CommandObjectRegister::~CommandObjectRegister() = default; | 
