[lld] [lld][AArch64][ELF][PAC] Support AUTH relocations and AUTH ELF marking (PR #72714)

Daniil Kovalev via llvm-commits llvm-commits at lists.llvm.org
Sun Dec 10 13:30:47 PST 2023


https://github.com/kovdan01 updated https://github.com/llvm/llvm-project/pull/72714

>From c493d78e6c482bb530189de05b79e7082a224fab Mon Sep 17 00:00:00 2001
From: Daniil Kovalev <dkovalev at accesssoftek.com>
Date: Thu, 28 Sep 2023 03:14:35 +0300
Subject: [PATCH] [lld][AArch64][ELF][PAC] Support AUTH relocations and AUTH
 ELF marking

This patch adds lld support for:

- Dynamic R_AARCH64_AUTH_* relocations (including RELR compressed AUTH
  relocations) as described here:
  https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#auth-variant-dynamic-relocations

- .note.AARCH64-PAUTH-ABI-tag section as defined here
  https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#elf-marking

Co-authored-by: Peter Collingbourne <peter at pcc.me.uk>
---
 lld/ELF/Arch/AArch64.cpp             |   5 +
 lld/ELF/Config.h                     |   4 +
 lld/ELF/Driver.cpp                   |  57 +++++++++-
 lld/ELF/InputFiles.cpp               |  44 ++++++++
 lld/ELF/InputFiles.h                 |   1 +
 lld/ELF/Relocations.cpp              |  26 +++++
 lld/ELF/SyntheticSections.cpp        |  44 ++++++--
 lld/ELF/SyntheticSections.h          |  19 +++-
 lld/ELF/Writer.cpp                   |  17 +++
 lld/test/ELF/aarch64-feature-pauth.s |  83 ++++++++++++++
 lld/test/ELF/aarch64-ptrauth.s       | 156 +++++++++++++++++++++++++++
 11 files changed, 445 insertions(+), 11 deletions(-)
 create mode 100644 lld/test/ELF/aarch64-feature-pauth.s
 create mode 100644 lld/test/ELF/aarch64-ptrauth.s

diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp
index 048f0ec30ebd28..6828d3f57c10e8 100644
--- a/lld/ELF/Arch/AArch64.cpp
+++ b/lld/ELF/Arch/AArch64.cpp
@@ -112,6 +112,7 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
   case R_AARCH64_MOVW_UABS_G2:
   case R_AARCH64_MOVW_UABS_G2_NC:
   case R_AARCH64_MOVW_UABS_G3:
+  case R_AARCH64_AUTH_ABS64:
     return R_ABS;
   case R_AARCH64_TLSDESC_ADR_PAGE21:
     return R_AARCH64_TLSDESC_PAGE;
@@ -395,6 +396,10 @@ void AArch64::relocate(uint8_t *loc, const Relocation &rel,
   case R_AARCH64_PREL64:
     write64(loc, val);
     break;
+  case R_AARCH64_AUTH_ABS64:
+    checkIntUInt(loc, val, 32, rel);
+    write32(loc, val);
+    break;
   case R_AARCH64_ADD_ABS_LO12_NC:
     or32AArch64Imm(loc, val);
     break;
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 56229334f9a44a..1b633a79842769 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -187,6 +187,7 @@ struct Config {
   llvm::StringRef cmseOutputLib;
   StringRef zBtiReport = "none";
   StringRef zCetReport = "none";
+  StringRef zPauthReport = "none";
   llvm::StringRef ltoBasicBlockSections;
   std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
   llvm::StringRef thinLTOPrefixReplaceOld;
@@ -275,6 +276,7 @@ struct Config {
   bool relocatable;
   bool relrGlibc = false;
   bool relrPackDynRelocs = false;
+  bool relrPackAuthDynRelocs = false;
   llvm::DenseSet<llvm::StringRef> saveTempsArgs;
   llvm::SmallVector<std::pair<llvm::GlobPattern, uint32_t>, 0> shuffleSections;
   bool singleRoRx;
@@ -492,6 +494,8 @@ struct Ctx {
   void reset();
 
   llvm::raw_fd_ostream openAuxiliaryFile(llvm::StringRef, std::error_code &);
+
+  SmallVector<uint8_t, 0> aarch64PauthAbiTag;
 };
 
 LLVM_LIBRARY_VISIBILITY extern Ctx ctx;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 6bef09eeca015a..4e8e9eb86ecf77 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -65,6 +65,7 @@
 #include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/raw_ostream.h"
+#include <algorithm>
 #include <cstdlib>
 #include <tuple>
 #include <utility>
@@ -459,6 +460,8 @@ static void checkOptions() {
       error("-z force-bti only supported on AArch64");
     if (config->zBtiReport != "none")
       error("-z bti-report only supported on AArch64");
+    if (config->zPauthReport != "none")
+      error("-z pauth-report only supported on AArch64");
   }
 
   if (config->emachine != EM_386 && config->emachine != EM_X86_64 &&
@@ -558,6 +561,7 @@ constexpr const char *knownZFlags[] = {
     "nognustack",
     "nokeep-text-section-prefix",
     "nopack-relative-relocs",
+    "nopack-relative-auth-relocs",
     "norelro",
     "noseparate-code",
     "nostart-stop-gc",
@@ -566,6 +570,7 @@ constexpr const char *knownZFlags[] = {
     "origin",
     "pac-plt",
     "pack-relative-relocs",
+    "pack-relative-auth-relocs",
     "rel",
     "rela",
     "relro",
@@ -583,7 +588,7 @@ constexpr const char *knownZFlags[] = {
 static bool isKnownZFlag(StringRef s) {
   return llvm::is_contained(knownZFlags, s) ||
          s.starts_with("common-page-size=") || s.starts_with("bti-report=") ||
-         s.starts_with("cet-report=") ||
+         s.starts_with("cet-report=") || s.starts_with("pauth-report=") ||
          s.starts_with("dead-reloc-in-nonalloc=") ||
          s.starts_with("max-page-size=") || s.starts_with("stack-size=") ||
          s.starts_with("start-stop-visibility=");
@@ -1514,7 +1519,8 @@ static void readConfigs(opt::InputArgList &args) {
   }
 
   auto reports = {std::make_pair("bti-report", &config->zBtiReport),
-                  std::make_pair("cet-report", &config->zCetReport)};
+                  std::make_pair("cet-report", &config->zCetReport),
+                  std::make_pair("pauth-report", &config->zPauthReport)};
   for (opt::Arg *arg : args.filtered(OPT_z)) {
     std::pair<StringRef, StringRef> option =
         StringRef(arg->getValue()).split('=');
@@ -1671,6 +1677,9 @@ static void readConfigs(opt::InputArgList &args) {
         getPackDynRelocs(args);
   }
 
+  config->relrPackAuthDynRelocs = getZFlag(
+      args, "pack-relative-auth-relocs", "nopack-relative-auth-relocs", false);
+
   if (auto *arg = args.getLastArg(OPT_symbol_ordering_file)){
     if (args.hasArg(OPT_call_graph_ordering_file))
       error("--symbol-ordering-file and --call-graph-order-file "
@@ -2639,6 +2648,47 @@ static uint32_t getAndFeatures() {
   return ret;
 }
 
+static void getAarch64PauthInfo() {
+  if (ctx.objectFiles.empty())
+    return;
+
+  auto NonEmptyIt = std::find_if(
+      ctx.objectFiles.begin(), ctx.objectFiles.end(),
+      [](const ELFFileBase *f) { return !f->aarch64PauthAbiTag.empty(); });
+  if (NonEmptyIt == ctx.objectFiles.end())
+    return;
+
+  ctx.aarch64PauthAbiTag = (*NonEmptyIt)->aarch64PauthAbiTag;
+  StringRef F1 = (*NonEmptyIt)->getName();
+  for (ELFFileBase *F : ArrayRef(ctx.objectFiles)) {
+    StringRef F2 = F->getName();
+    const SmallVector<uint8_t, 0> &D1 = ctx.aarch64PauthAbiTag;
+    const SmallVector<uint8_t, 0> &D2 = F->aarch64PauthAbiTag;
+    if (D1.empty() != D2.empty()) {
+      auto Helper = [](StringRef Report, const Twine &Msg) {
+        if (Report == "warning")
+          warn(Msg);
+        else if (Report == "error")
+          error(Msg);
+      };
+
+      Helper(config->zPauthReport,
+             (D1.empty() ? F1.str() : F2.str()) +
+                 " has no AArch64 PAuth compatibility info while " +
+                 (D1.empty() ? F2.str() : F1.str()) +
+                 " has one; either all or no input files must have it");
+    }
+
+    if (!D1.empty() && !D2.empty() &&
+        !std::equal(D1.begin(), D1.end(), D2.begin(), D2.end()))
+      errorOrWarn(
+          "incompatible values of AArch64 PAuth compatibility info found"
+          "\n" +
+          F1 + ": 0x" + toHex(ArrayRef(D1.data(), D1.size())) + "\n" + F2 +
+          ": 0x" + toHex(ArrayRef(D2.data(), D2.size())));
+  }
+}
+
 static void initSectionsAndLocalSyms(ELFFileBase *file, bool ignoreComdats) {
   switch (file->ekind) {
   case ELF32LEKind:
@@ -2976,6 +3026,9 @@ void LinkerDriver::link(opt::InputArgList &args) {
   // contain a hint to tweak linker's and loader's behaviors.
   config->andFeatures = getAndFeatures();
 
+  if (config->emachine == EM_AARCH64)
+    getAarch64PauthInfo();
+
   // The Target instance handles target-specific stuff, such as applying
   // relocations or writing a PLT section. It also contains target-dependent
   // values such as a default image base address.
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index cc2c5916e05c22..44c8050f2c967a 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -962,6 +962,44 @@ template <class ELFT> static uint32_t readAndFeatures(const InputSection &sec) {
   return featuresSet;
 }
 
+// Extract compatibility info for aarch64 pointer authentication from the
+// .note.AARCH64-PAUTH-ABI-tag section and write it to the corresponding ObjFile
+// field. See the following ABI documentation:
+// https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#elf-marking
+template <class ELFT>
+static void readAArch64PauthAbiTag(const InputSection &sec, ObjFile<ELFT> &f) {
+  using Elf_Nhdr = typename ELFT::Nhdr;
+  using Elf_Note = typename ELFT::Note;
+  ArrayRef<uint8_t> data = sec.content();
+  auto reportError = [&](const Twine &msg) {
+    errorOrWarn(toString(sec.file) + ":(" + sec.name + "): " + msg);
+  };
+
+  auto *nhdr = reinterpret_cast<const Elf_Nhdr *>(data.data());
+  if (data.size() < sizeof(Elf_Nhdr) ||
+      data.size() < nhdr->getSize(sec.addralign)) {
+    reportError("section is too short");
+    return;
+  }
+
+  Elf_Note note(*nhdr);
+  if (nhdr->n_type != NT_ARM_TYPE_PAUTH_ABI_TAG)
+    reportError("invalid type field value " + Twine(nhdr->n_type) + " (" +
+                Twine(NT_ARM_TYPE_PAUTH_ABI_TAG) + " expected)");
+  if (note.getName() != "ARM")
+    reportError("invalid name field value " + note.getName() +
+                " (ARM expected)");
+
+  ArrayRef<uint8_t> desc = note.getDesc(sec.addralign);
+  if (desc.size() < 16) {
+    reportError("too short AArch64 PAuth compatibility info "
+                "(at least 16 bytes expected)");
+    return;
+  }
+
+  f.aarch64PauthAbiTag = SmallVector<uint8_t, 0>(iterator_range(desc));
+}
+
 template <class ELFT>
 InputSectionBase *ObjFile<ELFT>::getRelocTarget(uint32_t idx,
                                                 const Elf_Shdr &sec,
@@ -1020,6 +1058,12 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
       return &InputSection::discarded;
     }
 
+    if (config->emachine == EM_AARCH64 &&
+        name == ".note.AARCH64-PAUTH-ABI-tag") {
+      readAArch64PauthAbiTag<ELFT>(InputSection(*this, sec, name), *this);
+      return &InputSection::discarded;
+    }
+
     // Split stacks is a feature to support a discontiguous stack,
     // commonly used in the programming language Go. For the details,
     // see https://gcc.gnu.org/wiki/SplitStacks. An object file compiled
diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h
index ab98d78fcf1455..6a74ba7fb20998 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -218,6 +218,7 @@ class ELFFileBase : public InputFile {
 public:
   uint32_t andFeatures = 0;
   bool hasCommonSyms = false;
+  SmallVector<uint8_t, 0> aarch64PauthAbiTag;
 };
 
 // .o file.
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index fe3d7f419e84aa..5b5e6b154d52f4 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -1444,6 +1444,32 @@ template <class ELFT, class RelTy> void RelocationScanner::scanOne(RelTy *&i) {
     }
   }
 
+  if (config->emachine == EM_AARCH64 && type == R_AARCH64_AUTH_ABS64) {
+    // Assume relocations from relocatable objects are RELA.
+    assert(RelTy::IsRela);
+    std::lock_guard<std::mutex> lock(relocMutex);
+    // For a preemptible symbol, we can't use a relative relocation. For an
+    // undefined symbol, we can't compute offset at link-time and use a relative
+    // relocation. Use a symbolic relocation instead.
+    Partition &part = sec->getPartition();
+    if (sym.isPreemptible || sym.isUndefined()) {
+      part.relaDyn->addSymbolReloc(type, *sec, offset, sym, addend, type);
+    } else if (part.relrAuthDyn && sec->addralign >= 2 && offset % 2 == 0 &&
+               isInt<32>(sym.getVA(addend))) {
+      // Implicit addend is below 32-bits so we can use the compressed
+      // relative relocation section. The R_AARCH64_AUTH_RELATIVE
+      // has a smaller addend fielf as bits [63:32] encode the signing-schema.
+      sec->addReloc({expr, type, offset, addend, &sym});
+      part.relrAuthDyn->relocsVec[parallel::getThreadIndex()].push_back(
+          {sec, offset});
+    } else {
+      part.relaDyn->addReloc({R_AARCH64_AUTH_RELATIVE, sec, offset,
+                              DynamicReloc::AddendOnlyWithTargetVA, sym, addend,
+                              R_ABS});
+    }
+    return;
+  }
+
   // If the relocation does not emit a GOT or GOTPLT entry but its computation
   // uses their addresses, we need GOT or GOTPLT to be created.
   //
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 2b32eb3a0fe355..fa7589806a7b5b 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -331,6 +331,29 @@ void GnuPropertySection::writeTo(uint8_t *buf) {
 
 size_t GnuPropertySection::getSize() const { return config->is64 ? 32 : 28; }
 
+AArch64PauthAbiTag::AArch64PauthAbiTag()
+    : SyntheticSection(llvm::ELF::SHF_ALLOC, llvm::ELF::SHT_NOTE,
+                       config->wordsize, ".note.AARCH64-PAUTH-ABI-tag") {}
+
+bool AArch64PauthAbiTag::isNeeded() const {
+  return !ctx.aarch64PauthAbiTag.empty();
+}
+
+void AArch64PauthAbiTag::writeTo(uint8_t *buf) {
+  const SmallVector<uint8_t, 0> &data = ctx.aarch64PauthAbiTag;
+  write32(buf, 4);                             // Name size
+  write32(buf + 4, data.size());               // Content size
+  write32(buf + 8, NT_ARM_TYPE_PAUTH_ABI_TAG); // Type
+  memcpy(buf + 12, "ARM", 4);                  // Name string
+  memcpy(buf + 16, data.data(), data.size());
+  memset(buf + 16 + data.size(), 0, getSize() - 16 - data.size()); // Padding
+}
+
+size_t AArch64PauthAbiTag::getSize() const {
+  return alignToPowerOf2(16 + ctx.aarch64PauthAbiTag.size(),
+                         config->is64 ? 8 : 4);
+}
+
 BuildIdSection::BuildIdSection()
     : SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"),
       hashSize(getHashSize()) {}
@@ -1406,6 +1429,12 @@ DynamicSection<ELFT>::computeContents() {
     addInt(config->useAndroidRelrTags ? DT_ANDROID_RELRENT : DT_RELRENT,
            sizeof(Elf_Relr));
   }
+  if (part.relrAuthDyn && part.relrAuthDyn->getParent() &&
+      !part.relrAuthDyn->relocs.empty()) {
+    addInSec(DT_AARCH64_AUTH_RELR, *part.relrAuthDyn);
+    addInt(DT_AARCH64_AUTH_RELRSZ, part.relrAuthDyn->getParent()->size);
+    addInt(DT_AARCH64_AUTH_RELRENT, sizeof(Elf_Relr));
+  }
   // .rel[a].plt section usually consists of two parts, containing plt and
   // iplt relocations. It is possible to have only iplt relocations in the
   // output. In that case relaPlt is empty and have zero offset, the same offset
@@ -1717,10 +1746,13 @@ template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *buf) {
   }
 }
 
-RelrBaseSection::RelrBaseSection(unsigned concurrency)
-    : SyntheticSection(SHF_ALLOC,
-                       config->useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR,
-                       config->wordsize, ".relr.dyn"),
+RelrBaseSection::RelrBaseSection(unsigned concurrency, bool isAArch64Auth)
+    : SyntheticSection(
+          SHF_ALLOC,
+          isAArch64Auth
+              ? SHT_AARCH64_AUTH_RELR
+              : (config->useAndroidRelrTags ? SHT_ANDROID_RELR : SHT_RELR),
+          config->wordsize, isAArch64Auth ? ".relr.auth.dyn" : ".relr.dyn"),
       relocsVec(concurrency) {}
 
 void RelrBaseSection::mergeRels() {
@@ -1988,8 +2020,8 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
 }
 
 template <class ELFT>
-RelrSection<ELFT>::RelrSection(unsigned concurrency)
-    : RelrBaseSection(concurrency) {
+RelrSection<ELFT>::RelrSection(unsigned concurrency, bool isAArch64Auth)
+    : RelrBaseSection(concurrency, isAArch64Auth) {
   this->entsize = config->wordsize;
 }
 
diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h
index 3a9f4ba886f6bb..d183a547c68205 100644
--- a/lld/ELF/SyntheticSections.h
+++ b/lld/ELF/SyntheticSections.h
@@ -144,6 +144,16 @@ class GnuPropertySection final : public SyntheticSection {
   size_t getSize() const override;
 };
 
+// .note.AARCH64-PAUTH-ABI-tag section. See
+// https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#elf-marking
+class AArch64PauthAbiTag final : public SyntheticSection {
+public:
+  AArch64PauthAbiTag();
+  void writeTo(uint8_t *buf) override;
+  size_t getSize() const override;
+  bool isNeeded() const override;
+};
+
 // .note.gnu.build-id section.
 class BuildIdSection : public SyntheticSection {
   // First 16 bytes are a header.
@@ -543,7 +553,8 @@ class RelocationBaseSection : public SyntheticSection {
   static bool classof(const SectionBase *d) {
     return SyntheticSection::classof(d) &&
            (d->type == llvm::ELF::SHT_RELA || d->type == llvm::ELF::SHT_REL ||
-            d->type == llvm::ELF::SHT_RELR);
+            d->type == llvm::ELF::SHT_RELR ||
+            d->type == llvm::ELF::SHT_AARCH64_AUTH_RELR);
   }
   int32_t dynamicTag, sizeDynamicTag;
   SmallVector<DynamicReloc, 0> relocs;
@@ -599,7 +610,7 @@ struct RelativeReloc {
 
 class RelrBaseSection : public SyntheticSection {
 public:
-  RelrBaseSection(unsigned concurrency);
+  RelrBaseSection(unsigned concurrency, bool isAArch64Auth = false);
   void mergeRels();
   bool isNeeded() const override {
     return !relocs.empty() ||
@@ -617,7 +628,7 @@ template <class ELFT> class RelrSection final : public RelrBaseSection {
   using Elf_Relr = typename ELFT::Relr;
 
 public:
-  RelrSection(unsigned concurrency);
+  RelrSection(unsigned concurrency, bool isAArch64Auth = false);
 
   bool updateAllocSize() override;
   size_t getSize() const override { return relrRelocs.size() * this->entsize; }
@@ -1319,6 +1330,7 @@ struct Partition {
   std::unique_ptr<PackageMetadataNote> packageMetadataNote;
   std::unique_ptr<RelocationBaseSection> relaDyn;
   std::unique_ptr<RelrBaseSection> relrDyn;
+  std::unique_ptr<RelrBaseSection> relrAuthDyn;
   std::unique_ptr<VersionDefinitionSection> verDef;
   std::unique_ptr<SyntheticSection> verNeed;
   std::unique_ptr<VersionTableSection> verSym;
@@ -1363,6 +1375,7 @@ struct InStruct {
   std::unique_ptr<StringTableSection> strTab;
   std::unique_ptr<SymbolTableBaseSection> symTab;
   std::unique_ptr<SymtabShndxSection> symTabShndx;
+  std::unique_ptr<AArch64PauthAbiTag> aarch64PauthAbiTag;
 
   void reset();
 };
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index a84e4864ab0e5a..f1b569daada663 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -445,6 +445,12 @@ template <class ELFT> void elf::createSyntheticSections() {
       add(*part.relrDyn);
     }
 
+    if (config->relrPackAuthDynRelocs) {
+      part.relrAuthDyn = std::make_unique<RelrSection<ELFT>>(
+          threadCount, /*isAArch64Auth=*/true);
+      add(*part.relrAuthDyn);
+    }
+
     if (!config->relocatable) {
       if (config->ehFrameHdr) {
         part.ehFrameHdr = std::make_unique<EhFrameHeader>();
@@ -566,6 +572,11 @@ template <class ELFT> void elf::createSyntheticSections() {
   if (config->andFeatures)
     add(*make<GnuPropertySection>());
 
+  if (!ctx.aarch64PauthAbiTag.empty()) {
+    in.aarch64PauthAbiTag = std::make_unique<AArch64PauthAbiTag>();
+    add(*in.aarch64PauthAbiTag);
+  }
+
   // .note.GNU-stack is always added when we are creating a re-linkable
   // object file. Other linkers are using the presence of this marker
   // section to control the executable-ness of the stack area, but that
@@ -1725,6 +1736,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
       changed |= part.relaDyn->updateAllocSize();
       if (part.relrDyn)
         changed |= part.relrDyn->updateAllocSize();
+      if (part.relrAuthDyn)
+        changed |= part.relrAuthDyn->updateAllocSize();
       if (part.memtagDescriptors)
         changed |= part.memtagDescriptors->updateAllocSize();
     }
@@ -2179,6 +2192,10 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
         part.relrDyn->mergeRels();
         finalizeSynthetic(part.relrDyn.get());
       }
+      if (part.relrAuthDyn) {
+        part.relrAuthDyn->mergeRels();
+        finalizeSynthetic(part.relrAuthDyn.get());
+      }
 
       finalizeSynthetic(part.dynSymTab.get());
       finalizeSynthetic(part.gnuHashTab.get());
diff --git a/lld/test/ELF/aarch64-feature-pauth.s b/lld/test/ELF/aarch64-feature-pauth.s
new file mode 100644
index 00000000000000..0520b2f28631e1
--- /dev/null
+++ b/lld/test/ELF/aarch64-feature-pauth.s
@@ -0,0 +1,83 @@
+# REQUIRES: aarch64
+
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag1.s -o tag11.o
+# RUN: cp tag11.o tag12.o
+# RUN: ld.lld -shared tag11.o tag12.o -o tagok.so
+# RUN: llvm-readelf -n tagok.so | FileCheck --check-prefix OK %s
+
+# OK: AArch64 PAuth ABI tag: platform 0x2a, version 0x1
+
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag2.s -o tag2.o
+# RUN: not ld.lld tag11.o tag12.o tag2.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR1 %s
+
+# ERR1: error: incompatible values of AArch64 PAuth compatibility info found
+# ERR1: {{.*}}: 0x2A000000000000000{{1|2}}00000000000000
+# ERR1: {{.*}}: 0x2A000000000000000{{1|2}}00000000000000
+
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-errs.s -o errs.o
+# RUN: not ld.lld errs.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR2 %s
+
+# ERR2:      error: {{.*}}: invalid type field value 42 (1 expected)
+# ERR2-NEXT: error: {{.*}}: invalid name field value XXX (ARM expected)
+# ERR2-NEXT: error: {{.*}}: too short AArch64 PAuth compatibility info (at least 16 bytes expected)
+
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu abi-tag-short.s -o short.o
+# RUN: not ld.lld short.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR3 %s
+
+# ERR3: error: {{.*}}: section is too short
+
+# RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu no-info.s -o noinfo1.o
+# RUN: cp noinfo1.o noinfo2.o
+# RUN: not ld.lld -z pauth-report=error tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix ERR4 %s
+# RUN: ld.lld -z pauth-report=warning tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix WARN %s
+# RUN: ld.lld -z pauth-report=none tag11.o noinfo1.o noinfo2.o -o /dev/null 2>&1 | FileCheck --check-prefix NONE %s
+
+# ERR4:      error: {{.*}}noinfo1.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it
+# ERR4-NEXT: error: {{.*}}noinfo2.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it
+# WARN:      warning: {{.*}}noinfo1.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it
+# WARN-NEXT: warning: {{.*}}noinfo2.o has no AArch64 PAuth compatibility info while {{.*}}tag11.o has one; either all or no input files must have it
+# NONE-NOT:  {{.*}} has no AArch64 PAuth compatibility info while {{.*}} has one; either all or no input files must have it
+
+#--- abi-tag-short.s
+
+.section ".note.AARCH64-PAUTH-ABI-tag", "a"
+.long 4
+.long 8
+
+#--- abi-tag-errs.s
+
+.section ".note.AARCH64-PAUTH-ABI-tag", "a"
+.long 4
+.long 8
+.long 42
+.asciz "XXX"
+
+.quad 42
+
+#--- abi-tag1.s
+
+.section ".note.AARCH64-PAUTH-ABI-tag", "a"
+.long 4
+.long 16
+.long 1
+.asciz "ARM"
+
+.quad 42         // platform
+.quad 1          // version
+
+#--- abi-tag2.s
+
+.section ".note.AARCH64-PAUTH-ABI-tag", "a"
+.long 4
+.long 16
+.long 1
+.asciz "ARM"
+
+.quad 42         // platform
+.quad 2          // version
+
+#--- no-info.s
+
+.section ".test", "a"
diff --git a/lld/test/ELF/aarch64-ptrauth.s b/lld/test/ELF/aarch64-ptrauth.s
new file mode 100644
index 00000000000000..db946fc4c3e55c
--- /dev/null
+++ b/lld/test/ELF/aarch64-ptrauth.s
@@ -0,0 +1,156 @@
+// REQUIRES: aarch64
+
+// RUN: llvm-mc -filetype=obj -triple=aarch64 %p/Inputs/shared2.s -o %t.so.o
+// RUN: ld.lld -shared %t.so.o -soname=so -o %t.so
+// RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t.o
+// RUN: ld.lld -pie -z nopack-relative-auth-relocs %t.o %t.so -o %t2
+// RUN: llvm-readobj -r %t2 | FileCheck --check-prefix=UNPACKED %s
+
+// UNPACKED:          Section ({{.+}}) .rela.dyn {
+// UNPACKED-NEXT:       0x30680 R_AARCH64_AUTH_RELATIVE - 0x1
+// UNPACKED-NEXT:       0x30688 R_AARCH64_AUTH_RELATIVE - 0x2
+// UNPACKED-NEXT:       0x30690 R_AARCH64_AUTH_RELATIVE - 0x3
+// UNPACKED-NEXT:       0x30698 R_AARCH64_AUTH_RELATIVE - 0x4
+// UNPACKED-NEXT:       0x306A0 R_AARCH64_AUTH_RELATIVE - 0x5
+// UNPACKED-NEXT:       0x306A8 R_AARCH64_AUTH_RELATIVE - 0x6
+// UNPACKED-NEXT:       0x306B0 R_AARCH64_AUTH_RELATIVE - 0x7
+// UNPACKED-NEXT:       0x306B8 R_AARCH64_AUTH_RELATIVE - 0x8
+// UNPACKED-NEXT:       0x306C8 R_AARCH64_AUTH_RELATIVE - 0x1
+// UNPACKED-NEXT:       0x306D0 R_AARCH64_AUTH_RELATIVE - 0x2
+// UNPACKED-NEXT:       0x306D8 R_AARCH64_AUTH_RELATIVE - 0x3
+// UNPACKED-NEXT:       0x306E0 R_AARCH64_AUTH_RELATIVE - 0x4
+// UNPACKED-NEXT:       0x306E8 R_AARCH64_AUTH_RELATIVE - 0x5
+// UNPACKED-NEXT:       0x306F0 R_AARCH64_AUTH_RELATIVE - 0x6
+// UNPACKED-NEXT:       0x306F8 R_AARCH64_AUTH_RELATIVE - 0x7
+// UNPACKED-NEXT:       0x30710 R_AARCH64_AUTH_RELATIVE - 0x1
+// UNPACKED-NEXT:       0x30718 R_AARCH64_AUTH_RELATIVE - 0x2
+// UNPACKED-NEXT:       0x30720 R_AARCH64_AUTH_RELATIVE - 0x3
+// UNPACKED-NEXT:       0x30728 R_AARCH64_AUTH_RELATIVE - 0x4
+// UNPACKED-NEXT:       0x30730 R_AARCH64_AUTH_RELATIVE - 0x5
+// UNPACKED-NEXT:       0x30738 R_AARCH64_AUTH_RELATIVE - 0x6
+// UNPACKED-NEXT:       0x30740 R_AARCH64_AUTH_RELATIVE - 0x7
+// UNPACKED-NEXT:       0x30748 R_AARCH64_AUTH_RELATIVE - 0x8
+// UNPACKED-NEXT:       0x30750 R_AARCH64_AUTH_RELATIVE - 0x9
+// UNPACKED-NEXT:       0x30759 R_AARCH64_AUTH_RELATIVE - 0xA
+// UNPACKED-NEXT:       0x306C0 R_AARCH64_AUTH_ABS64 bar2 0x1
+// UNPACKED-NEXT:       0x30708 R_AARCH64_AUTH_ABS64 bar2 0x0
+// UNPACKED-NEXT:       0x30761 R_AARCH64_AUTH_ABS64 bar2 0x0
+// UNPACKED-NEXT:       0x30769 R_AARCH64_AUTH_ABS64 bar2 0x0
+// UNPACKED-NEXT:       0x30771 R_AARCH64_AUTH_ABS64 bar2 0x1
+// UNPACKED-NEXT:       0x30779 R_AARCH64_AUTH_ABS64 bar2 0x1
+// UNPACKED-NEXT:       0x30781 R_AARCH64_AUTH_ABS64 bar2 0x0
+// UNPACKED-NEXT:       0x30700 R_AARCH64_AUTH_ABS64 zed2 0x0
+// UNPACKED-NEXT:     }
+
+// RUN: ld.lld -pie -z pack-relative-auth-relocs %t.o %t.so -o %t2
+// RUN: llvm-readobj -S --dynamic-table %t2 | FileCheck --check-prefix=RELR-HEADERS %s
+// RUN: llvm-readobj -r --raw-relr %t2 | FileCheck --check-prefix=RAW-RELR %s
+// RUN: llvm-readobj -r %t2 | FileCheck --check-prefix=RELR %s
+
+// RELR-HEADERS:       Index: 1
+// RELR-HEADERS-NEXT:  Name: .dynsym
+
+// RELR-HEADERS:       Name: .relr.auth.dyn
+// RELR-HEADERS-NEXT:  Type: SHT_AARCH64_AUTH_RELR
+// RELR-HEADERS-NEXT:  Flags [ (0x2)
+// RELR-HEADERS-NEXT:    SHF_ALLOC (0x2)
+// RELR-HEADERS-NEXT:  ]
+// RELR-HEADERS-NEXT:  Address: [[ADDR:.*]]
+// RELR-HEADERS-NEXT:  Offset: [[ADDR]]
+// RELR-HEADERS-NEXT:  Size: 16
+// RELR-HEADERS-NEXT:  Link: 0
+// RELR-HEADERS-NEXT:  Info: 0
+// RELR-HEADERS-NEXT:  AddressAlignment: 8
+// RELR-HEADERS-NEXT:  EntrySize: 8
+
+// RELR-HEADERS:       0x0000000070000012 AARCH64_AUTH_RELR    [[ADDR]]
+// RELR-HEADERS:       0x0000000070000011 AARCH64_AUTH_RELRSZ  16 (bytes)
+// RELR-HEADERS:       0x0000000070000013 AARCH64_AUTH_RELRENT 8 (bytes)
+
+/// SHT_RELR section contains address/bitmap entries
+/// encoding the offsets for relative relocation.
+// RAW-RELR:           Section ({{.+}}) .relr.auth.dyn {
+// RAW-RELR-NEXT:      0x30480
+// RAW-RELR-NEXT:      0x7FCFEFF
+// RAW-RELR-NEXT:      }
+
+/// Decoded SHT_RELR section is same as UNPACKED,
+/// but contains only the relative relocations.
+/// Any relative relocations with odd offset stay in SHT_RELA.
+
+// RELR:      Section ({{.+}}) .rela.dyn {
+// RELR-NEXT:   0x30559 R_AARCH64_AUTH_RELATIVE - 0xA
+// RELR-NEXT:   0x304C0 R_AARCH64_AUTH_ABS64 bar2 0x1
+// RELR-NEXT:   0x30508 R_AARCH64_AUTH_ABS64 bar2 0x0
+// RELR-NEXT:   0x30561 R_AARCH64_AUTH_ABS64 bar2 0x0
+// RELR-NEXT:   0x30569 R_AARCH64_AUTH_ABS64 bar2 0x0
+// RELR-NEXT:   0x30571 R_AARCH64_AUTH_ABS64 bar2 0x1
+// RELR-NEXT:   0x30579 R_AARCH64_AUTH_ABS64 bar2 0x1
+// RELR-NEXT:   0x30581 R_AARCH64_AUTH_ABS64 bar2 0x0
+// RELR-NEXT:   0x30500 R_AARCH64_AUTH_ABS64 zed2 0x0
+// RELR-NEXT: }
+// RELR-NEXT: Section ({{.+}}) .relr.auth.dyn {
+// RELR-NEXT:   0x30480 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30488 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30490 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30498 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x304A0 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x304A8 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x304B0 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x304B8 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x304C8 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x304D0 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x304D8 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x304E0 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x304E8 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x304F0 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x304F8 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30510 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30518 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30520 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30528 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30530 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30538 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30540 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30548 R_AARCH64_RELATIVE -
+// RELR-NEXT:   0x30550 R_AARCH64_RELATIVE -
+// RELR-NEXT: }
+
+.section .test, "aw"
+.p2align 3
+.quad (__ehdr_start + 1)@AUTH(da,42)
+.quad (__ehdr_start + 2)@AUTH(da,42)
+.quad (__ehdr_start + 3)@AUTH(da,42)
+.quad (__ehdr_start + 4)@AUTH(da,42)
+.quad (__ehdr_start + 5)@AUTH(da,42)
+.quad (__ehdr_start + 6)@AUTH(da,42)
+.quad (__ehdr_start + 7)@AUTH(da,42)
+.quad (__ehdr_start + 8)@AUTH(da,42)
+.quad (bar2 + 1)@AUTH(ia,42)
+
+.quad (__ehdr_start + 1)@AUTH(da,65535)
+.quad (__ehdr_start + 2)@AUTH(da,65535)
+.quad (__ehdr_start + 3)@AUTH(da,65535)
+.quad (__ehdr_start + 4)@AUTH(da,65535)
+.quad (__ehdr_start + 5)@AUTH(da,65535)
+.quad (__ehdr_start + 6)@AUTH(da,65535)
+.quad (__ehdr_start + 7)@AUTH(da,65535)
+.quad zed2 at AUTH(da,42)
+.quad bar2 at AUTH(ia,42)
+
+.quad (__ehdr_start + 1)@AUTH(da,0)
+.quad (__ehdr_start + 2)@AUTH(da,0)
+.quad (__ehdr_start + 3)@AUTH(da,0)
+.quad (__ehdr_start + 4)@AUTH(da,0)
+.quad (__ehdr_start + 5)@AUTH(da,0)
+.quad (__ehdr_start + 6)@AUTH(da,0)
+.quad (__ehdr_start + 7)@AUTH(da,0)
+.quad (__ehdr_start + 8)@AUTH(da,0)
+.quad (__ehdr_start + 9)@AUTH(da,0)
+.byte 00
+.quad (__ehdr_start + 10)@AUTH(da,0)
+.quad bar2 at AUTH(ia,42)
+.quad bar2 at AUTH(ia,42)
+.quad (bar2 + 1)@AUTH(ia,42)
+.quad (bar2 + 1)@AUTH(ia,42)
+.quad bar2 at AUTH(ia,42)



More information about the llvm-commits mailing list