[LLDB][LoongArch] Make software single stepping work
authorHui Li <lihui@loongson.cn>
Thu, 8 Dec 2022 11:59:11 +0000 (19:59 +0800)
committerWeining Lu <luweining@loongson.cn>
Thu, 8 Dec 2022 11:59:39 +0000 (19:59 +0800)
Hardware single stepping is not currently supported by the linux kernel.
In order to support single step debugging, add EmulateInstructionLoongArch
to implement the software Single Stepping. This patch only support the
simplest single step execution of non-jump instructions.

Reviewed By: SixWeining, DavidSpickett

Differential Revision: https://reviews.llvm.org/D139158

lldb/source/Plugins/Instruction/CMakeLists.txt
lldb/source/Plugins/Instruction/LoongArch/CMakeLists.txt [new file with mode: 0644]
lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp [new file with mode: 0644]
lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h [new file with mode: 0644]
lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp
lldb/tools/lldb-server/CMakeLists.txt
lldb/tools/lldb-server/SystemInitializerLLGS.cpp

index 631c0b3..46d610f 100644 (file)
@@ -1,5 +1,6 @@
 add_subdirectory(ARM)
 add_subdirectory(ARM64)
+add_subdirectory(LoongArch)
 add_subdirectory(MIPS)
 add_subdirectory(MIPS64)
 add_subdirectory(PPC64)
diff --git a/lldb/source/Plugins/Instruction/LoongArch/CMakeLists.txt b/lldb/source/Plugins/Instruction/LoongArch/CMakeLists.txt
new file mode 100644 (file)
index 0000000..59802ee
--- /dev/null
@@ -0,0 +1,11 @@
+add_lldb_library(lldbPluginInstructionLoongArch PLUGIN
+  EmulateInstructionLoongArch.cpp
+
+  LINK_LIBS
+    lldbCore
+    lldbInterpreter
+    lldbPluginProcessUtility
+    lldbSymbol
+  LINK_COMPONENTS
+    Support
+  )
diff --git a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp
new file mode 100644 (file)
index 0000000..14ac993
--- /dev/null
@@ -0,0 +1,181 @@
+//===---EmulateInstructionLoongArch.cpp------------------------------------===//
+//
+// 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 <cstdlib>
+
+#include "EmulateInstructionLoongArch.h"
+#include "Plugins/Process/Utility/InstructionUtils.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_loongarch64.h"
+#include "Plugins/Process/Utility/lldb-loongarch-register-enums.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Interpreter/OptionValueArray.h"
+#include "lldb/Interpreter/OptionValueDictionary.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/Stream.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/MathExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE_ADV(EmulateInstructionLoongArch, InstructionLoongArch)
+
+namespace lldb_private {
+
+EmulateInstructionLoongArch::Opcode *
+EmulateInstructionLoongArch::GetOpcodeForInstruction(uint32_t inst) {
+  // TODO: Add the mask of jump instruction.
+  static EmulateInstructionLoongArch::Opcode g_opcodes[] = {
+      {0x00000000, 0x00000000, &EmulateInstructionLoongArch::EmulateNonJMP,
+       "NonJMP"}};
+  static const size_t num_loongarch_opcodes = std::size(g_opcodes);
+
+  for (size_t i = 0; i < num_loongarch_opcodes; ++i)
+    if ((g_opcodes[i].mask & inst) == g_opcodes[i].value)
+      return &g_opcodes[i];
+  return nullptr;
+}
+
+bool EmulateInstructionLoongArch::EvaluateInstruction(uint32_t options) {
+  uint32_t inst_size = m_opcode.GetByteSize();
+  uint32_t inst = m_opcode.GetOpcode32();
+  bool increase_pc = options & eEmulateInstructionOptionAutoAdvancePC;
+  bool success = false;
+
+  Opcode *opcode_data = GetOpcodeForInstruction(inst);
+  if (!opcode_data)
+    return false;
+
+  lldb::addr_t old_pc = 0;
+  if (increase_pc) {
+    old_pc = ReadPC(&success);
+    if (!success)
+      return false;
+  }
+
+  // Call the Emulate... function.
+  if (!(this->*opcode_data->callback)(inst))
+    return false;
+
+  if (increase_pc) {
+    lldb::addr_t new_pc = ReadPC(&success);
+    if (!success)
+      return false;
+
+    if (new_pc == old_pc && !WritePC(old_pc + inst_size))
+      return false;
+  }
+  return true;
+}
+
+bool EmulateInstructionLoongArch::ReadInstruction() {
+  bool success = false;
+  m_addr = ReadPC(&success);
+  if (!success) {
+    m_addr = LLDB_INVALID_ADDRESS;
+    return false;
+  }
+
+  Context ctx;
+  ctx.type = eContextReadOpcode;
+  ctx.SetNoArgs();
+  uint32_t inst = (uint32_t)ReadMemoryUnsigned(ctx, m_addr, 4, 0, &success);
+  m_opcode.SetOpcode32(inst, GetByteOrder());
+
+  return true;
+}
+
+lldb::addr_t EmulateInstructionLoongArch::ReadPC(bool *success) {
+  return ReadRegisterUnsigned(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC,
+                              LLDB_INVALID_ADDRESS, success);
+}
+
+bool EmulateInstructionLoongArch::WritePC(lldb::addr_t pc) {
+  EmulateInstruction::Context ctx;
+  ctx.type = eContextAdvancePC;
+  ctx.SetNoArgs();
+  return WriteRegisterUnsigned(ctx, eRegisterKindGeneric,
+                               LLDB_REGNUM_GENERIC_PC, pc);
+}
+
+llvm::Optional<RegisterInfo>
+EmulateInstructionLoongArch::GetRegisterInfo(lldb::RegisterKind reg_kind,
+                                             uint32_t reg_index) {
+  if (reg_kind == eRegisterKindGeneric) {
+    switch (reg_index) {
+    case LLDB_REGNUM_GENERIC_PC:
+      reg_kind = eRegisterKindLLDB;
+      reg_index = gpr_pc_loongarch;
+      break;
+    case LLDB_REGNUM_GENERIC_SP:
+      reg_kind = eRegisterKindLLDB;
+      reg_index = gpr_sp_loongarch;
+      break;
+    case LLDB_REGNUM_GENERIC_FP:
+      reg_kind = eRegisterKindLLDB;
+      reg_index = gpr_fp_loongarch;
+      break;
+    case LLDB_REGNUM_GENERIC_RA:
+      reg_kind = eRegisterKindLLDB;
+      reg_index = gpr_ra_loongarch;
+      break;
+    // We may handle LLDB_REGNUM_GENERIC_ARGx when more instructions are
+    // supported.
+    default:
+      llvm_unreachable("unsupported register");
+    }
+  }
+
+  const RegisterInfo *array =
+      RegisterInfoPOSIX_loongarch64::GetRegisterInfoPtr(m_arch);
+  const uint32_t length =
+      RegisterInfoPOSIX_loongarch64::GetRegisterInfoCount(m_arch);
+
+  if (reg_index >= length || reg_kind != eRegisterKindLLDB)
+    return {};
+  return array[reg_index];
+}
+
+bool EmulateInstructionLoongArch::SetTargetTriple(const ArchSpec &arch) {
+  return SupportsThisArch(arch);
+}
+
+bool EmulateInstructionLoongArch::TestEmulation(
+    Stream *out_stream, ArchSpec &arch, OptionValueDictionary *test_data) {
+  return false;
+}
+
+void EmulateInstructionLoongArch::Initialize() {
+  PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void EmulateInstructionLoongArch::Terminate() {
+  PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb_private::EmulateInstruction *
+EmulateInstructionLoongArch::CreateInstance(const ArchSpec &arch,
+                                            InstructionType inst_type) {
+  if (EmulateInstructionLoongArch::SupportsThisInstructionType(inst_type) &&
+      SupportsThisArch(arch))
+    return new EmulateInstructionLoongArch(arch);
+  return nullptr;
+}
+
+bool EmulateInstructionLoongArch::SupportsThisArch(const ArchSpec &arch) {
+  return arch.GetTriple().isLoongArch();
+}
+
+bool EmulateInstructionLoongArch::EmulateNonJMP(uint32_t inst) { return false; }
+
+} // namespace lldb_private
diff --git a/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h
new file mode 100644 (file)
index 0000000..06abc99
--- /dev/null
@@ -0,0 +1,76 @@
+//===---EmulateInstructionLoongArch.h--------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_INSTRUCTION_LOONGARCH_EMULATEINSTRUCTIONLOONGARCH_H
+#define LLDB_SOURCE_PLUGINS_INSTRUCTION_LOONGARCH_EMULATEINSTRUCTIONLOONGARCH_H
+
+#include "lldb/Core/EmulateInstruction.h"
+#include "lldb/Interpreter/OptionValue.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Status.h"
+
+namespace lldb_private {
+
+class EmulateInstructionLoongArch : public EmulateInstruction {
+public:
+  static llvm::StringRef GetPluginNameStatic() { return "LoongArch"; }
+
+  static llvm::StringRef GetPluginDescriptionStatic() {
+    return "Emulate instructions for the LoongArch architecture.";
+  }
+
+  static bool SupportsThisInstructionType(InstructionType inst_type) {
+    return inst_type == eInstructionTypePCModifying;
+  }
+
+  static bool SupportsThisArch(const ArchSpec &arch);
+
+  static lldb_private::EmulateInstruction *
+  CreateInstance(const lldb_private::ArchSpec &arch, InstructionType inst_type);
+
+  static void Initialize();
+
+  static void Terminate();
+
+public:
+  EmulateInstructionLoongArch(const ArchSpec &arch)
+      : EmulateInstruction(arch) {}
+
+  llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+  bool SupportsEmulatingInstructionsOfType(InstructionType inst_type) override {
+    return SupportsThisInstructionType(inst_type);
+  }
+
+  bool SetTargetTriple(const ArchSpec &arch) override;
+  bool ReadInstruction() override;
+  bool EvaluateInstruction(uint32_t options) override;
+  bool TestEmulation(Stream *out_stream, ArchSpec &arch,
+                     OptionValueDictionary *test_data) override;
+
+  llvm::Optional<RegisterInfo> GetRegisterInfo(lldb::RegisterKind reg_kind,
+                                               uint32_t reg_num) override;
+  lldb::addr_t ReadPC(bool *success);
+  bool WritePC(lldb::addr_t pc);
+
+private:
+  struct Opcode {
+    uint32_t mask;
+    uint32_t value;
+    bool (EmulateInstructionLoongArch::*callback)(uint32_t opcode);
+    const char *name;
+  };
+
+  Opcode *GetOpcodeForInstruction(uint32_t inst);
+
+  bool EmulateNonJMP(uint32_t inst);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_INSTRUCTION_LOONGARCH_EMULATEINSTRUCTIONLOONGARCH_H
index ffa15f4..5485d8c 100644 (file)
@@ -883,7 +883,7 @@ bool NativeProcessLinux::MonitorClone(NativeThreadLinux &parent,
 
 bool NativeProcessLinux::SupportHardwareSingleStepping() const {
   if (m_arch.IsMIPS() || m_arch.GetMachine() == llvm::Triple::arm ||
-      m_arch.GetTriple().isRISCV())
+      m_arch.GetTriple().isRISCV() || m_arch.GetTriple().isLoongArch())
     return false;
   return true;
 }
index b8bf30f..6bf8a0d 100644 (file)
@@ -168,7 +168,7 @@ Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping(
       size_hint = 4;
     }
   } else if (arch.IsMIPS() || arch.GetTriple().isPPC64() ||
-             arch.GetTriple().isRISCV())
+             arch.GetTriple().isRISCV() || arch.GetTriple().isLoongArch())
     size_hint = 4;
   error = process.SetBreakpoint(next_pc, size_hint, /*hardware=*/false);
 
index 833563b..67103e8 100644 (file)
@@ -51,6 +51,7 @@ add_lldb_tool(lldb-server
       lldbVersion
       ${LLDB_PLUGINS}
       lldbPluginInstructionARM
+      lldbPluginInstructionLoongArch
       lldbPluginInstructionMIPS
       lldbPluginInstructionMIPS64
       lldbPluginInstructionRISCV
index a8bdcdf..4233252 100644 (file)
@@ -29,6 +29,11 @@ using HostObjectFile = ObjectFileELF;
 #include "Plugins/Instruction/ARM/EmulateInstructionARM.h"
 #endif
 
+#if defined(__loongarch__)
+#define LLDB_TARGET_LoongArch
+#include "Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h"
+#endif
+
 #if defined(__mips64__) || defined(mips64) || defined(__mips64) ||             \
     defined(__MIPS64__) || defined(_M_MIPS64)
 #define LLDB_TARGET_MIPS64
@@ -57,6 +62,9 @@ llvm::Error SystemInitializerLLGS::Initialize() {
 #if defined(LLDB_TARGET_ARM) || defined(LLDB_TARGET_ARM64)
   EmulateInstructionARM::Initialize();
 #endif
+#if defined(LLDB_TARGET_LoongArch)
+  EmulateInstructionLoongArch::Initialize();
+#endif
 #if defined(LLDB_TARGET_MIPS) || defined(LLDB_TARGET_MIPS64)
   EmulateInstructionMIPS::Initialize();
 #endif
@@ -76,6 +84,9 @@ void SystemInitializerLLGS::Terminate() {
 #if defined(LLDB_TARGET_ARM) || defined(LLDB_TARGET_ARM64)
   EmulateInstructionARM::Terminate();
 #endif
+#if defined(LLDB_TARGET_LoongArch)
+  EmulateInstructionLoongArch::Terminate();
+#endif
 #if defined(LLDB_TARGET_MIPS) || defined(LLDB_TARGET_MIPS64)
   EmulateInstructionMIPS::Terminate();
 #endif