From 0ea68e81fa7aca2019c6d1e6b98d72502221d3e9 Mon Sep 17 00:00:00 2001 From: Owen Anderson Date: Fri, 27 Jun 2025 00:31:25 +0800 Subject: [PATCH] [CHERIoT] Add a "cheriot capability" memory/register formatter to LLDB. --- lldb/include/lldb/lldb-enumerations.h | 9 +- lldb/source/Commands/CommandObjectMemory.cpp | 1 + lldb/source/Core/DumpDataExtractor.cpp | 108 ++++++++++++++++++ lldb/source/DataFormatters/FormatManager.cpp | 1 + lldb/unittests/Core/DumpDataExtractorTest.cpp | 10 ++ 5 files changed, 125 insertions(+), 4 deletions(-) diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 50d2233509de6..53bcfc9710bde 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -195,11 +195,12 @@ enum Format { ///< character arrays that can contain non printable ///< characters eFormatAddressInfo, ///< Describe what an address points to (func + offset - ///< with file/line, symbol + offset, data, etc) - eFormatHexFloat, ///< ISO C99 hex float string - eFormatInstruction, ///< Disassemble an opcode - eFormatVoid, ///< Do not print this + ///< with file/line, symbol + offset, data, etc) + eFormatHexFloat, ///< ISO C99 hex float string + eFormatInstruction, ///< Disassemble an opcode + eFormatVoid, ///< Do not print this eFormatUnicode8, + eFormatCheriotCapability, kNumFormats }; diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp index f2fa5391f8f33..16c36709315c3 100644 --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -1354,6 +1354,7 @@ class CommandObjectMemoryWrite : public CommandObjectParsed { case eFormatAddressInfo: case eFormatHexFloat: case eFormatInstruction: + case eFormatCheriotCapability: case eFormatVoid: result.AppendError("unsupported format for writing memory"); return; diff --git a/lldb/source/Core/DumpDataExtractor.cpp b/lldb/source/Core/DumpDataExtractor.cpp index 72140736d8877..32532ab4680f4 100644 --- a/lldb/source/Core/DumpDataExtractor.cpp +++ b/lldb/source/Core/DumpDataExtractor.cpp @@ -413,6 +413,114 @@ lldb::offset_t lldb_private::DumpDataExtractor( } switch (item_format) { + case eFormatCheriotCapability: { + if (item_byte_size != 8) { + s->Printf("error: unsupported byte size (%" PRIu64 + ") for cheriot capability format", + (uint64_t)item_byte_size); + return offset; + } + + // Extract the raw bits of the capability. + uint64_t uval64 = DE.GetMaxU64(&offset, item_byte_size); + uint32_t addr = uval64; + uint32_t meta = (uval64 >> 32); + unsigned B = meta & 0x1FF; + unsigned T = (meta >> 9) & 0x1FF; + unsigned E = (meta >> 18) & 0xF; + unsigned otype = (meta >> 22) & 0x7; + std::bitset<6> perms = (meta >> 25) & 0x3F; + + bool GL = perms[5]; + bool LD = false; + bool MC = false; + bool SD = false; + bool SL = false; + bool LM = false; + bool LG = false; + bool SR = false; + bool EX = false; + bool U0 = false; + bool SE = false; + bool US = false; + + // Cheriot capabilities pack 11 permissions into 6 bits, + // so decoding is non-trivial. + if (perms[4] && perms[3]) { + LD = MC = SD = true; + SL = perms[2]; + LM = perms[1]; + LD = perms[0]; + } else if (perms[4] && !perms[3] && perms[2]) { + LD = MC = true; + LM = perms[1]; + LG = perms[0]; + } else if (perms[4] && !perms[3] && !perms[2] && !perms[1] && !perms[0]) { + SD = MC = true; + } else if (perms[4] && !perms[3] && !perms[2]) { + LD = perms[1]; + SD = perms[0]; + } else if (!perms[4] && perms[3]) { + EX = LD = MC = true; + SR = perms[2]; + LM = perms[1]; + LG = perms[0]; + } else { + U0 = perms[2]; + SE = perms[1]; + US = perms[0]; + } + + // Render the permissions to a string. + std::string perm_string; + perm_string += GL ? 'G' : '-'; + perm_string += ' '; + perm_string += LD ? 'R' : '-'; + perm_string += SD ? 'W' : '-'; + perm_string += MC ? 'c' : '-'; + perm_string += LG ? 'g' : '-'; + perm_string += LM ? 'm' : '-'; + perm_string += SL ? 'l' : '-'; + perm_string += ' '; + perm_string += EX ? 'X' : '-'; + perm_string += SR ? 'a' : '-'; + perm_string += ' '; + perm_string += SE ? 'S' : '-'; + perm_string += US ? 'U' : '-'; + perm_string += U0 ? '0' : '-'; + + // Decode otype, and present with a semantic string if possible. + if (otype != 0 && !EX) + otype += 8; + constexpr llvm::StringRef otypes[] = { + "[unsealed]", + "[IRQ inherit forward sentry]", + "[IRQ disable forward sentry]", + "[IRQ enable forward sentry]", + "[IRQ disable return sentry]", + "[IRQ enable return sentry]", + }; + llvm::StringRef otype_str = (otype < sizeof(otypes)) ? otypes[otype] : ""; + + // Compute the base and top addresses for the bounds. + unsigned e = (E != 15) ? E : 24; + uint64_t a_top = addr >> (e + 9); + uint64_t a_mid = (addr >> e) & 0x1FF; + uint64_t a_hi = (a_mid < B) ? 1 : 0; + uint64_t t_hi = (T < B) ? 1 : 0; + uint64_t c_b = -a_hi; + uint64_t c_t = t_hi - a_hi; + uint64_t a_top_base = a_top + c_b; + uint64_t a_top_top = a_top + c_t; + uint64_t base = ((a_top_base << 9) + B) << e; + uint64_t top = ((a_top_top << 9) + T) << e; + uint64_t len = top - base; + + s->Printf("0x%08x (v:? 0x%08llx-0x%08llx l:0x%llx o:0x%x%s p: %s)", addr, + base, top, len, otype, otype_str.data(), perm_string.c_str()); + + break; + } case eFormatBoolean: if (item_byte_size <= 8) s->Printf("%s", DE.GetMaxU64Bitfield(&offset, item_byte_size, diff --git a/lldb/source/DataFormatters/FormatManager.cpp b/lldb/source/DataFormatters/FormatManager.cpp index 3b891cecb1c8b..a268c7f25b1e1 100644 --- a/lldb/source/DataFormatters/FormatManager.cpp +++ b/lldb/source/DataFormatters/FormatManager.cpp @@ -72,6 +72,7 @@ static constexpr FormatInfo g_format_infos[] = { {eFormatInstruction, 'i', "instruction"}, {eFormatVoid, 'v', "void"}, {eFormatUnicode8, 'u', "unicode8"}, + {eFormatCheriotCapability, 'P', "cheriot capability"}, }; static_assert((sizeof(g_format_infos) / sizeof(g_format_infos[0])) == diff --git a/lldb/unittests/Core/DumpDataExtractorTest.cpp b/lldb/unittests/Core/DumpDataExtractorTest.cpp index 3d1e8bc5e4623..40ef00d28c0fd 100644 --- a/lldb/unittests/Core/DumpDataExtractorTest.cpp +++ b/lldb/unittests/Core/DumpDataExtractorTest.cpp @@ -272,6 +272,11 @@ TEST_F(DumpDataExtractorTest, Formats) { // Has no special handling, intended for use elsewhere. TestDump(99, lldb::Format::eFormatVoid, "0x00000063"); + + // Cheriot capability formatting + TestDump(0x7608d4cd00220b34, lldb::eFormatCheriotCapability, + "0x00220b34 (v:? 0x00220b34-0x002211a8 l:0x674 " + "o:0x0[unsealed] p: G RWc-m- -- ---)"); } TEST_F(DumpDataExtractorTest, FormatCharArray) { @@ -403,4 +408,9 @@ TEST_F(DumpDataExtractorTest, ItemByteSizeErrors) { TestDumpWithItemByteSize( 17, lldb::Format::eFormatHexFloat, "error: unsupported byte size (17) for hex float format"); + + // Cheriot capabilities must be 8 bytes. + TestDumpWithItemByteSize( + 17, lldb::Format::eFormatCheriotCapability, + "error: unsupported byte size (17) for cheriot capability format"); }