Skip to content

Conversation

@androm3da
Copy link
Member

@androm3da androm3da commented Jul 11, 2025

Merge the attributes of object files being linked together. The .hexagon.attributes section can be used by loaders and analysis tools. This is similar to the .riscv.attributes, introduced in 8a900f2 / https://reviews.llvm.org/D138550.

@llvmbot
Copy link
Member

llvmbot commented Jul 11, 2025

@llvm/pr-subscribers-backend-hexagon
@llvm/pr-subscribers-lld-elf

@llvm/pr-subscribers-lld

Author: Brian Cain (androm3da)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/148098.diff

5 Files Affected:

  • (modified) lld/ELF/Arch/Hexagon.cpp (+121)
  • (modified) lld/ELF/Config.h (+1)
  • (modified) lld/ELF/Driver.cpp (+4)
  • (modified) lld/ELF/Target.h (+1)
  • (added) lld/test/ELF/hexagon-attributes.s (+150)
diff --git a/lld/ELF/Arch/Hexagon.cpp b/lld/ELF/Arch/Hexagon.cpp
index 726e425b36dc6..c449efa51c715 100644
--- a/lld/ELF/Arch/Hexagon.cpp
+++ b/lld/ELF/Arch/Hexagon.cpp
@@ -7,11 +7,19 @@
 //===----------------------------------------------------------------------===//
 
 #include "InputFiles.h"
+#include "OutputSections.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
+#include "Thunks.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Support/ELFAttributes.h"
 #include "llvm/Support/Endian.h"
+#include "llvm/Support/HexagonAttributeParser.h"
+#include "llvm/Support/HexagonAttributes.h"
+#include "llvm/Support/LEB128.h"
 
 using namespace llvm;
 using namespace llvm::object;
@@ -415,4 +423,117 @@ int64_t Hexagon::getImplicitAddend(const uint8_t *buf, RelType type) const {
   }
 }
 
+namespace {
+class HexagonAttributesSection final : public SyntheticSection {
+public:
+  HexagonAttributesSection(Ctx &ctx)
+      : SyntheticSection(ctx, ".hexagon.attributes", SHT_HEXAGON_ATTRIBUTES, 0,
+                         1) {}
+
+  size_t getSize() const override { return size; }
+  void writeTo(uint8_t *buf) override;
+
+  static constexpr StringRef vendor = "hexagon";
+  DenseMap<unsigned, unsigned> intAttr;
+  size_t size = 0;
+};
+} // namespace
+
+static HexagonAttributesSection *
+mergeAttributesSection(Ctx &ctx,
+                       const SmallVector<InputSectionBase *, 0> &sections) {
+  ctx.in.hexagonAttributes = std::make_unique<HexagonAttributesSection>(ctx);
+  auto &merged =
+      static_cast<HexagonAttributesSection &>(*ctx.in.hexagonAttributes);
+
+  // Collect all tags values from attributes section.
+  const auto &attributesTags = HexagonAttrs::getHexagonAttributeTags();
+  for (const InputSectionBase *sec : sections) {
+    HexagonAttributeParser parser;
+    if (Error e = parser.parse(sec->content(), llvm::endianness::little))
+      Warn(ctx) << sec << ": " << std::move(e);
+    for (const auto &tag : attributesTags) {
+      switch (HexagonAttrs::AttrType(tag.attr)) {
+      case HexagonAttrs::ARCH:
+      case HexagonAttrs::HVXARCH:
+        if (auto i = parser.getAttributeValue(tag.attr)) {
+          auto r = merged.intAttr.try_emplace(tag.attr, *i);
+          if (!r.second && r.first->second != *i) {
+            if (r.first->second < *i)
+              r.first->second = *i;
+          }
+        }
+        continue;
+
+      case HexagonAttrs::HVXIEEEFP:
+      case HexagonAttrs::HVXQFLOAT:
+      case HexagonAttrs::ZREG:
+      case HexagonAttrs::AUDIO:
+      case HexagonAttrs::CABAC:
+        if (auto i = parser.getAttributeValue(tag.attr)) {
+          auto r = merged.intAttr.try_emplace(tag.attr, *i);
+          if (!r.second && r.first->second != *i) {
+            r.first->second |= *i;
+          }
+        }
+        continue;
+      }
+    }
+  }
+
+  // The total size of headers: format-version [ <section-length> "vendor-name"
+  // [ <file-tag> <size>.
+  size_t size = 5 + merged.vendor.size() + 1 + 5;
+  for (auto &attr : merged.intAttr)
+    if (attr.second != 0)
+      size += getULEB128Size(attr.first) + getULEB128Size(attr.second);
+  merged.size = size;
+  return &merged;
+}
+
+void HexagonAttributesSection::writeTo(uint8_t *buf) {
+  const size_t size = getSize();
+  uint8_t *const end = buf + size;
+  *buf = ELFAttrs::Format_Version;
+  write32(ctx, buf + 1, size - 1);
+  buf += 5;
+
+  memcpy(buf, vendor.data(), vendor.size());
+  buf += vendor.size() + 1;
+
+  *buf = ELFAttrs::File;
+  write32(ctx, buf + 1, end - buf);
+  buf += 5;
+
+  for (auto &attr : intAttr) {
+    if (attr.second == 0)
+      continue;
+    buf += encodeULEB128(attr.first, buf);
+    buf += encodeULEB128(attr.second, buf);
+  }
+}
+
+void elf::mergeHexagonAttributesSections(Ctx &ctx) {
+  // Find the first input SHT_HEXAGON_ATTRIBUTES; return if not found.
+  size_t place =
+      llvm::find_if(ctx.inputSections,
+                    [](auto *s) { return s->type == SHT_HEXAGON_ATTRIBUTES; }) -
+      ctx.inputSections.begin();
+  if (place == ctx.inputSections.size())
+    return;
+
+  // Extract all SHT_HEXAGON_ATTRIBUTES sections into `sections`.
+  SmallVector<InputSectionBase *, 0> sections;
+  llvm::erase_if(ctx.inputSections, [&](InputSectionBase *s) {
+    if (s->type != SHT_HEXAGON_ATTRIBUTES)
+      return false;
+    sections.push_back(s);
+    return true;
+  });
+
+  // Add the merged section.
+  ctx.inputSections.insert(ctx.inputSections.begin() + place,
+                           mergeAttributesSection(ctx, sections));
+}
+
 void elf::setHexagonTargetInfo(Ctx &ctx) { ctx.target.reset(new Hexagon(ctx)); }
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 4c771e47a0e72..8961c5141200e 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -569,6 +569,7 @@ struct UndefinedDiag {
 struct InStruct {
   std::unique_ptr<InputSection> attributes;
   std::unique_ptr<SyntheticSection> riscvAttributes;
+  std::unique_ptr<SyntheticSection> hexagonAttributes;
   std::unique_ptr<BssSection> bss;
   std::unique_ptr<BssSection> bssRelRo;
   std::unique_ptr<SyntheticSection> gnuProperty;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index a194ae71d559a..a18cc4d4e4dd0 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -3450,6 +3450,10 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
   if (ctx.arg.emachine == EM_RISCV)
     mergeRISCVAttributesSections(ctx);
 
+  // Merge .hexagon.attributes sections.
+  if (ctx.arg.emachine == EM_HEXAGON)
+    mergeHexagonAttributesSections(ctx);
+
   {
     llvm::TimeTraceScope timeScope("Assign sections");
 
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 6dd20b2f0cbaa..93f15920bfedb 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -245,6 +245,7 @@ template <typename ELFT> void writeARMCmseImportLib(Ctx &);
 uint64_t getLoongArchPageDelta(uint64_t dest, uint64_t pc, RelType type);
 void riscvFinalizeRelax(int passes);
 void mergeRISCVAttributesSections(Ctx &);
+void mergeHexagonAttributesSections(Ctx &);
 void addArmInputSectionMappingSymbols(Ctx &);
 void addArmSyntheticSectionMappingSymbol(Defined *);
 void sortArmMappingSymbols(Ctx &);
diff --git a/lld/test/ELF/hexagon-attributes.s b/lld/test/ELF/hexagon-attributes.s
new file mode 100644
index 0000000000000..ff22e3a750e0d
--- /dev/null
+++ b/lld/test/ELF/hexagon-attributes.s
@@ -0,0 +1,150 @@
+# REQUIRES: hexagon
+
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf a.s -o a.o
+# RUN: ld.lld -e 0 a.o -o out 2>&1 | count 0
+# RUN: llvm-readelf -S -l --arch-specific out | FileCheck %s --check-prefixes=HDR,CHECK
+# RUN: ld.lld -e 0 a.o a.o -o out1 2>&1 | count 0
+# RUN: llvm-readobj --arch-specific out1 | FileCheck %s
+# RUN: ld.lld -r a.o a.o -o out1 2>&1 | count 0
+# RUN: llvm-readobj --arch-specific out1 | FileCheck %s
+
+# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf b.s -o b.o
+# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf c.s -o c.o
+# RUN: llvm-mc -filetype=obj -triple=hexagon-unknown-elf d.s -o d.o
+# RUN: ld.lld a.o b.o c.o -o out2
+# RUN: llvm-readobj --arch-specific out2 | FileCheck %s --check-prefix=CHECK2
+# RUN: ld.lld a.o b.o c.o d.o -o out3
+# RUN: llvm-readobj --arch-specific out3 | FileCheck %s --check-prefix=CHECK3
+
+# HDR:      Name              Type             Address          Off    Size   ES Flg Lk Inf Al
+# HDR:      .hexagon.attributes HEXAGON_ATTRIBUTES 00000000 {{.*}} {{.*}} 00      0   0  1{{$}}
+
+# HDR:      Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
+# HDR:      LOAD           {{.*}}
+# HDR-NEXT: GNU_STACK      {{.*}}
+
+# CHECK:      BuildAttributes {
+# CHECK-NEXT:   FormatVersion: 0x41
+# CHECK-NEXT:   Section 1 {
+# CHECK-NEXT:     SectionLength: 19
+# CHECK-NEXT:     Vendor: hexagon
+# CHECK-NEXT:     Tag: Tag_File (0x1)
+# CHECK-NEXT:     Size: 7
+# CHECK-NEXT:     FileAttributes {
+# CHECK-NEXT:       Attribute {
+# CHECK-NEXT:         Tag: 4
+# CHECK-NEXT:         TagName: arch
+# CHECK-NEXT:         Value: 68{{$}}
+# CHECK-NEXT:       }
+# CHECK-NEXT:     }
+# CHECK-NEXT:   }
+# CHECK-NEXT: }
+
+# CHECK2:      BuildAttributes {
+# CHECK2-NEXT:   FormatVersion: 0x41
+# CHECK2-NEXT:   Section 1 {
+# CHECK2-NEXT:     SectionLength: 21
+# CHECK2-NEXT:     Vendor: hexagon
+# CHECK2-NEXT:     Tag: Tag_File (0x1)
+# CHECK2-NEXT:     Size: 9
+# CHECK2-NEXT:     FileAttributes {
+# CHECK2-NEXT:       Attribute {
+# CHECK2-NEXT:         Tag: 4
+# CHECK2-NEXT:         TagName: arch
+# CHECK2-NEXT:         Value: 68{{$}}
+# CHECK2-NEXT:       }
+# CHECK2-NEXT:       Attribute {
+# CHECK2-NEXT:         Tag: 5
+# CHECK2-NEXT:         TagName: hvx_arch
+# CHECK2-NEXT:         Value: 68{{$}}
+# CHECK2-NEXT:       }
+# CHECK2-NEXT:     }
+# CHECK2-NEXT:   }
+# CHECK2-NEXT: }
+
+# CHECK3:      BuildAttributes {
+# CHECK3-NEXT:   FormatVersion: 0x41
+# CHECK3-NEXT:   Section 1 {
+# CHECK3-NEXT:     SectionLength: 25
+# CHECK3-NEXT:     Vendor: hexagon
+# CHECK3-NEXT:     Tag: Tag_File (0x1)
+# CHECK3-NEXT:     Size: 13
+# CHECK3-NEXT:     FileAttributes {
+# CHECK3-NEXT:       Attribute {
+# CHECK3-NEXT:         Tag: 7
+# CHECK3-NEXT:         TagName: hvx_qfloat
+# CHECK3-NEXT:         Value: 68{{$}}
+# CHECK3-NEXT:       }
+# CHECK3-NEXT:       Attribute {
+# CHECK3-NEXT:         Tag: 9
+# CHECK3-NEXT:         TagName: audio
+# CHECK3-NEXT:         Value: 68{{$}}
+# CHECK3-NEXT:       }
+# CHECK3-NEXT:       Attribute {
+# CHECK3-NEXT:         Tag: 4
+# CHECK3-NEXT:         TagName: arch
+# CHECK3-NEXT:         Value: 68{{$}}
+# CHECK3-NEXT:       }
+# CHECK3-NEXT:       Attribute {
+# CHECK3-NEXT:         Tag: 5
+# CHECK3-NEXT:         TagName: hvx_arch
+# CHECK3-NEXT:         Value: 68{{$}}
+# CHECK3-NEXT:       }
+# CHECK3-NEXT:     }
+# CHECK3-NEXT:   }
+# CHECK3-NEXT: }
+
+#--- a.s
+.section .hexagon.attributes,"",@0x70000003
+.byte 0x41
+.long .Lend-.hexagon.attributes-1
+.asciz "hexagon"
+.Lbegin:
+.byte 1
+.long .Lend-.Lbegin
+.byte 4
+.byte 68
+.Lend:
+
+#--- b.s
+.section .hexagon.attributes,"",@0x70000003
+.byte 0x41
+.long .Lend1-.hexagon.attributes-1
+.asciz "hexagon"
+.Lbegin1:
+.byte 1
+.long .Lend1-.Lbegin1
+.byte 4
+.byte 68
+.Lend1:
+
+#--- c.s
+.section .hexagon.attributes,"",@0x70000003
+.byte 0x41
+.long .Lend2-.hexagon.attributes-1
+.asciz "hexagon"
+.Lbegin2:
+.byte 1
+.long .Lend2-.Lbegin2
+.byte 4
+.byte 68
+.byte 5
+.byte 68
+.Lend2:
+
+#--- d.s
+.section .hexagon.attributes,"",@0x70000003
+.byte 0x41
+.long .Lend3-.hexagon.attributes-1
+.asciz "hexagon"
+.Lbegin3:
+.byte 1
+.long .Lend3-.Lbegin3
+.byte 4
+.byte 68
+.byte 7
+.byte 68
+.byte 9
+.byte 68
+.Lend3:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This header is now misaligned

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still misaligned

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've addressed it now. Thanks.

Copy link
Member

@MaskRay MaskRay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update the description with the purpose and mention that this is similar to .riscv.attributes (primary patch: https://reviews.llvm.org/D138550)?

Does Hexagon have a processor supplementary ABI document? Ideally this should be mentioned.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move before RISCV

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

lld/ELF/Config.h Outdated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move before riscvAttributes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC this change doesn't use Thunks.h, therefore should not add this include in this patch.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I must've crossed the wires with the other in-progress PR, sorry. Removed.

@androm3da
Copy link
Member Author

Can you update the description with the purpose and mention that this is similar to .riscv.attributes (primary patch: https://reviews.llvm.org/D138550)?

Done

Does Hexagon have a processor supplementary ABI document? Ideally this should be mentioned.

It does: https://docs.qualcomm.com/bundle/publicresource/80-N2040-23_REV_K_Qualcomm_Hexagon_Application_Binary_Interface_User_Guide.pdf

Unfortunately, it's not mentioned in this document. We should revise this to add the .hexagon.attributes there, I agree. WDYT @quic-akaryaki?

case HexagonAttrs::CABAC:
if (auto i = parser.getAttributeValue(tag.attr)) {
auto r = merged.intAttr.try_emplace(tag.attr, *i);
if (!r.second && r.first->second != *i) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

drop braces for this single-line single-statement body. We can remove && r.first->second != *i.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@androm3da androm3da merged commit d2bcc51 into llvm:main Jul 15, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants