[llvm-branch-commits] CodeGen: Optionally emit PAuth relocations as IRELATIVE relocations. (PR #133533)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Mar 28 15:34:16 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-aarch64

Author: Peter Collingbourne (pcc)

<details>
<summary>Changes</summary>

This supports the following use cases:
- ConstantPtrAuth expressions that are unrepresentable using standard PAuth
  relocations such as expressions involving an integer operand or
  deactivation symbols.
- libc implementations that do not support PAuth relocations.

For more information see the RFC:
https://discourse.llvm.org/t/rfc-structure-protection-a-family-of-uaf-mitigation-techniques/85555

TODO:
- Add tests.


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


1 Files Affected:

- (modified) llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp (+163-15) 


``````````diff
diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index 8f26e9b791dff..cbff94f4dc227 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -54,6 +54,7 @@
 #include "llvm/MC/MCSectionMachO.h"
 #include "llvm/MC/MCStreamer.h"
 #include "llvm/MC/MCSymbol.h"
+#include "llvm/MC/MCValue.h"
 #include "llvm/MC/TargetRegistry.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/CommandLine.h"
@@ -84,6 +85,7 @@ class AArch64AsmPrinter : public AsmPrinter {
   bool EnableImportCallOptimization = false;
   DenseMap<MCSection *, std::vector<std::pair<MCSymbol *, MCSymbol *>>>
       SectionToImportedFunctionCalls;
+  unsigned PAuthIFuncNextUniqueID = 1;
 
 public:
   AArch64AsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
@@ -191,6 +193,10 @@ class AArch64AsmPrinter : public AsmPrinter {
   // authenticating)
   void LowerLOADgotAUTH(const MachineInstr &MI);
 
+  const MCExpr *emitPAuthRelocationAsIRelative(
+      const MCExpr *Target, uint16_t Disc, AArch64PACKey::ID KeyID,
+      bool HasAddressDiversity, bool IsDSOLocal);
+
   /// tblgen'erated driver function for lowering simple MI->MC
   /// pseudo instructions.
   bool lowerPseudoInstExpansion(const MachineInstr *MI, MCInst &Inst);
@@ -2218,6 +2224,145 @@ void AArch64AsmPrinter::emitPtrauthBranch(const MachineInstr *MI) {
   EmitToStreamer(*OutStreamer, BRInst);
 }
 
+static void emitAddress(MCStreamer &Streamer, MCRegister Reg,
+                        const MCExpr *Expr, bool DSOLocal,
+                        const MCSubtargetInfo &STI) {
+  MCValue Val;
+  if (!Expr->evaluateAsRelocatable(Val, nullptr))
+    report_fatal_error("emitAddress could not evaluate");
+  if (DSOLocal) {
+    Streamer.emitInstruction(
+        MCInstBuilder(AArch64::ADRP)
+            .addReg(Reg)
+            .addExpr(AArch64MCExpr::create(Expr, AArch64MCExpr::VK_ABS_PAGE,
+                                           Streamer.getContext())),
+        STI);
+    Streamer.emitInstruction(
+        MCInstBuilder(AArch64::ADDXri)
+            .addReg(Reg)
+            .addReg(Reg)
+            .addExpr(AArch64MCExpr::create(Expr, AArch64MCExpr::VK_LO12,
+                                           Streamer.getContext()))
+            .addImm(0),
+        STI);
+  } else {
+    Streamer.emitInstruction(MCInstBuilder(AArch64::ADRP)
+                                 .addReg(Reg)
+                                 .addExpr(AArch64MCExpr::create(
+                                     Val.getSymA(), AArch64MCExpr::VK_GOT_PAGE,
+                                     Streamer.getContext())),
+                             STI);
+    Streamer.emitInstruction(MCInstBuilder(AArch64::LDRXui)
+                                 .addReg(Reg)
+                                 .addReg(Reg)
+                                 .addExpr(AArch64MCExpr::create(
+                                     Val.getSymA(), AArch64MCExpr::VK_GOT_LO12,
+                                     Streamer.getContext())),
+                             STI);
+    if (Val.getConstant())
+      Streamer.emitInstruction(MCInstBuilder(AArch64::ADDXri)
+                                   .addReg(Reg)
+                                   .addReg(Reg)
+                                   .addImm(Val.getConstant())
+                                   .addImm(0),
+                               STI);
+  }
+}
+
+static bool targetSupportsPAuthRelocation(const Triple &TT,
+                                          const MCExpr *Target) {
+  // No released version of glibc supports PAuth relocations.
+  if (TT.isOSGlibc())
+    return false;
+
+  // We emit PAuth constants as IRELATIVE relocations in cases where the
+  // constant cannot be represented as a PAuth relocation:
+  // 1) The signed value is not a symbol.
+  return !isa<MCConstantExpr>(Target);
+}
+
+static bool targetSupportsIRelativeRelocation(const Triple &TT) {
+  // IFUNCs are ELF-only.
+  if (!TT.isOSBinFormatELF())
+    return false;
+
+  // musl doesn't support IFUNCs.
+  if (TT.isMusl())
+    return false;
+
+  return true;
+}
+
+const MCExpr *AArch64AsmPrinter::emitPAuthRelocationAsIRelative(
+    const MCExpr *Target, uint16_t Disc, AArch64PACKey::ID KeyID,
+    bool HasAddressDiversity, bool IsDSOLocal) {
+  const Triple &TT = TM.getTargetTriple();
+
+  // We only emit an IRELATIVE relocation if the target supports IRELATIVE and
+  // does not support the kind of PAuth relocation that we are trying to emit.
+  if (targetSupportsPAuthRelocation(TT, Target, DSExpr) ||
+      !targetSupportsIRelativeRelocation(TT))
+    return nullptr;
+
+  // For now, only the DA key is supported.
+  if (KeyID != AArch64PACKey::DA)
+    return nullptr;
+
+  std::unique_ptr<MCSubtargetInfo> STI(
+      TM.getTarget().createMCSubtargetInfo(TT.str(), "", ""));
+  assert(STI && "Unable to create subtarget info");
+
+  MCSymbol *Place = OutStreamer->getContext().createTempSymbol();
+  OutStreamer->emitLabel(Place);
+  OutStreamer->pushSection();
+
+  OutStreamer->switchSection(OutStreamer->getContext().getELFSection(
+      ".text.startup", ELF::SHT_PROGBITS, ELF::SHF_ALLOC | ELF::SHF_EXECINSTR,
+      0, "", true, PAuthIFuncNextUniqueID++, nullptr));
+
+  MCSymbol *IFuncSym =
+      OutStreamer->getContext().createLinkerPrivateSymbol("pauth_ifunc");
+  OutStreamer->emitSymbolAttribute(IFuncSym, MCSA_ELF_TypeIndFunction);
+  OutStreamer->emitLabel(IFuncSym);
+  if (isa<MCConstantExpr>(Target)) {
+    OutStreamer->emitInstruction(MCInstBuilder(AArch64::MOVZXi)
+                                 .addReg(AArch64::X0)
+                                 .addExpr(Target)
+                                 .addImm(0),
+                             *STI);
+  } else {
+    emitAddress(*OutStreamer, AArch64::X0, Target, IsDSOLocal, *STI);
+  }
+  if (HasAddressDiversity) {
+    auto *PlacePlusDisc = MCBinaryExpr::createAdd(
+        MCSymbolRefExpr::create(Place, OutStreamer->getContext()),
+        MCConstantExpr::create(static_cast<int16_t>(Disc),
+                               OutStreamer->getContext()),
+        OutStreamer->getContext());
+    emitAddress(*OutStreamer, AArch64::X1, PlacePlusDisc, /*IsDSOLocal=*/true,
+                *STI);
+  } else {
+    emitMOVZ(AArch64::X1, Disc, 0);
+  }
+
+  MCSymbol *PrePACInst = OutStreamer->getContext().createTempSymbol();
+  OutStreamer->emitLabel(PrePACInst);
+
+  // We don't know the subtarget because this is being emitted for a global
+  // initializer. Because the performance of IFUNC resolvers is unimportant, we
+  // always call the EmuPAC runtime, which will end up using the PAC instruction
+  // if the target supports PAC.
+  MCSymbol *EmuPAC =
+      OutStreamer->getContext().getOrCreateSymbol("__emupac_pacda");
+  const MCSymbolRefExpr *EmuPACRef =
+      MCSymbolRefExpr::create(EmuPAC, OutStreamer->getContext());
+  OutStreamer->emitInstruction(MCInstBuilder(AArch64::B).addExpr(EmuPACRef),
+                               *STI);
+  OutStreamer->popSection();
+
+  return MCSymbolRefExpr::create(IFuncSym, OutStreamer->getContext());
+}
+
 const MCExpr *
 AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {
   MCContext &Ctx = OutContext;
@@ -2229,23 +2374,20 @@ AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {
 
   auto *BaseGVB = dyn_cast<GlobalValue>(BaseGV);
 
-  // If we can't understand the referenced ConstantExpr, there's nothing
-  // else we can do: emit an error.
-  if (!BaseGVB) {
-    BaseGV->getContext().emitError(
-        "cannot resolve target base/addend of ptrauth constant");
-    return nullptr;
+  const MCExpr *Sym;
+  if (BaseGVB) {
+    // If there is an addend, turn that into the appropriate MCExpr.
+    Sym = MCSymbolRefExpr::create(getSymbol(BaseGVB), Ctx);
+    if (Offset.sgt(0))
+      Sym = MCBinaryExpr::createAdd(
+          Sym, MCConstantExpr::create(Offset.getSExtValue(), Ctx), Ctx);
+    else if (Offset.slt(0))
+      Sym = MCBinaryExpr::createSub(
+          Sym, MCConstantExpr::create((-Offset).getSExtValue(), Ctx), Ctx);
+  } else {
+    Sym = MCConstantExpr::create(Offset.getSExtValue(), Ctx);
   }
 
-  // If there is an addend, turn that into the appropriate MCExpr.
-  const MCExpr *Sym = MCSymbolRefExpr::create(getSymbol(BaseGVB), Ctx);
-  if (Offset.sgt(0))
-    Sym = MCBinaryExpr::createAdd(
-        Sym, MCConstantExpr::create(Offset.getSExtValue(), Ctx), Ctx);
-  else if (Offset.slt(0))
-    Sym = MCBinaryExpr::createSub(
-        Sym, MCConstantExpr::create((-Offset).getSExtValue(), Ctx), Ctx);
-
   uint64_t KeyID = CPA.getKey()->getZExtValue();
   // We later rely on valid KeyID value in AArch64PACKeyIDToString call from
   // AArch64AuthMCExpr::printImpl, so fail fast.
@@ -2259,6 +2401,12 @@ AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {
     report_fatal_error("AArch64 PAC Discriminator '" + Twine(Disc) +
                        "' out of range [0, 0xFFFF]");
 
+  // Check if we need to represent this with an IRELATIVE and emit it if so.
+  if (auto *IFuncSym = emitPAuthRelocationAsIRelative(
+          Sym, Disc, AArch64PACKey::ID(KeyID), CPA.hasAddressDiscriminator(),
+          BaseGVB && BaseGVB->isDSOLocal()))
+    return IFuncSym;
+
   // Finally build the complete @AUTH expr.
   return AArch64AuthMCExpr::create(Sym, Disc, AArch64PACKey::ID(KeyID),
                                    CPA.hasAddressDiscriminator(), Ctx);

``````````

</details>


https://github.com/llvm/llvm-project/pull/133533


More information about the llvm-branch-commits mailing list