[Lldb-commits] [lldb] [lldb] Add SubtargetFeatures to ArchSpec (PR #173046)

via lldb-commits lldb-commits at lists.llvm.org
Thu Feb 5 09:27:11 PST 2026


https://github.com/daniilavdeev updated https://github.com/llvm/llvm-project/pull/173046

>From 4f2ee91216d83ca8c0eb2c85496e2cbf7f9ca992 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniilavdeev237 at gmail.com>
Date: Tue, 18 Nov 2025 05:23:58 +0000
Subject: [PATCH 1/3] [lldb] Add SubtargetFeatures to ArchSpec

This patch introduces SubtargetFeatures in ArchSpec to enable consistent
handling of architecture-specific extensions in LLDB. For RISCV targets
in particular, it allows LLDB to propagate target extension features
from ELF attributes to expression evaluation and disassembly.
---
 lldb/include/lldb/Utility/ArchSpec.h          |  11 ++
 .../Plugins/ObjectFile/ELF/ObjectFileELF.cpp  | 121 ++++++++++++++++++
 .../Plugins/ObjectFile/ELF/ObjectFileELF.h    |   4 +
 3 files changed, 136 insertions(+)

diff --git a/lldb/include/lldb/Utility/ArchSpec.h b/lldb/include/lldb/Utility/ArchSpec.h
index 361108fd8f0e7..438a5e1faf86d 100644
--- a/lldb/include/lldb/Utility/ArchSpec.h
+++ b/lldb/include/lldb/Utility/ArchSpec.h
@@ -14,6 +14,7 @@
 #include "lldb/lldb-forward.h"
 #include "lldb/lldb-private-enumerations.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/TargetParser/SubtargetFeature.h"
 #include "llvm/TargetParser/Triple.h"
 #include <cstddef>
 #include <cstdint>
@@ -542,6 +543,14 @@ class ArchSpec {
 
   void SetFlags(const std::string &elf_abi);
 
+  const llvm::SubtargetFeatures &GetSubtargetFeatures() const {
+    return m_subtarget_features;
+  }
+
+  void SetSubtargetFeatures(llvm::SubtargetFeatures &&subtarget_features) {
+    m_subtarget_features = std::move(subtarget_features);
+  }
+
 protected:
   void UpdateCore();
 
@@ -553,6 +562,8 @@ class ArchSpec {
   // these are application specific extensions like micromips, mips16 etc.
   uint32_t m_flags = 0;
 
+  llvm::SubtargetFeatures m_subtarget_features;
+
   // Called when m_def or m_entry are changed.  Fills in all remaining members
   // with default values.
   void CoreUpdated(bool update_triple);
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index 7d783d5dd6b9f..00c5170f4a6d2 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -27,6 +27,7 @@
 #include "lldb/Target/Target.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
 #include "lldb/Utility/FileSpecList.h"
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/Log.h"
@@ -45,6 +46,9 @@
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/MipsABIFlags.h"
+#include "llvm/Support/RISCVAttributes.h"
+#include "llvm/TargetParser/RISCVISAInfo.h"
+#include "llvm/TargetParser/SubtargetFeature.h"
 
 #define CASE_AND_STREAM(s, def, width)                                         \
   case def:                                                                    \
@@ -1405,6 +1409,112 @@ void ObjectFileELF::ParseARMAttributes(DataExtractor &data, uint64_t length,
   }
 }
 
+static std::optional<lldb::offset_t>
+FindSubSectionOffsetByName(const DataExtractor &data, lldb::offset_t offset,
+                           uint32_t length, llvm::StringRef name) {
+  uint32_t section_length = 0;
+  llvm::StringRef section_name;
+  do {
+    offset += section_length;
+    // Sub-section's size and name are included in the total sub-section length.
+    // Don't shift the offset here, so it will point at the beginning of the
+    // sub-section and could be used as a return value.
+    auto tmp_offset = offset;
+    section_length = data.GetU32(&tmp_offset);
+    section_name = data.GetCStr(&tmp_offset);
+  } while (section_name != name && offset + section_length < length);
+  if (section_name == name)
+    return offset;
+  return std::nullopt;
+}
+
+static std::optional<lldb::offset_t>
+FindSubSubSectionOffsetByTag(const DataExtractor &data, lldb::offset_t offset,
+                             unsigned tag) {
+  // Consume a sub-section size and name to shift the offset at the beginning of
+  // the sub-sub-sections list.
+  auto upper_section_length = data.GetU32(&offset);
+  data.GetCStr(&offset);
+  auto upper_section_end_offset = offset + upper_section_length;
+
+  uint32_t section_length = 0;
+  unsigned section_tag = 0;
+  do {
+    offset += section_length;
+    // Similar to sub-section sub-sub-section's tag and size are included in the
+    // total sub-sub-section length.
+    auto tmp_offset = offset;
+    section_tag = data.GetULEB128(&tmp_offset);
+    section_length = data.GetU32(&tmp_offset);
+  } while (section_tag != tag &&
+           offset + section_length < upper_section_end_offset);
+  if (section_tag == tag)
+    return offset;
+  return std::nullopt;
+}
+
+static std::optional<std::variant<uint64_t, llvm::StringRef>>
+GetSubSubSubSectionValue(const DataExtractor &data, lldb::offset_t offset,
+                         unsigned tag) {
+  // Consume a sub-sub-section tag and size to shift the offset at the beginning
+  // of the sub-sub-sub-sections list.
+  data.GetULEB128(&offset);
+  auto upper_section_length = data.GetU32(&offset);
+  auto upper_section_end_offset = offset + upper_section_length;
+
+  std::variant<uint64_t, llvm::StringRef> result;
+  unsigned section_tag = 0;
+  do {
+    section_tag = data.GetULEB128(&offset);
+    // From the riscv psABI document:
+    // RISC-V attributes have a string value if the tag number is odd and an
+    // integer value if the tag number is even.
+    if (section_tag % 2)
+      result = data.GetCStr(&offset);
+    else
+      result = data.GetULEB128(&offset);
+  } while (section_tag != tag && offset < upper_section_end_offset);
+  if (section_tag == tag)
+    return result;
+  return std::nullopt;
+}
+
+void ObjectFileELF::ParseRISCVAttributes(DataExtractor &data, uint64_t length,
+                                         ArchSpec &arch_spec) {
+  lldb::offset_t offset = 0;
+
+  uint8_t format_version = data.GetU8(&offset);
+  if (format_version != llvm::ELFAttrs::Format_Version)
+    return;
+
+  auto subsection_or_opt =
+      FindSubSectionOffsetByName(data, offset, length, "riscv");
+  if (!subsection_or_opt.has_value()) {
+    // In fact 'riscv' sub-section is mandatory, so we shouldn't be here.
+    return;
+  }
+
+  auto subsubsection_or_opt = FindSubSubSectionOffsetByTag(
+      data, *subsection_or_opt, llvm::ELFAttrs::File);
+  if (!subsubsection_or_opt.has_value())
+    return;
+
+  auto value_or_opt = GetSubSubSubSectionValue(data, *subsubsection_or_opt,
+                                               llvm::RISCVAttrs::ARCH);
+  if (!value_or_opt.has_value())
+    return;
+
+  auto isa_info = llvm::RISCVISAInfo::parseArchString(
+      std::get<llvm::StringRef>(*value_or_opt),
+      /* EnableExperimentalExtension=*/true);
+  if (llvm::errorToBool(isa_info.takeError()))
+    return;
+
+  llvm::SubtargetFeatures features;
+  features.addFeaturesVector((*isa_info)->toFeatures());
+  arch_spec.SetSubtargetFeatures(std::move(features));
+}
+
 // GetSectionHeaderInfo
 size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
                                            DataExtractor &object_data,
@@ -1622,6 +1732,17 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
             ParseARMAttributes(data, section_size, arch_spec);
         }
 
+        if (arch_spec.GetMachine() == llvm::Triple::riscv32 ||
+            arch_spec.GetMachine() == llvm::Triple::riscv64) {
+          DataExtractor data;
+
+          if (sheader.sh_type == llvm::ELF::SHT_RISCV_ATTRIBUTES &&
+              section_size != 0 &&
+              data.SetData(object_data, sheader.sh_offset, section_size) ==
+                  section_size)
+            ParseRISCVAttributes(data, section_size, arch_spec);
+        }
+
         if (name == g_sect_name_gnu_debuglink) {
           DataExtractor data;
           if (section_size && (data.SetData(object_data, sheader.sh_offset,
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
index 2927e6969230c..a98f6604dfe39 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
@@ -275,6 +275,10 @@ class ObjectFileELF : public lldb_private::ObjectFile {
                                  uint64_t length,
                                  lldb_private::ArchSpec &arch_spec);
 
+  static void ParseRISCVAttributes(lldb_private::DataExtractor &data,
+                                   uint64_t length,
+                                   lldb_private::ArchSpec &arch_spec);
+
   /// Parses the elf section headers and returns the uuid, debug link name,
   /// crc, archspec.
   static size_t GetSectionHeaderInfo(SectionHeaderColl &section_headers,

>From 941529fe827c4fb06311ad0220fc36cddb41c425 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniilavdeev237 at gmail.com>
Date: Fri, 16 Jan 2026 09:02:13 +0000
Subject: [PATCH 2/3] fix

---
 .../Plugins/ObjectFile/ELF/ObjectFileELF.cpp    | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index 00c5170f4a6d2..f07e08ba95779 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -1481,6 +1481,8 @@ GetSubSubSubSectionValue(const DataExtractor &data, lldb::offset_t offset,
 
 void ObjectFileELF::ParseRISCVAttributes(DataExtractor &data, uint64_t length,
                                          ArchSpec &arch_spec) {
+  Log *log = GetLog(LLDBLog::Modules);
+
   lldb::offset_t offset = 0;
 
   uint8_t format_version = data.GetU8(&offset);
@@ -1489,19 +1491,22 @@ void ObjectFileELF::ParseRISCVAttributes(DataExtractor &data, uint64_t length,
 
   auto subsection_or_opt =
       FindSubSectionOffsetByName(data, offset, length, "riscv");
-  if (!subsection_or_opt.has_value()) {
-    // In fact 'riscv' sub-section is mandatory, so we shouldn't be here.
+  if (!subsection_or_opt) {
+    LLDB_LOGF(log,
+              "ObjectFileELF::%s Ill-formed .riscv.attributes section: "
+              "mandatory 'riscv' sub-section was not preserved",
+              __FUNCTION__);
     return;
   }
 
   auto subsubsection_or_opt = FindSubSubSectionOffsetByTag(
       data, *subsection_or_opt, llvm::ELFAttrs::File);
-  if (!subsubsection_or_opt.has_value())
+  if (!subsubsection_or_opt)
     return;
 
   auto value_or_opt = GetSubSubSubSectionValue(data, *subsubsection_or_opt,
                                                llvm::RISCVAttrs::ARCH);
-  if (!value_or_opt.has_value())
+  if (!value_or_opt)
     return;
 
   auto isa_info = llvm::RISCVISAInfo::parseArchString(
@@ -1732,10 +1737,8 @@ size_t ObjectFileELF::GetSectionHeaderInfo(SectionHeaderColl &section_headers,
             ParseARMAttributes(data, section_size, arch_spec);
         }
 
-        if (arch_spec.GetMachine() == llvm::Triple::riscv32 ||
-            arch_spec.GetMachine() == llvm::Triple::riscv64) {
+        if (arch_spec.GetTriple().isRISCV()) {
           DataExtractor data;
-
           if (sheader.sh_type == llvm::ELF::SHT_RISCV_ATTRIBUTES &&
               section_size != 0 &&
               data.SetData(object_data, sheader.sh_offset, section_size) ==

>From 977d5d9d167dabdde8e1a5d82a08d64ec69c2fdb Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniilavdeev237 at gmail.com>
Date: Thu, 5 Feb 2026 17:26:19 +0000
Subject: [PATCH 3/3] fix

---
 .../Plugins/ObjectFile/ELF/ObjectFileELF.cpp  | 77 +++++++++++++++----
 1 file changed, 62 insertions(+), 15 deletions(-)

diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index f07e08ba95779..f284641323536 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -1423,8 +1423,10 @@ FindSubSectionOffsetByName(const DataExtractor &data, lldb::offset_t offset,
     section_length = data.GetU32(&tmp_offset);
     section_name = data.GetCStr(&tmp_offset);
   } while (section_name != name && offset + section_length < length);
+
   if (section_name == name)
     return offset;
+
   return std::nullopt;
 }
 
@@ -1433,9 +1435,9 @@ FindSubSubSectionOffsetByTag(const DataExtractor &data, lldb::offset_t offset,
                              unsigned tag) {
   // Consume a sub-section size and name to shift the offset at the beginning of
   // the sub-sub-sections list.
-  auto upper_section_length = data.GetU32(&offset);
+  auto parent_section_length = data.GetU32(&offset);
   data.GetCStr(&offset);
-  auto upper_section_end_offset = offset + upper_section_length;
+  auto parent_section_end_offset = offset + parent_section_length;
 
   uint32_t section_length = 0;
   unsigned section_tag = 0;
@@ -1447,35 +1449,39 @@ FindSubSubSectionOffsetByTag(const DataExtractor &data, lldb::offset_t offset,
     section_tag = data.GetULEB128(&tmp_offset);
     section_length = data.GetU32(&tmp_offset);
   } while (section_tag != tag &&
-           offset + section_length < upper_section_end_offset);
+           offset + section_length < parent_section_end_offset);
+
   if (section_tag == tag)
     return offset;
+
   return std::nullopt;
 }
 
 static std::optional<std::variant<uint64_t, llvm::StringRef>>
-GetSubSubSubSectionValue(const DataExtractor &data, lldb::offset_t offset,
-                         unsigned tag) {
+GetAttributeValueByTag(const DataExtractor &data, lldb::offset_t offset,
+                       unsigned tag) {
   // Consume a sub-sub-section tag and size to shift the offset at the beginning
-  // of the sub-sub-sub-sections list.
+  // of the attribute list.
   data.GetULEB128(&offset);
-  auto upper_section_length = data.GetU32(&offset);
-  auto upper_section_end_offset = offset + upper_section_length;
+  auto parent_section_length = data.GetU32(&offset);
+  auto parent_section_end_offset = offset + parent_section_length;
 
   std::variant<uint64_t, llvm::StringRef> result;
-  unsigned section_tag = 0;
+  unsigned attribute_tag = 0;
   do {
-    section_tag = data.GetULEB128(&offset);
+    attribute_tag = data.GetULEB128(&offset);
     // From the riscv psABI document:
     // RISC-V attributes have a string value if the tag number is odd and an
     // integer value if the tag number is even.
-    if (section_tag % 2)
+    if (attribute_tag % 2)
       result = data.GetCStr(&offset);
     else
       result = data.GetULEB128(&offset);
-  } while (section_tag != tag && offset < upper_section_end_offset);
-  if (section_tag == tag)
+  } while (attribute_tag != tag && offset < parent_section_end_offset);
+
+  if (attribute_tag == tag)
     return result;
+
   return std::nullopt;
 }
 
@@ -1485,6 +1491,47 @@ void ObjectFileELF::ParseRISCVAttributes(DataExtractor &data, uint64_t length,
 
   lldb::offset_t offset = 0;
 
+  // According to the riscv psABI, the .riscv.attributes section has the
+  // following hierarchical structure:
+  //
+  // Section:
+  //   .riscv.attributes {
+  //       - (uint8_t) format
+  //       - Sub-Section 1 {
+  //           * (uint32_t) length
+  //           * (c_str) name
+  //           * Sub-Sub-Section 1.1 {
+  //               > (uleb128_t) tag
+  //               > (uint32_t) length
+  //               > (uleb128_t) attribute_tag_1.1.1
+  //                   $ (c_str or uleb128_t) value
+  //               > (uleb128_t) attribute_tag_1.1.2
+  //                   $ (c_str or uleb128_t) value
+  //               ...
+  //               Other attributes...
+  //               ...
+  //               > (uleb128_t) attribute_tag_1.1.N
+  //                   $ (c_str or uleb128_t) value
+  //           }
+  //           * Sub-Sub-Section 1.2 {
+  //               ...
+  //               Sub-Sub-Section structure...
+  //               ...
+  //           }
+  //           ...
+  //           Other sub-sub-sections...
+  //           ...
+  //       }
+  //       - Sub-Section 2 {
+  //           ...
+  //           Sub-Section structure...
+  //           ...
+  //       }
+  //       ...
+  //       Other sub-sections...
+  //       ...
+  //   }
+
   uint8_t format_version = data.GetU8(&offset);
   if (format_version != llvm::ELFAttrs::Format_Version)
     return;
@@ -1504,8 +1551,8 @@ void ObjectFileELF::ParseRISCVAttributes(DataExtractor &data, uint64_t length,
   if (!subsubsection_or_opt)
     return;
 
-  auto value_or_opt = GetSubSubSubSectionValue(data, *subsubsection_or_opt,
-                                               llvm::RISCVAttrs::ARCH);
+  auto value_or_opt = GetAttributeValueByTag(data, *subsubsection_or_opt,
+                                             llvm::RISCVAttrs::ARCH);
   if (!value_or_opt)
     return;
 



More information about the lldb-commits mailing list