Author: Weining Lu Date: 2022-12-08T19:06:07+08:00 New Revision: 3a9e07b1e7f4718a0e117f3a732f1679c4bf2e30
URL: https://github.com/llvm/llvm-project/commit/3a9e07b1e7f4718a0e117f3a732f1679c4bf2e30 DIFF: https://github.com/llvm/llvm-project/commit/3a9e07b1e7f4718a0e117f3a732f1679c4bf2e30.diff LOG: [LLDB][LoongArch] Make software single stepping work 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 Added: lldb/source/Plugins/Instruction/LoongArch/CMakeLists.txt lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h Modified: lldb/source/Plugins/Instruction/CMakeLists.txt 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 Removed: ################################################################################ diff --git a/lldb/source/Plugins/Instruction/CMakeLists.txt b/lldb/source/Plugins/Instruction/CMakeLists.txt index 631c0b307cac..46d610f261e0 100644 --- a/lldb/source/Plugins/Instruction/CMakeLists.txt +++ b/lldb/source/Plugins/Instruction/CMakeLists.txt @@ -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 index 000000000000..59802ee8fa9a --- /dev/null +++ b/lldb/source/Plugins/Instruction/LoongArch/CMakeLists.txt @@ -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 index 000000000000..14ac993d9ac0 --- /dev/null +++ b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.cpp @@ -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 index 000000000000..06abc99d1ee9 --- /dev/null +++ b/lldb/source/Plugins/Instruction/LoongArch/EmulateInstructionLoongArch.h @@ -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 diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp index ffa15f4023db..5485d8c7f327 100644 --- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -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; } diff --git a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp index b8bf30f3b6de..6bf8a0dc28b2 100644 --- a/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp +++ b/lldb/source/Plugins/Process/Utility/NativeProcessSoftwareSingleStep.cpp @@ -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); diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt index 833563b5a0fe..67103e87a1d4 100644 --- a/lldb/tools/lldb-server/CMakeLists.txt +++ b/lldb/tools/lldb-server/CMakeLists.txt @@ -51,6 +51,7 @@ add_lldb_tool(lldb-server lldbVersion ${LLDB_PLUGINS} lldbPluginInstructionARM + lldbPluginInstructionLoongArch lldbPluginInstructionMIPS lldbPluginInstructionMIPS64 lldbPluginInstructionRISCV diff --git a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp index a8bdcdf0e77c..4233252a84df 100644 --- a/lldb/tools/lldb-server/SystemInitializerLLGS.cpp +++ b/lldb/tools/lldb-server/SystemInitializerLLGS.cpp @@ -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 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits