https://github.com/svs-quic created https://github.com/llvm/llvm-project/pull/207675
Add a minimal `RISC-V Architecture` plugin and implement `IsValidTrapInstruction` for trap opcodes. The plugin recognizes riscv32 and riscv64 targets and validates 32-bit `EBREAK` and 16-bit `C.EBREAK`. On RISC-V, `EBREAK` traps with the PC still pointing at the `EBREAK` instruction. A plain continue resumes from that same PC and traps again. This issue was found while trying to run the `TestBuiltinDebugTrap` test case locally on a `RISC-V QEMU`. Added a cpp unit test to verify the behaviour of `IsValidTrapInstruction`. I've chosen to verify `EBREAK` on `riscv32` and `C.EBREAK` on `riscv64` so that there is some coverage for both targets. Assisted by gpt-5.5 >From a921acec8a4e4b577066372ce3f8511d66d18bbe Mon Sep 17 00:00:00 2001 From: Sudharsan Veeravalli <[email protected]> Date: Mon, 6 Jul 2026 12:52:43 +0530 Subject: [PATCH] [RISCV][LLDB] Add RISC-V Architecture plugin for trap validation --- .../Plugins/Architecture/CMakeLists.txt | 1 + .../Architecture/RISCV/ArchitectureRISCV.cpp | 46 +++++++++++ .../Architecture/RISCV/ArchitectureRISCV.h | 36 +++++++++ .../Plugins/Architecture/RISCV/CMakeLists.txt | 7 ++ lldb/unittests/Architecture/CMakeLists.txt | 1 + .../RISCV/ArchitectureRISCVTest.cpp | 80 +++++++++++++++++++ .../Architecture/RISCV/CMakeLists.txt | 11 +++ lldb/unittests/CMakeLists.txt | 1 + 8 files changed, 183 insertions(+) create mode 100644 lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp create mode 100644 lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h create mode 100644 lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt create mode 100644 lldb/unittests/Architecture/CMakeLists.txt create mode 100644 lldb/unittests/Architecture/RISCV/ArchitectureRISCVTest.cpp create mode 100644 lldb/unittests/Architecture/RISCV/CMakeLists.txt diff --git a/lldb/source/Plugins/Architecture/CMakeLists.txt b/lldb/source/Plugins/Architecture/CMakeLists.txt index 0f898ef5116e9..93288ea5841db 100644 --- a/lldb/source/Plugins/Architecture/CMakeLists.txt +++ b/lldb/source/Plugins/Architecture/CMakeLists.txt @@ -4,3 +4,4 @@ add_subdirectory(Arm) add_subdirectory(Mips) add_subdirectory(PPC64) add_subdirectory(AArch64) +add_subdirectory(RISCV) diff --git a/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp new file mode 100644 index 0000000000000..f56a0712981a9 --- /dev/null +++ b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.cpp @@ -0,0 +1,46 @@ +//===-- ArchitectureRISCV.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 "Plugins/Architecture/RISCV/ArchitectureRISCV.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Utility/ArchSpec.h" + +#include <algorithm> + +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ArchitectureRISCV) + +void ArchitectureRISCV::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + "RISC-V-specific algorithms", + &ArchitectureRISCV::Create); +} + +void ArchitectureRISCV::Terminate() { + PluginManager::UnregisterPlugin(&ArchitectureRISCV::Create); +} + +std::unique_ptr<Architecture> ArchitectureRISCV::Create(const ArchSpec &arch) { + auto machine = arch.GetMachine(); + if (machine != llvm::Triple::riscv32 && machine != llvm::Triple::riscv64) + return nullptr; + return std::unique_ptr<Architecture>(new ArchitectureRISCV()); +} + +bool ArchitectureRISCV::IsValidTrapInstruction( + llvm::ArrayRef<uint8_t> reference, llvm::ArrayRef<uint8_t> observed) const { + // RISC-V has only two trap encodings here: 16-bit C.EBREAK or 32-bit EBREAK. + // These instructions don't have any operands so check that the reference and + // exact bytes match. + if ((reference.size() != 2 && reference.size() != 4) || + reference.size() > observed.size()) + return false; + + return std::equal(reference.begin(), reference.end(), observed.begin()); +} diff --git a/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h new file mode 100644 index 0000000000000..0a97411a25f76 --- /dev/null +++ b/lldb/source/Plugins/Architecture/RISCV/ArchitectureRISCV.h @@ -0,0 +1,36 @@ +//===-- ArchitectureRISCV.h -------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_ARCHITECTURE_RISCV_ARCHITECTURERISCV_H +#define LLDB_SOURCE_PLUGINS_ARCHITECTURE_RISCV_ARCHITECTURERISCV_H + +#include "lldb/Core/Architecture.h" + +namespace lldb_private { + +class ArchitectureRISCV : public Architecture { +public: + static llvm::StringRef GetPluginNameStatic() { return "riscv"; } + static void Initialize(); + static void Terminate(); + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + void OverrideStopInfo(Thread &thread) const override {} + + bool IsValidTrapInstruction(llvm::ArrayRef<uint8_t> reference, + llvm::ArrayRef<uint8_t> observed) const override; + +private: + static std::unique_ptr<Architecture> Create(const ArchSpec &arch); + ArchitectureRISCV() = default; +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_ARCHITECTURE_RISCV_ARCHITECTURERISCV_H diff --git a/lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt b/lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt new file mode 100644 index 0000000000000..443b948179e63 --- /dev/null +++ b/lldb/source/Plugins/Architecture/RISCV/CMakeLists.txt @@ -0,0 +1,7 @@ +add_lldb_library(lldbPluginArchitectureRISCV PLUGIN + ArchitectureRISCV.cpp + + LINK_LIBS + lldbCore + lldbUtility + ) diff --git a/lldb/unittests/Architecture/CMakeLists.txt b/lldb/unittests/Architecture/CMakeLists.txt new file mode 100644 index 0000000000000..4e66d7aaa260d --- /dev/null +++ b/lldb/unittests/Architecture/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(RISCV) diff --git a/lldb/unittests/Architecture/RISCV/ArchitectureRISCVTest.cpp b/lldb/unittests/Architecture/RISCV/ArchitectureRISCVTest.cpp new file mode 100644 index 0000000000000..619bc5696f861 --- /dev/null +++ b/lldb/unittests/Architecture/RISCV/ArchitectureRISCVTest.cpp @@ -0,0 +1,80 @@ +//===-- ArchitectureRISCVTest.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 "Plugins/Architecture/RISCV/ArchitectureRISCV.h" +#include "lldb/Core/Architecture.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Utility/ArchSpec.h" + +#include "gtest/gtest.h" + +using namespace lldb_private; + +class ArchitectureRISCVTest : public testing::Test { +protected: + static void SetUpTestSuite() { ArchitectureRISCV::Initialize(); } + static void TearDownTestSuite() { ArchitectureRISCV::Terminate(); } +}; + +TEST_F(ArchitectureRISCVTest, CreatesPluginForRISCVTargets) { + EXPECT_TRUE(PluginManager::CreateArchitectureInstance( + ArchSpec("riscv32-unknown-unknown-elf"))); + EXPECT_TRUE(PluginManager::CreateArchitectureInstance( + ArchSpec("riscv64-unknown-unknown-elf"))); + EXPECT_FALSE(PluginManager::CreateArchitectureInstance( + ArchSpec("x86_64-unknown-unknown-elf"))); +} + +TEST_F(ArchitectureRISCVTest, ValidatesEBreak) { + std::unique_ptr<Architecture> arch = + PluginManager::CreateArchitectureInstance( + ArchSpec("riscv32-unknown-unknown-elf")); + ASSERT_TRUE(arch); + + const uint8_t ebreak[] = {0x73, 0x00, 0x10, 0x00}; + const uint8_t ebreak_with_extra_bytes[] = {0x73, 0x00, 0x10, + 0x00, 0xff, 0xff}; + const uint8_t wrong_immediate[] = {0x73, 0x00, 0x20, 0x00}; + const uint8_t truncated_ebreak[] = {0x73, 0x00}; + const llvm::ArrayRef<uint8_t> empty_reference; + const uint8_t bad_size_reference[] = {0x73, 0x00, 0x10}; + + EXPECT_TRUE(arch->IsValidTrapInstruction(ebreak, ebreak)); + EXPECT_TRUE(arch->IsValidTrapInstruction(ebreak, ebreak_with_extra_bytes)); + EXPECT_FALSE(arch->IsValidTrapInstruction(ebreak, wrong_immediate)); + EXPECT_FALSE(arch->IsValidTrapInstruction(ebreak, truncated_ebreak)); + EXPECT_FALSE(arch->IsValidTrapInstruction(empty_reference, ebreak)); + EXPECT_FALSE(arch->IsValidTrapInstruction(bad_size_reference, ebreak)); +} + +TEST_F(ArchitectureRISCVTest, ValidatesCompressedEBreak) { + std::unique_ptr<Architecture> arch = + PluginManager::CreateArchitectureInstance( + ArchSpec("riscv64-unknown-unknown-elf")); + ASSERT_TRUE(arch); + + const uint8_t compressed_ebreak[] = {0x02, 0x90}; + const uint8_t compressed_ebreak_with_extra_bytes[] = {0x02, 0x90, 0xff, 0xff}; + const uint8_t compressed_unimp[] = {0x00, 0x00}; + const uint8_t truncated_compressed_ebreak[] = {0x02}; + const llvm::ArrayRef<uint8_t> empty_reference; + const uint8_t bad_size_reference[] = {0x02, 0x90, 0xff}; + + EXPECT_TRUE( + arch->IsValidTrapInstruction(compressed_ebreak, compressed_ebreak)); + EXPECT_TRUE(arch->IsValidTrapInstruction(compressed_ebreak, + compressed_ebreak_with_extra_bytes)); + EXPECT_FALSE( + arch->IsValidTrapInstruction(empty_reference, compressed_ebreak)); + EXPECT_FALSE( + arch->IsValidTrapInstruction(bad_size_reference, compressed_ebreak)); + EXPECT_FALSE( + arch->IsValidTrapInstruction(compressed_ebreak, compressed_unimp)); + EXPECT_FALSE(arch->IsValidTrapInstruction(compressed_ebreak, + truncated_compressed_ebreak)); +} diff --git a/lldb/unittests/Architecture/RISCV/CMakeLists.txt b/lldb/unittests/Architecture/RISCV/CMakeLists.txt new file mode 100644 index 0000000000000..f8e75bd423e13 --- /dev/null +++ b/lldb/unittests/Architecture/RISCV/CMakeLists.txt @@ -0,0 +1,11 @@ +if ("RISCV" IN_LIST LLVM_TARGETS_TO_BUILD) + add_lldb_unittest(ArchitectureRISCVTests + ArchitectureRISCVTest.cpp + + LINK_COMPONENTS + Support + LINK_LIBS + lldbCore + lldbPluginArchitectureRISCV + lldbUtility) +endif() diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt index 6ce3643bc65be..b0b7f68a7dcd6 100644 --- a/lldb/unittests/CMakeLists.txt +++ b/lldb/unittests/CMakeLists.txt @@ -77,6 +77,7 @@ if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") add_subdirectory(DAP) endif() add_subdirectory(ABI) +add_subdirectory(Architecture) add_subdirectory(Breakpoint) add_subdirectory(Callback) add_subdirectory(Core) _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
