[llvm] [readobj][AArch64] Parse AArch64 build attributes (PR #124276)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 27 05:04:51 PST 2025


https://github.com/sivan-shani updated https://github.com/llvm/llvm-project/pull/124276

>From ba91667361d0d6d2841634930d191c5b4920aba3 Mon Sep 17 00:00:00 2001
From: Sivan Shani <sivan.shani at arm.com>
Date: Thu, 23 Jan 2025 18:00:32 +0000
Subject: [PATCH] [readobj][AArch64] Parse AArch64 build attributes

Add support for parsing AArch64 build attributes in llvm-readobj
---
 llvm/include/llvm/Object/ELFObjectFile.h      |   3 +
 .../llvm/Support/AArch64AttributeParser.h     |  32 ++++
 .../include/llvm/Support/ELFAttributeParser.h |   4 +-
 llvm/lib/Support/AArch64AttributeParser.cpp   | 153 ++++++++++++++++++
 llvm/lib/Support/CMakeLists.txt               |   1 +
 llvm/lib/Support/ELFAttributeParser.cpp       |   5 +-
 .../aarch64-build-attributes-comprehensive.s  |  56 +++++++
 .../AArch64/aarch64-build-attributes-err.s    |   5 +
 llvm/tools/llvm-readobj/ELFDumper.cpp         |   7 +
 .../Support/ELFAttributeParserTest.cpp        |   3 +
 .../gn/secondary/llvm/lib/Support/BUILD.gn    |   1 +
 11 files changed, 266 insertions(+), 4 deletions(-)
 create mode 100644 llvm/include/llvm/Support/AArch64AttributeParser.h
 create mode 100644 llvm/lib/Support/AArch64AttributeParser.cpp
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-build-attributes-comprehensive.s
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-build-attributes-err.s

diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h
index 60c72062a3f6a1..bafc92cafe5395 100644
--- a/llvm/include/llvm/Object/ELFObjectFile.h
+++ b/llvm/include/llvm/Object/ELFObjectFile.h
@@ -410,6 +410,9 @@ template <class ELFT> class ELFObjectFile : public ELFObjectFileBase {
     case ELF::EM_ARM:
       Type = ELF::SHT_ARM_ATTRIBUTES;
       break;
+    case ELF::EM_AARCH64:
+      Type = ELF::SHT_AARCH64_ATTRIBUTES;
+      break;
     case ELF::EM_RISCV:
       Type = ELF::SHT_RISCV_ATTRIBUTES;
       break;
diff --git a/llvm/include/llvm/Support/AArch64AttributeParser.h b/llvm/include/llvm/Support/AArch64AttributeParser.h
new file mode 100644
index 00000000000000..655cf749d22d27
--- /dev/null
+++ b/llvm/include/llvm/Support/AArch64AttributeParser.h
@@ -0,0 +1,32 @@
+//=== - AArch64AttributeParser.h-AArch64 Attribute Information Printer - ===//
+//
+// 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 LLVM_SUPPORT_AARCH64ATTRIBUTEPARSER_H
+#define LLVM_SUPPORT_AARCH64ATTRIBUTEPARSER_H
+
+#include "ELFAttributeParser.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+
+class ScopedPrinter;
+
+class AArch64AttributeParser : public ELFAttributeParser {
+  Error handler(uint64_t Tag, bool &Handled) override {
+    return Error::success();
+  }
+
+public:
+  Error parse(ArrayRef<uint8_t> Section, llvm::endianness Endian) override;
+
+  AArch64AttributeParser(ScopedPrinter *Sw) : ELFAttributeParser(Sw) {}
+  AArch64AttributeParser() {}
+};
+} // namespace llvm
+
+#endif // LLVM_SUPPORT_AARCH64ATTRIBUTEPARSER_H
diff --git a/llvm/include/llvm/Support/ELFAttributeParser.h b/llvm/include/llvm/Support/ELFAttributeParser.h
index ffb92468fb37eb..25f4fd49042826 100644
--- a/llvm/include/llvm/Support/ELFAttributeParser.h
+++ b/llvm/include/llvm/Support/ELFAttributeParser.h
@@ -56,8 +56,10 @@ class ELFAttributeParser {
 
   ELFAttributeParser(TagNameMap tagNameMap, StringRef vendor)
       : vendor(vendor), sw(nullptr), tagToStringMap(tagNameMap) {}
+  ELFAttributeParser(ScopedPrinter *sw) : sw(sw) {}
+  ELFAttributeParser() {}
 
-  Error parse(ArrayRef<uint8_t> section, llvm::endianness endian);
+  virtual Error parse(ArrayRef<uint8_t> section, llvm::endianness endian);
 
   std::optional<unsigned> getAttributeValue(unsigned tag) const {
     auto I = attributes.find(tag);
diff --git a/llvm/lib/Support/AArch64AttributeParser.cpp b/llvm/lib/Support/AArch64AttributeParser.cpp
new file mode 100644
index 00000000000000..03e03e9f2af880
--- /dev/null
+++ b/llvm/lib/Support/AArch64AttributeParser.cpp
@@ -0,0 +1,153 @@
+//===-AArch64AttributeParser.cpp-AArch64 Attribute Information Printer-===//
+//
+// 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 "llvm/Support/AArch64AttributeParser.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/AArch64BuildAttributes.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstdint>
+
+namespace llvm {
+
+Error AArch64AttributeParser::parse(ArrayRef<uint8_t> Section,
+                                    llvm::endianness Endian) {
+
+  unsigned SectionNumber = 0;
+  de = DataExtractor(Section, Endian == llvm::endianness::little, 0);
+
+  // Early returns have specific errors. Consume the Error in cursor.
+  struct ClearCursorError {
+    DataExtractor::Cursor &Cursor;
+    ~ClearCursorError() { consumeError(Cursor.takeError()); }
+  } Clear{cursor};
+
+  /*
+    AArch64 build attributes layout:
+    <format-version: ‘A’> --> There is only one version, 'A' (0x41)
+    [ <uint32: subsection-length> <NTBS: vendor-name> <bytes: vendor-data> ]
+      --> subsection-length: the offset from the start of this subsection to the
+    start of the next one.
+      --> vendor-name: NUL-terminated byte string.
+      --> vendor-data expands to:
+        [ <uint8: optional> <uint8: parameter type> <attribute>*]
+          --> optional: 0- required, 1- optional
+          --> type: 0- ULEB128, 1- NTBS
+          --> attribute: <tag, value>* pair. Tag is ULEB128, value is <parameter
+    type> type.
+  */
+
+  // Get format-version
+  uint8_t FormatVersion = de.getU8(cursor);
+  if (ELFAttrs::Format_Version != FormatVersion)
+    return createStringError(errc::invalid_argument,
+                             "unrecognized format-version: 0x" +
+                                 utohexstr(FormatVersion));
+
+  while (!de.eof(cursor)) {
+    uint32_t BASubsectionLength = de.getU32(cursor);
+    // Minimal valid BA subsection header size is at least 8: length(4) name(at
+    // least a single char + null) optionality(1) and type(1)
+    if (BASubsectionLength < 8)
+      return createStringError(
+          errc::invalid_argument,
+          "invalid AArch64 build attribute subsection size at offset: " +
+              utohexstr(cursor.tell() - 4));
+
+    StringRef VendorName = de.getCStrRef(cursor);
+    // The layout of a private subsection (--> vendor name does not starts with
+    // 'aeabi') is unknown, skip)
+    if (!VendorName.starts_with("aeabi")) {
+      sw->startLine()
+          << "** Skipping private AArch64 build attributes subsection: "
+          << VendorName << "\n";
+      // Offset in Section
+      uint64_t OffsetInSection = cursor.tell();
+      // Size: 4 bytes, Vendor Name: VendorName.size() + 1 (null termination)
+      uint32_t BytesForLengthName = 4 + (VendorName.size() + 1);
+      cursor.seek(OffsetInSection + BASubsectionLength - BytesForLengthName);
+      continue;
+    }
+    // All public subsections names must be known
+    if (VendorName.starts_with("aeabi")) {
+      if (!("aeabi_feature_and_bits" == VendorName ||
+            "aeabi_pauthabi" == VendorName)) {
+        return createStringError(
+            errc::invalid_argument,
+            "unknown public AArch64 build attribute subsection name at "
+            "offset: " +
+                utohexstr(cursor.tell() - (VendorName.size() + 1)));
+      }
+    }
+
+    uint8_t IsOptional = de.getU8(cursor);
+    StringRef IsOptionalStr = IsOptional ? "optional" : "required";
+    uint8_t Type = de.getU8(cursor);
+    StringRef TypeStr = Type ? "ntbs" : "uleb128";
+
+    if (sw) {
+      sw->startLine() << "Section " << ++SectionNumber << " {\n";
+      sw->indent();
+      sw->printNumber("SectionLength", BASubsectionLength);
+      sw->startLine() << "VendorName" << ": " << VendorName
+                      << " Optionality: " << IsOptionalStr
+                      << " Type: " << TypeStr << "\n";
+      sw->startLine() << "Attributes {\n";
+      sw->indent();
+    }
+
+    // Offset in Section
+    uint64_t OffsetInSection = cursor.tell();
+    // Size: 4 bytes, Vendor Name: VendorName.size() + 1 (null termination),
+    // optionality: 1, size: 1
+    uint32_t BytesAllButAttributes = 4 + (VendorName.size() + 1) + 1 + 1;
+    while (cursor.tell() <
+           (OffsetInSection + BASubsectionLength - BytesAllButAttributes)) {
+
+      uint64_t Tag = de.getULEB128(cursor);
+      std::string Str = utostr(Tag);
+      StringRef TagStr(Str);
+      if ("aeabi_feature_and_bits" == VendorName) {
+        StringRef TagAsString =
+            AArch64BuildAttributes::getFeatureAndBitsTagsStr(Tag);
+        if ("" != TagAsString)
+          TagStr = TagAsString;
+      }
+      if ("aeabi_pauthabi" == VendorName) {
+        StringRef TagAsString = AArch64BuildAttributes::getPauthABITagsStr(Tag);
+        if ("" != TagAsString)
+          TagStr = TagAsString;
+      }
+
+      if (Type) { // type==1 --> ntbs
+        StringRef Value = de.getCStrRef(cursor);
+        if (sw)
+          sw->printString(TagStr, Value);
+      } else { // type==0 --> uleb128
+        uint64_t Value = de.getULEB128(cursor);
+        if (sw)
+          sw->printNumber(TagStr, Value);
+      }
+    }
+    if (sw) {
+      // Close 'Attributes'
+      sw->unindent();
+      sw->startLine() << "}\n";
+      // Close 'Section'
+      sw->unindent();
+      sw->startLine() << "}\n";
+    }
+  }
+
+  return cursor.takeError();
+}
+} // namespace llvm
\ No newline at end of file
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index 122240c27b1fcd..0be9f62c810a65 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -146,6 +146,7 @@ add_llvm_component_library(LLVMSupport
   ARMBuildAttrs.cpp
   AArch64BuildAttributes.cpp
   ARMAttributeParser.cpp
+  AArch64AttributeParser.cpp
   ARMWinEH.cpp
   Allocator.cpp
   AutoConvert.cpp
diff --git a/llvm/lib/Support/ELFAttributeParser.cpp b/llvm/lib/Support/ELFAttributeParser.cpp
index 26c3d54e17ade8..47055b6fda2f80 100644
--- a/llvm/lib/Support/ELFAttributeParser.cpp
+++ b/llvm/lib/Support/ELFAttributeParser.cpp
@@ -128,9 +128,8 @@ Error ELFAttributeParser::parseSubsection(uint32_t length) {
   }
 
   // Handle a subsection with an unrecognized vendor-name by skipping
-  // over it to the next subsection. ADDENDA32 in the Arm ABI defines
-  // that vendor attribute sections must not affect compatibility, so
-  // this should always be safe.
+  // over it to the next subsection. vendor attribute sections must not
+  // affect compatibility, so this should always be safe.
   if (vendorName.lower() != vendor) {
     cursor.seek(end);
     return Error::success();
diff --git a/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-build-attributes-comprehensive.s b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-build-attributes-comprehensive.s
new file mode 100644
index 00000000000000..253148ba93ca71
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-build-attributes-comprehensive.s
@@ -0,0 +1,56 @@
+# RUN: llvm-mc -triple=aarch64 -filetype=obj %s -o - | llvm-readelf --arch-specific - | FileCheck %s --check-prefix=ATTR
+
+# ATTR: BuildAttributes {
+# ATTR-NEXT:   FormatVersion: 0x41
+# ATTR-NEXT:  ** Skipping private AArch64 build attributes subsection: private_subsection_1
+# ATTR-NEXT:  Section 1 {
+# ATTR-NEXT:    SectionLength: 37
+# ATTR-NEXT:    VendorName: aeabi_feature_and_bits Optionality: optional Type: uleb128
+# ATTR-NEXT:    Attributes {
+# ATTR-NEXT:      Tag_Feature_BTI: 1
+# ATTR-NEXT:      Tag_Feature_PAC: 1
+# ATTR-NEXT:      Tag_Feature_GCS: 1
+# ATTR-NEXT:      3: 1
+# ATTR-NEXT:    }
+# ATTR-NEXT:  }
+# ATTR-NEXT:  ** Skipping private AArch64 build attributes subsection: private_subsection_3
+# ATTR-NEXT:  Section 2 {
+# ATTR-NEXT:    SectionLength: 35
+# ATTR-NEXT:    VendorName: aeabi_pauthabi Optionality: required Type: uleb128
+# ATTR-NEXT:    Attributes {
+# ATTR-NEXT:      Tag_PAuth_Schema: 1
+# ATTR-NEXT:      Tag_PAuth_Platform: 1
+# ATTR-NEXT:      5: 1
+# ATTR-NEXT:      6: 1
+# ATTR-NEXT:      7: 1
+# ATTR-NEXT:      8: 1
+# ATTR-NEXT:      9: 1
+# ATTR-NEXT:    }
+# ATTR-NEXT:  }
+# ATTR-NEXT:  ** Skipping private AArch64 build attributes subsection: private_subsection_4
+# ATTR-NEXT:  ** Skipping private AArch64 build attributes subsection: private_subsection_2
+# ATTR-NEXT: }
+
+
+.aeabi_subsection private_subsection_1, optional, uleb128
+.aeabi_attribute 1, 1
+.aeabi_subsection aeabi_feature_and_bits, optional, uleb128
+.aeabi_attribute Tag_Feature_BTI, 1
+.aeabi_attribute 1, 1
+.aeabi_attribute 2, 1
+.aeabi_attribute 3, 1
+.aeabi_subsection private_subsection_3, optional, ntbs
+.aeabi_attribute 1, "1"
+.aeabi_subsection aeabi_pauthabi, required, uleb128
+.aeabi_attribute Tag_PAuth_Schema, 1
+.aeabi_attribute Tag_PAuth_Platform, 1
+.aeabi_attribute 5, 1
+.aeabi_attribute 6, 1
+.aeabi_attribute 7, 1
+.aeabi_attribute 8, 1
+.aeabi_attribute 9, 1
+.aeabi_subsection private_subsection_4, required, ntbs
+.aeabi_attribute 1, "1"
+.aeabi_subsection private_subsection_2, required, uleb128
+.aeabi_attribute 1, 1
+.aeabi_attribute 2, 1
diff --git a/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-build-attributes-err.s b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-build-attributes-err.s
new file mode 100644
index 00000000000000..30502c79cf7a37
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/AArch64/aarch64-build-attributes-err.s
@@ -0,0 +1,5 @@
+# RUN: llvm-mc -triple=aarch64 -filetype=obj %s -o - | llvm-readelf --arch-specific - 2>&1 | FileCheck %s --check-prefix=ERR
+
+# ERR: unable to dump attributes from the Unknown section with index 3: unknown public AArch64 build attribute subsection name at offset: 5
+
+.aeabi_subsection aeabi_a, optional, uleb128
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index bfca65aad52b44..3ee9a15eb8b4a9 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -39,6 +39,7 @@
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Object/RelocationResolver.h"
 #include "llvm/Object/StackMapParser.h"
+#include "llvm/Support/AArch64AttributeParser.h"
 #include "llvm/Support/AMDGPUMetadata.h"
 #include "llvm/Support/ARMAttributeParser.h"
 #include "llvm/Support/ARMBuildAttributes.h"
@@ -2872,6 +2873,12 @@ template <class ELFT> void ELFDumper<ELFT>::printArchSpecificInfo() {
         ELF::SHT_ARM_ATTRIBUTES, std::make_unique<ARMAttributeParser>(&W),
         Obj.isLE() ? llvm::endianness::little : llvm::endianness::big);
     break;
+  case EM_AARCH64:
+    printAttributes(ELF::SHT_AARCH64_ATTRIBUTES,
+                    std::make_unique<AArch64AttributeParser>(&W),
+                    Obj.isLE() ? llvm::endianness::little
+                               : llvm::endianness::big);
+    break;
   case EM_RISCV:
     if (Obj.isLE())
       printAttributes(ELF::SHT_RISCV_ATTRIBUTES,
diff --git a/llvm/unittests/Support/ELFAttributeParserTest.cpp b/llvm/unittests/Support/ELFAttributeParserTest.cpp
index 38e7b09cc3c7d5..0e034814c4d856 100644
--- a/llvm/unittests/Support/ELFAttributeParserTest.cpp
+++ b/llvm/unittests/Support/ELFAttributeParserTest.cpp
@@ -27,6 +27,9 @@ class AttributeHeaderParser : public ELFAttributeParser {
   AttributeHeaderParser(ScopedPrinter *printer)
       : ELFAttributeParser(printer, emptyTagNameMap, "test") {}
   AttributeHeaderParser() : ELFAttributeParser(emptyTagNameMap, "test") {}
+  Error parse(ArrayRef<uint8_t> section, llvm::endianness endian) override {
+    return ELFAttributeParser::parse(section, endian);
+  }
 };
 
 static void testParseError(ArrayRef<uint8_t> bytes, const char *msg) {
diff --git a/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn
index 5146d4141f29b6..b6b67b830fe761 100644
--- a/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/lib/Support/BUILD.gn
@@ -41,6 +41,7 @@ static_library("Support") {
     "APInt.cpp",
     "APSInt.cpp",
     "ARMAttributeParser.cpp",
+    "AArch64AttributeParser.cpp",
     "ARMBuildAttrs.cpp",
     "ARMWinEH.cpp",
     "Allocator.cpp",



More information about the llvm-commits mailing list