Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ARM,MC] Support FDPIC relocations #82187

Conversation

MaskRay
Copy link
Member

@MaskRay MaskRay commented Feb 18, 2024

Linux kernel fs/binfmt_elf_fdpic.c supports FDPIC for MMU-less systems.
GCC/binutils/qemu support FDPIC ABI for ARM
(https://github.com/mickael-guene/fdpic_doc).
ARM FDPIC Toolchain and ABI provides a summary.

This patch implements FDPIC relocations to the integrated assembler.
There are 6 static relocations and 2 dynamic relocations, with
R_ARM_FUNCDESC as both static and dynamic.

gas requires --fdpic to assemble data relocations like .word f(FUNCDESC).
This patch adds MCTargetOptions::FDPIC and reports an error if FDPIC
is not set.

Created using spr 1.3.4
@llvmbot
Copy link
Collaborator

llvmbot commented Feb 18, 2024

@llvm/pr-subscribers-mc
@llvm/pr-subscribers-llvm-binary-utilities

@llvm/pr-subscribers-objectyaml

Author: Fangrui Song (MaskRay)

Changes

Linux kernel fs/binfmt_elf_fdpic.c supports FDPIC for MMU-less systems.
GCC/binutils/qemu support FDPIC ABI for ARM
(https://github.com/mickael-guene/fdpic_doc).
ARM FDPIC Toolchain and ABI provides a summary.

This patch implements FDPIC relocations to the integrated assembler.
There are 6 static relocations and 2 dynamic relocations, with
R_ARM_FUNCDESC as both static and dynamic.

gas requires --fdpic to assemble data relocations like .word f(FUNCDESC).
This patch adds MCTargetOptions::FDPIC and reports an error if FDPIC
is not set.


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

18 Files Affected:

  • (modified) llvm/include/llvm/BinaryFormat/ELF.h (+1)
  • (modified) llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def (+7)
  • (modified) llvm/include/llvm/MC/MCExpr.h (+6)
  • (modified) llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h (+4)
  • (modified) llvm/include/llvm/MC/MCTargetOptions.h (+1)
  • (modified) llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h (+2)
  • (modified) llvm/lib/MC/MCExpr.cpp (+8-7)
  • (modified) llvm/lib/MC/MCParser/AsmParser.cpp (+1-1)
  • (modified) llvm/lib/MC/MCTargetOptions.cpp (+1-1)
  • (modified) llvm/lib/MC/MCTargetOptionsCommandFlags.cpp (+5)
  • (modified) llvm/lib/ObjectYAML/ELFYAML.cpp (+1)
  • (modified) llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp (+34)
  • (modified) llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp (+4-1)
  • (modified) llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp (+23)
  • (added) llvm/test/MC/ARM/fdpic.s (+31)
  • (modified) llvm/test/tools/llvm-readobj/ELF/file-header-os-abi.test (+7)
  • (modified) llvm/test/tools/llvm-readobj/ELF/reloc-types-arm.test (+14)
  • (modified) llvm/tools/llvm-readobj/ELFDumper.cpp (+2-1)
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index 124bba76c1774c..bace3a92677a82 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -362,6 +362,7 @@ enum {
   ELFOSABI_AMDGPU_PAL = 65,    // AMD PAL runtime
   ELFOSABI_AMDGPU_MESA3D = 66, // AMD GCN GPUs (GFX6+) for MESA runtime
   ELFOSABI_ARM = 97,           // ARM
+  ELFOSABI_ARM_FDPIC = 65,     // ARM FDPIC
   ELFOSABI_C6000_ELFABI = 64,  // Bare-metal TMS320C6000
   ELFOSABI_C6000_LINUX = 65,   // Linux TMS320C6000
   ELFOSABI_STANDALONE = 255,   // Standalone (embedded) application
diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def
index 47084d1eb0aad5..7e9fe965241f25 100644
--- a/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def
+++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def
@@ -143,3 +143,10 @@ ELF_RELOC(R_ARM_THM_BF16,               0x88)
 ELF_RELOC(R_ARM_THM_BF12,               0x89)
 ELF_RELOC(R_ARM_THM_BF18,               0x8a)
 ELF_RELOC(R_ARM_IRELATIVE,              0xa0)
+ELF_RELOC(R_ARM_GOTFUNCDESC,            0xa1)
+ELF_RELOC(R_ARM_GOTOFFFUNCDESC,         0xa2)
+ELF_RELOC(R_ARM_FUNCDESC,               0xa3)
+ELF_RELOC(R_ARM_FUNCDESC_VALUE,         0xa4)
+ELF_RELOC(R_ARM_TLS_GD32_FDPIC,         0xa5)
+ELF_RELOC(R_ARM_TLS_LDM32_FDPIC,        0xa6)
+ELF_RELOC(R_ARM_TLS_IE32_FDPIC,         0xa7)
diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h
index 67836292874f5f..b3119609372049 100644
--- a/llvm/include/llvm/MC/MCExpr.h
+++ b/llvm/include/llvm/MC/MCExpr.h
@@ -223,6 +223,12 @@ class MCSymbolRefExpr : public MCExpr {
     VK_SECREL,
     VK_SIZE,    // symbol@SIZE
     VK_WEAKREF, // The link between the symbols in .weakref foo, bar
+    VK_FUNCDESC,
+    VK_GOTFUNCDESC,
+    VK_GOTOFFFUNCDESC,
+    VK_TLSGD_FDPIC,
+    VK_TLSLDM_FDPIC,
+    VK_GOTTPOFF_FDPIC,
 
     VK_X86_ABS8,
     VK_X86_PLTOFF,
diff --git a/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h b/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h
index fe905f2c3ba5fe..7edd3f8ce4904c 100644
--- a/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h
+++ b/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h
@@ -525,6 +525,10 @@ class MCTargetAsmParser : public MCAsmParserExtension {
   // Return whether this parser accept star as start of statement
   virtual bool starIsStartOfStatement() { return false; };
 
+  virtual MCSymbolRefExpr::VariantKind
+  getVariantKindForName(StringRef Name) const {
+    return MCSymbolRefExpr::getVariantKindForName(Name);
+  }
   virtual const MCExpr *applyModifierToExpr(const MCExpr *E,
                                             MCSymbolRefExpr::VariantKind,
                                             MCContext &Ctx) {
diff --git a/llvm/include/llvm/MC/MCTargetOptions.h b/llvm/include/llvm/MC/MCTargetOptions.h
index e2dd1e0433dbe4..a7295879e15f0f 100644
--- a/llvm/include/llvm/MC/MCTargetOptions.h
+++ b/llvm/include/llvm/MC/MCTargetOptions.h
@@ -51,6 +51,7 @@ class MCTargetOptions {
   bool MCNoTypeCheck : 1;
   bool MCSaveTempLabels : 1;
   bool MCIncrementalLinkerCompatible : 1;
+  bool FDPIC : 1;
   bool ShowMCEncoding : 1;
   bool ShowMCInst : 1;
   bool AsmVerbose : 1;
diff --git a/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h b/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h
index 7f6ee6c8be224a..ba3784cab5b11d 100644
--- a/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h
+++ b/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h
@@ -29,6 +29,8 @@ std::optional<bool> getExplicitRelaxAll();
 
 bool getIncrementalLinkerCompatible();
 
+bool getFDPIC();
+
 int getDwarfVersion();
 
 bool getDwarf64();
diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp
index 80def6dfc24b1a..485fd1885ddb52 100644
--- a/llvm/lib/MC/MCExpr.cpp
+++ b/llvm/lib/MC/MCExpr.cpp
@@ -220,6 +220,7 @@ const MCSymbolRefExpr *MCSymbolRefExpr::create(StringRef Name, VariantKind Kind,
 
 StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
   switch (Kind) {
+    // clang-format off
   case VK_Invalid: return "<<invalid>>";
   case VK_None: return "<<none>>";
 
@@ -232,13 +233,16 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
   case VK_GOTPCREL: return "GOTPCREL";
   case VK_GOTPCREL_NORELAX: return "GOTPCREL_NORELAX";
   case VK_GOTTPOFF: return "GOTTPOFF";
+  case VK_GOTTPOFF_FDPIC: return "gottpoff_fdpic";
   case VK_INDNTPOFF: return "INDNTPOFF";
   case VK_NTPOFF: return "NTPOFF";
   case VK_GOTNTPOFF: return "GOTNTPOFF";
   case VK_PLT: return "PLT";
   case VK_TLSGD: return "TLSGD";
+  case VK_TLSGD_FDPIC: return "tlsgd_fdpic";
   case VK_TLSLD: return "TLSLD";
   case VK_TLSLDM: return "TLSLDM";
+  case VK_TLSLDM_FDPIC: return "tlsldm_fdpic";
   case VK_TPOFF: return "TPOFF";
   case VK_TPREL: return "TPREL";
   case VK_TLSCALL: return "tlscall";
@@ -253,6 +257,9 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
   case VK_SECREL: return "SECREL32";
   case VK_SIZE: return "SIZE";
   case VK_WEAKREF: return "WEAKREF";
+  case VK_FUNCDESC: return "FUNCDESC";
+  case VK_GOTFUNCDESC: return "GOTFUNCDESC";
+  case VK_GOTOFFFUNCDESC: return "GOTOFFFUNCDESC";
   case VK_X86_ABS8: return "ABS8";
   case VK_X86_PLTOFF: return "PLTOFF";
   case VK_ARM_NONE: return "none";
@@ -386,6 +393,7 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
   case VK_VE_TLS_GD_LO32: return "tls_gd_lo";
   case VK_VE_TPOFF_HI32: return "tpoff_hi";
   case VK_VE_TPOFF_LO32: return "tpoff_lo";
+    // clang-format on
   }
   llvm_unreachable("Invalid variant kind");
 }
@@ -493,13 +501,6 @@ MCSymbolRefExpr::getVariantKindForName(StringRef Name) {
     .Case("ie", VK_Hexagon_IE)
     .Case("ldgot", VK_Hexagon_LD_GOT)
     .Case("ldplt", VK_Hexagon_LD_PLT)
-    .Case("none", VK_ARM_NONE)
-    .Case("got_prel", VK_ARM_GOT_PREL)
-    .Case("target1", VK_ARM_TARGET1)
-    .Case("target2", VK_ARM_TARGET2)
-    .Case("prel31", VK_ARM_PREL31)
-    .Case("sbrel", VK_ARM_SBREL)
-    .Case("tlsldo", VK_ARM_TLSLDO)
     .Case("lo8", VK_AVR_LO8)
     .Case("hi8", VK_AVR_HI8)
     .Case("hlo8", VK_AVR_HLO8)
diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp
index 8e508dbdb1c69b..a1c32eee328643 100644
--- a/llvm/lib/MC/MCParser/AsmParser.cpp
+++ b/llvm/lib/MC/MCParser/AsmParser.cpp
@@ -1237,7 +1237,7 @@ bool AsmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc,
 
     // Lookup the symbol variant if used.
     if (!Split.second.empty()) {
-      Variant = MCSymbolRefExpr::getVariantKindForName(Split.second);
+      Variant = getTargetParser().getVariantKindForName(Split.second);
       if (Variant != MCSymbolRefExpr::VK_Invalid) {
         SymbolName = Split.first;
       } else if (MAI.doesAllowAtInName() && !MAI.useParensForSymbolVariant()) {
diff --git a/llvm/lib/MC/MCTargetOptions.cpp b/llvm/lib/MC/MCTargetOptions.cpp
index 07c6e752cb613d..bff4b8da2fb1b0 100644
--- a/llvm/lib/MC/MCTargetOptions.cpp
+++ b/llvm/lib/MC/MCTargetOptions.cpp
@@ -15,7 +15,7 @@ MCTargetOptions::MCTargetOptions()
     : MCRelaxAll(false), MCNoExecStack(false), MCFatalWarnings(false),
       MCNoWarn(false), MCNoDeprecatedWarn(false), MCNoTypeCheck(false),
       MCSaveTempLabels(false), MCIncrementalLinkerCompatible(false),
-      ShowMCEncoding(false), ShowMCInst(false), AsmVerbose(false),
+      FDPIC(false), ShowMCEncoding(false), ShowMCInst(false), AsmVerbose(false),
       PreserveAsmComments(true), Dwarf64(false),
       EmitDwarfUnwind(EmitDwarfUnwindType::Default),
       MCUseDwarfDirectory(DefaultDwarfDirectory),
diff --git a/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp b/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp
index 8a4923e4792fb5..fb8334d626cb8b 100644
--- a/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp
+++ b/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp
@@ -36,6 +36,7 @@ using namespace llvm;
 
 MCOPT_EXP(bool, RelaxAll)
 MCOPT(bool, IncrementalLinkerCompatible)
+MCOPT(bool, FDPIC)
 MCOPT(int, DwarfVersion)
 MCOPT(bool, Dwarf64)
 MCOPT(EmitDwarfUnwindType, EmitDwarfUnwind)
@@ -66,6 +67,9 @@ llvm::mc::RegisterMCTargetOptionsFlags::RegisterMCTargetOptionsFlags() {
           "emit an object file which can be used with an incremental linker"));
   MCBINDOPT(IncrementalLinkerCompatible);
 
+  static cl::opt<bool> FDPIC("fdpic", cl::desc("Use the FDPIC ABI"));
+  MCBINDOPT(FDPIC);
+
   static cl::opt<int> DwarfVersion("dwarf-version", cl::desc("Dwarf version"),
                                    cl::init(0));
   MCBINDOPT(DwarfVersion);
@@ -135,6 +139,7 @@ MCTargetOptions llvm::mc::InitMCTargetOptionsFromFlags() {
   MCTargetOptions Options;
   Options.MCRelaxAll = getRelaxAll();
   Options.MCIncrementalLinkerCompatible = getIncrementalLinkerCompatible();
+  Options.FDPIC = getFDPIC();
   Options.Dwarf64 = getDwarf64();
   Options.DwarfVersion = getDwarfVersion();
   Options.ShowMCInst = getShowMCInst();
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index de1ef2458152c8..9c1a28db592a1c 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -406,6 +406,7 @@ void ScalarEnumerationTraits<ELFYAML::ELF_ELFOSABI>::enumeration(
   ECase(ELFOSABI_AMDGPU_PAL);
   ECase(ELFOSABI_AMDGPU_MESA3D);
   ECase(ELFOSABI_ARM);
+  ECase(ELFOSABI_ARM_FDPIC);
   ECase(ELFOSABI_C6000_ELFABI);
   ECase(ELFOSABI_C6000_LINUX);
   ECase(ELFOSABI_STANDALONE);
diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
index c82ab57bdf80f1..37bfb76a494dee 100644
--- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
+++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
@@ -737,6 +737,9 @@ class ARMAsmParser : public MCTargetAsmParser {
   void ReportNearMisses(SmallVectorImpl<NearMissInfo> &NearMisses, SMLoc IDLoc,
                         OperandVector &Operands);
 
+  MCSymbolRefExpr::VariantKind
+  getVariantKindForName(StringRef Name) const override;
+
   void doBeforeLabelEmit(MCSymbol *Symbol, SMLoc IDLoc) override;
 
   void onLabelParsed(MCSymbol *Symbol) override;
@@ -11358,6 +11361,37 @@ bool ARMAsmParser::parseDirectiveARM(SMLoc L) {
   return false;
 }
 
+MCSymbolRefExpr::VariantKind
+ARMAsmParser::getVariantKindForName(StringRef Name) const {
+  return StringSwitch<MCSymbolRefExpr::VariantKind>(Name.lower())
+      .Case("funcdesc", MCSymbolRefExpr::VK_FUNCDESC)
+      .Case("got", MCSymbolRefExpr::VK_GOT)
+      .Case("got_prel", MCSymbolRefExpr::VK_ARM_GOT_PREL)
+      .Case("gotfuncdesc", MCSymbolRefExpr::VK_GOTFUNCDESC)
+      .Case("gotoff", MCSymbolRefExpr::VK_GOTOFF)
+      .Case("gotofffuncdesc", MCSymbolRefExpr::VK_GOTOFFFUNCDESC)
+      .Case("gottpoff", MCSymbolRefExpr::VK_GOTTPOFF)
+      .Case("gottpoff_fdpic", MCSymbolRefExpr::VK_GOTTPOFF_FDPIC)
+      .Case("imgrel", MCSymbolRefExpr::VK_COFF_IMGREL32)
+      .Case("none", MCSymbolRefExpr::VK_ARM_NONE)
+      .Case("plt", MCSymbolRefExpr::VK_PLT)
+      .Case("prel31", MCSymbolRefExpr::VK_ARM_PREL31)
+      .Case("sbrel", MCSymbolRefExpr::VK_ARM_SBREL)
+      .Case("secrel32", MCSymbolRefExpr::VK_SECREL)
+      .Case("target1", MCSymbolRefExpr::VK_ARM_TARGET1)
+      .Case("target2", MCSymbolRefExpr::VK_ARM_TARGET2)
+      .Case("tlscall", MCSymbolRefExpr::VK_TLSCALL)
+      .Case("tlsdesc", MCSymbolRefExpr::VK_TLSDESC)
+      .Case("tlsgd", MCSymbolRefExpr::VK_TLSGD)
+      .Case("tlsgd_fdpic", MCSymbolRefExpr::VK_TLSGD_FDPIC)
+      .Case("tlsld", MCSymbolRefExpr::VK_TLSLD)
+      .Case("tlsldm", MCSymbolRefExpr::VK_TLSLDM)
+      .Case("tlsldm_fdpic", MCSymbolRefExpr::VK_TLSLDM_FDPIC)
+      .Case("tlsldo", MCSymbolRefExpr::VK_ARM_TLSLDO)
+      .Case("tpoff", MCSymbolRefExpr::VK_TPOFF)
+      .Default(MCSymbolRefExpr::VK_Invalid);
+}
+
 void ARMAsmParser::doBeforeLabelEmit(MCSymbol *Symbol, SMLoc IDLoc) {
   // We need to flush the current implicit IT block on a label, because it is
   // not legal to branch into an IT block.
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
index 1d17bb349f24ba..6cd4badb7704b7 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
@@ -29,6 +29,7 @@
 #include "llvm/MC/MCSectionELF.h"
 #include "llvm/MC/MCSectionMachO.h"
 #include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
 #include "llvm/MC/MCValue.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/EndianStream.h"
@@ -1349,7 +1350,9 @@ static MCAsmBackend *createARMAsmBackend(const Target &T,
     return new ARMAsmBackendWinCOFF(T, STI.getTargetTriple().isThumb());
   case Triple::ELF:
     assert(TheTriple.isOSBinFormatELF() && "using ELF for non-ELF target");
-    uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TheTriple.getOS());
+    uint8_t OSABI = Options.FDPIC
+                        ? ELF::ELFOSABI_ARM_FDPIC
+                        : MCELFObjectTargetWriter::getOSABI(TheTriple.getOS());
     return new ARMAsmBackendELF(T, STI.getTargetTriple().isThumb(), OSABI,
                                 Endian);
   }
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
index 44695a86c4e36c..baec4a5cb5215a 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
@@ -84,6 +84,11 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target,
   if (Kind >= FirstLiteralRelocationKind)
     return Kind - FirstLiteralRelocationKind;
   MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant();
+  auto CheckFDPIC = [&]() {
+    if (getOSABI() != ELF::ELFOSABI_ARM_FDPIC)
+      Ctx.reportError(Fixup.getLoc(),
+                      "relocation only supported in FDPIC mode");
+  };
 
   if (IsPCRel) {
     switch (Fixup.getTargetKind()) {
@@ -240,6 +245,24 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target,
       return ELF::R_ARM_TLS_LDM32;
     case MCSymbolRefExpr::VK_ARM_TLSDESCSEQ:
       return ELF::R_ARM_TLS_DESCSEQ;
+    case MCSymbolRefExpr::VK_FUNCDESC:
+      CheckFDPIC();
+      return ELF::R_ARM_FUNCDESC;
+    case MCSymbolRefExpr::VK_GOTFUNCDESC:
+      CheckFDPIC();
+      return ELF::R_ARM_GOTFUNCDESC;
+    case MCSymbolRefExpr::VK_GOTOFFFUNCDESC:
+      CheckFDPIC();
+      return ELF::R_ARM_GOTOFFFUNCDESC;
+    case MCSymbolRefExpr::VK_TLSGD_FDPIC:
+      CheckFDPIC();
+      return ELF::R_ARM_TLS_GD32_FDPIC;
+    case MCSymbolRefExpr::VK_TLSLDM_FDPIC:
+      CheckFDPIC();
+      return ELF::R_ARM_TLS_LDM32_FDPIC;
+    case MCSymbolRefExpr::VK_GOTTPOFF_FDPIC:
+      CheckFDPIC();
+      return ELF::R_ARM_TLS_IE32_FDPIC;
     }
   case ARM::fixup_arm_condbranch:
   case ARM::fixup_arm_uncondbranch:
diff --git a/llvm/test/MC/ARM/fdpic.s b/llvm/test/MC/ARM/fdpic.s
new file mode 100644
index 00000000000000..4f36393d9110e8
--- /dev/null
+++ b/llvm/test/MC/ARM/fdpic.s
@@ -0,0 +1,31 @@
+# RUN: llvm-mc -triple=armv7-linux-gnueabi %s | FileCheck %s --check-prefix=ASM
+# RUN: llvm-mc -filetype=obj -triple=armv7-linux-gnueabi --fdpic %s | llvm-readelf -h -r - | FileCheck %s
+
+# RUN: not llvm-mc -filetype=obj -triple=armv7-linux-gnueabi %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR
+
+# ASM:      .long f(FUNCDESC)
+# ASM-NEXT: .long f(GOTFUNCDESC)
+# ASM-NEXT: .long f(GOTOFFFUNCDESC)
+
+# CHECK:      OS/ABI: ARM FDPIC
+# CHECK:      Machine: ARM
+# CHECK:      Flags: 0x5000000
+
+# CHECK:      R_ARM_FUNCDESC        00000000 f
+# CHECK-NEXT: R_ARM_GOTFUNCDESC     00000000 f
+# CHECK-NEXT: R_ARM_GOTOFFFUNCDESC  00000000 f
+# CHECK-NEXT: R_ARM_TLS_GD32_FDPIC  00000000 tls
+# CHECK-NEXT: R_ARM_TLS_LDM32_FDPIC 00000000 tls
+# CHECK-NEXT: R_ARM_TLS_IE32_FDPIC  00000000 tls
+
+.data
+# ERR: [[#@LINE+1]]:7: error: relocation only supported in FDPIC mode
+.long f(FUNCDESC)
+# ERR: [[#@LINE+1]]:7: error: relocation only supported in FDPIC mode
+.long f(GOTFUNCDESC)
+# ERR: [[#@LINE+1]]:7: error: relocation only supported in FDPIC mode
+.long f(GOTOFFFUNCDESC)
+# ERR: [[#@LINE+1]]:7: error: relocation only supported in FDPIC mode
+.long tls(tlsgd_fdpic)
+.long tls(tlsldm_fdpic)
+.long tls(gottpoff_fdpic)
diff --git a/llvm/test/tools/llvm-readobj/ELF/file-header-os-abi.test b/llvm/test/tools/llvm-readobj/ELF/file-header-os-abi.test
index eb60d2a021af63..a48346d6b9c85e 100644
--- a/llvm/test/tools/llvm-readobj/ELF/file-header-os-abi.test
+++ b/llvm/test/tools/llvm-readobj/ELF/file-header-os-abi.test
@@ -192,6 +192,13 @@ FileHeader:
 # OSABI-ARM-LLVM: OS/ABI: ARM (0x61)
 # OSABI-ARM-GNU:  OS/ABI: ARM
 
+# RUN: yaml2obj %s -DOSABI=ELFOSABI_ARM_FDPIC -DMACHINE=EM_ARM -o %t.osabi.arm_fdpic
+# RUN: llvm-readobj --file-headers %t.osabi.arm_fdpic | FileCheck %s --match-full-lines --check-prefix=OSABI-ARMFDPIC-LLVM
+# RUN: llvm-readelf --file-headers %t.osabi.arm_fdpic | FileCheck %s --match-full-lines --check-prefix=OSABI-ARMFDPIC-GNU
+
+# OSABI-ARMFDPIC-LLVM: OS/ABI: ARM FDPIC (0x41)
+# OSABI-ARMFDPIC-GNU:  OS/ABI: ARM FDPIC
+
 ## Check all EM_TI_C6000 specific values.
 
 # RUN: yaml2obj %s -DOSABI=ELFOSABI_C6000_ELFABI -DMACHINE=EM_TI_C6000 -o %t.osabi.c6000.elfabi
diff --git a/llvm/test/tools/llvm-readobj/ELF/reloc-types-arm.test b/llvm/test/tools/llvm-readobj/ELF/reloc-types-arm.test
index 96d6cfed4df3e1..dafe01ba36afb1 100644
--- a/llvm/test/tools/llvm-readobj/ELF/reloc-types-arm.test
+++ b/llvm/test/tools/llvm-readobj/ELF/reloc-types-arm.test
@@ -135,6 +135,13 @@
 # CHECK: Type: R_ARM_THM_TLS_DESCSEQ16 (129)
 # CHECK: Type: R_ARM_THM_TLS_DESCSEQ32 (130)
 # CHECK: Type: R_ARM_IRELATIVE (160)
+# CHECK: Type: R_ARM_GOTFUNCDESC (161)
+# CHECK: Type: R_ARM_GOTOFFFUNCDESC (162)
+# CHECK: Type: R_ARM_FUNCDESC (163)
+# CHECK: Type: R_ARM_FUNCDESC_VALUE (164)
+# CHECK: Type: R_ARM_TLS_GD32_FDPIC (165)
+# CHECK: Type: R_ARM_TLS_LDM32_FDPIC (166)
+# CHECK: Type: R_ARM_TLS_IE32_FDPIC (167)
 
 --- !ELF
 FileHeader:
@@ -278,3 +285,10 @@ Sections:
       - Type: R_ARM_THM_TLS_DESCSEQ16
       - Type: R_ARM_THM_TLS_DESCSEQ32
       - Type: R_ARM_IRELATIVE
+      - Type: R_ARM_GOTFUNCDESC
+      - Type: R_ARM_GOTOFFFUNCDESC
+      - Type: R_ARM_FUNCDESC
+      - Type: R_ARM_FUNCDESC_VALUE
+      - Type: R_ARM_TLS_GD32_FDPIC
+      - Type: R_ARM_TLS_LDM32_FDPIC
+      - Type: R_ARM_TLS_IE32_FDPIC
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index fa39e04f4ce9c0..8b8c4aa8d842b5 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -1095,7 +1095,8 @@ const EnumEntry<unsigned> AMDGPUElfOSABI[] = {
 };
 
 const EnumEntry<unsigned> ARMElfOSABI[] = {
-  {"ARM", "ARM", ELF::ELFOSABI_ARM}
+    {"ARM", "ARM", ELF::ELFOSABI_ARM},
+    {"ARM FDPIC", "ARM FDPIC", ELF::ELFOSABI_ARM_FDPIC},
 };
 
 const EnumEntry<unsigned> C6000ElfOSABI[] = {

@llvmbot
Copy link
Collaborator

llvmbot commented Feb 18, 2024

@llvm/pr-subscribers-backend-arm

Author: Fangrui Song (MaskRay)

Changes

Linux kernel fs/binfmt_elf_fdpic.c supports FDPIC for MMU-less systems.
GCC/binutils/qemu support FDPIC ABI for ARM
(https://github.com/mickael-guene/fdpic_doc).
ARM FDPIC Toolchain and ABI provides a summary.

This patch implements FDPIC relocations to the integrated assembler.
There are 6 static relocations and 2 dynamic relocations, with
R_ARM_FUNCDESC as both static and dynamic.

gas requires --fdpic to assemble data relocations like .word f(FUNCDESC).
This patch adds MCTargetOptions::FDPIC and reports an error if FDPIC
is not set.


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

18 Files Affected:

  • (modified) llvm/include/llvm/BinaryFormat/ELF.h (+1)
  • (modified) llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def (+7)
  • (modified) llvm/include/llvm/MC/MCExpr.h (+6)
  • (modified) llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h (+4)
  • (modified) llvm/include/llvm/MC/MCTargetOptions.h (+1)
  • (modified) llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h (+2)
  • (modified) llvm/lib/MC/MCExpr.cpp (+8-7)
  • (modified) llvm/lib/MC/MCParser/AsmParser.cpp (+1-1)
  • (modified) llvm/lib/MC/MCTargetOptions.cpp (+1-1)
  • (modified) llvm/lib/MC/MCTargetOptionsCommandFlags.cpp (+5)
  • (modified) llvm/lib/ObjectYAML/ELFYAML.cpp (+1)
  • (modified) llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp (+34)
  • (modified) llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp (+4-1)
  • (modified) llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp (+23)
  • (added) llvm/test/MC/ARM/fdpic.s (+31)
  • (modified) llvm/test/tools/llvm-readobj/ELF/file-header-os-abi.test (+7)
  • (modified) llvm/test/tools/llvm-readobj/ELF/reloc-types-arm.test (+14)
  • (modified) llvm/tools/llvm-readobj/ELFDumper.cpp (+2-1)
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index 124bba76c1774c..bace3a92677a82 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -362,6 +362,7 @@ enum {
   ELFOSABI_AMDGPU_PAL = 65,    // AMD PAL runtime
   ELFOSABI_AMDGPU_MESA3D = 66, // AMD GCN GPUs (GFX6+) for MESA runtime
   ELFOSABI_ARM = 97,           // ARM
+  ELFOSABI_ARM_FDPIC = 65,     // ARM FDPIC
   ELFOSABI_C6000_ELFABI = 64,  // Bare-metal TMS320C6000
   ELFOSABI_C6000_LINUX = 65,   // Linux TMS320C6000
   ELFOSABI_STANDALONE = 255,   // Standalone (embedded) application
diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def
index 47084d1eb0aad5..7e9fe965241f25 100644
--- a/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def
+++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def
@@ -143,3 +143,10 @@ ELF_RELOC(R_ARM_THM_BF16,               0x88)
 ELF_RELOC(R_ARM_THM_BF12,               0x89)
 ELF_RELOC(R_ARM_THM_BF18,               0x8a)
 ELF_RELOC(R_ARM_IRELATIVE,              0xa0)
+ELF_RELOC(R_ARM_GOTFUNCDESC,            0xa1)
+ELF_RELOC(R_ARM_GOTOFFFUNCDESC,         0xa2)
+ELF_RELOC(R_ARM_FUNCDESC,               0xa3)
+ELF_RELOC(R_ARM_FUNCDESC_VALUE,         0xa4)
+ELF_RELOC(R_ARM_TLS_GD32_FDPIC,         0xa5)
+ELF_RELOC(R_ARM_TLS_LDM32_FDPIC,        0xa6)
+ELF_RELOC(R_ARM_TLS_IE32_FDPIC,         0xa7)
diff --git a/llvm/include/llvm/MC/MCExpr.h b/llvm/include/llvm/MC/MCExpr.h
index 67836292874f5f..b3119609372049 100644
--- a/llvm/include/llvm/MC/MCExpr.h
+++ b/llvm/include/llvm/MC/MCExpr.h
@@ -223,6 +223,12 @@ class MCSymbolRefExpr : public MCExpr {
     VK_SECREL,
     VK_SIZE,    // symbol@SIZE
     VK_WEAKREF, // The link between the symbols in .weakref foo, bar
+    VK_FUNCDESC,
+    VK_GOTFUNCDESC,
+    VK_GOTOFFFUNCDESC,
+    VK_TLSGD_FDPIC,
+    VK_TLSLDM_FDPIC,
+    VK_GOTTPOFF_FDPIC,
 
     VK_X86_ABS8,
     VK_X86_PLTOFF,
diff --git a/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h b/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h
index fe905f2c3ba5fe..7edd3f8ce4904c 100644
--- a/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h
+++ b/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h
@@ -525,6 +525,10 @@ class MCTargetAsmParser : public MCAsmParserExtension {
   // Return whether this parser accept star as start of statement
   virtual bool starIsStartOfStatement() { return false; };
 
+  virtual MCSymbolRefExpr::VariantKind
+  getVariantKindForName(StringRef Name) const {
+    return MCSymbolRefExpr::getVariantKindForName(Name);
+  }
   virtual const MCExpr *applyModifierToExpr(const MCExpr *E,
                                             MCSymbolRefExpr::VariantKind,
                                             MCContext &Ctx) {
diff --git a/llvm/include/llvm/MC/MCTargetOptions.h b/llvm/include/llvm/MC/MCTargetOptions.h
index e2dd1e0433dbe4..a7295879e15f0f 100644
--- a/llvm/include/llvm/MC/MCTargetOptions.h
+++ b/llvm/include/llvm/MC/MCTargetOptions.h
@@ -51,6 +51,7 @@ class MCTargetOptions {
   bool MCNoTypeCheck : 1;
   bool MCSaveTempLabels : 1;
   bool MCIncrementalLinkerCompatible : 1;
+  bool FDPIC : 1;
   bool ShowMCEncoding : 1;
   bool ShowMCInst : 1;
   bool AsmVerbose : 1;
diff --git a/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h b/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h
index 7f6ee6c8be224a..ba3784cab5b11d 100644
--- a/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h
+++ b/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h
@@ -29,6 +29,8 @@ std::optional<bool> getExplicitRelaxAll();
 
 bool getIncrementalLinkerCompatible();
 
+bool getFDPIC();
+
 int getDwarfVersion();
 
 bool getDwarf64();
diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp
index 80def6dfc24b1a..485fd1885ddb52 100644
--- a/llvm/lib/MC/MCExpr.cpp
+++ b/llvm/lib/MC/MCExpr.cpp
@@ -220,6 +220,7 @@ const MCSymbolRefExpr *MCSymbolRefExpr::create(StringRef Name, VariantKind Kind,
 
 StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
   switch (Kind) {
+    // clang-format off
   case VK_Invalid: return "<<invalid>>";
   case VK_None: return "<<none>>";
 
@@ -232,13 +233,16 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
   case VK_GOTPCREL: return "GOTPCREL";
   case VK_GOTPCREL_NORELAX: return "GOTPCREL_NORELAX";
   case VK_GOTTPOFF: return "GOTTPOFF";
+  case VK_GOTTPOFF_FDPIC: return "gottpoff_fdpic";
   case VK_INDNTPOFF: return "INDNTPOFF";
   case VK_NTPOFF: return "NTPOFF";
   case VK_GOTNTPOFF: return "GOTNTPOFF";
   case VK_PLT: return "PLT";
   case VK_TLSGD: return "TLSGD";
+  case VK_TLSGD_FDPIC: return "tlsgd_fdpic";
   case VK_TLSLD: return "TLSLD";
   case VK_TLSLDM: return "TLSLDM";
+  case VK_TLSLDM_FDPIC: return "tlsldm_fdpic";
   case VK_TPOFF: return "TPOFF";
   case VK_TPREL: return "TPREL";
   case VK_TLSCALL: return "tlscall";
@@ -253,6 +257,9 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
   case VK_SECREL: return "SECREL32";
   case VK_SIZE: return "SIZE";
   case VK_WEAKREF: return "WEAKREF";
+  case VK_FUNCDESC: return "FUNCDESC";
+  case VK_GOTFUNCDESC: return "GOTFUNCDESC";
+  case VK_GOTOFFFUNCDESC: return "GOTOFFFUNCDESC";
   case VK_X86_ABS8: return "ABS8";
   case VK_X86_PLTOFF: return "PLTOFF";
   case VK_ARM_NONE: return "none";
@@ -386,6 +393,7 @@ StringRef MCSymbolRefExpr::getVariantKindName(VariantKind Kind) {
   case VK_VE_TLS_GD_LO32: return "tls_gd_lo";
   case VK_VE_TPOFF_HI32: return "tpoff_hi";
   case VK_VE_TPOFF_LO32: return "tpoff_lo";
+    // clang-format on
   }
   llvm_unreachable("Invalid variant kind");
 }
@@ -493,13 +501,6 @@ MCSymbolRefExpr::getVariantKindForName(StringRef Name) {
     .Case("ie", VK_Hexagon_IE)
     .Case("ldgot", VK_Hexagon_LD_GOT)
     .Case("ldplt", VK_Hexagon_LD_PLT)
-    .Case("none", VK_ARM_NONE)
-    .Case("got_prel", VK_ARM_GOT_PREL)
-    .Case("target1", VK_ARM_TARGET1)
-    .Case("target2", VK_ARM_TARGET2)
-    .Case("prel31", VK_ARM_PREL31)
-    .Case("sbrel", VK_ARM_SBREL)
-    .Case("tlsldo", VK_ARM_TLSLDO)
     .Case("lo8", VK_AVR_LO8)
     .Case("hi8", VK_AVR_HI8)
     .Case("hlo8", VK_AVR_HLO8)
diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp
index 8e508dbdb1c69b..a1c32eee328643 100644
--- a/llvm/lib/MC/MCParser/AsmParser.cpp
+++ b/llvm/lib/MC/MCParser/AsmParser.cpp
@@ -1237,7 +1237,7 @@ bool AsmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc,
 
     // Lookup the symbol variant if used.
     if (!Split.second.empty()) {
-      Variant = MCSymbolRefExpr::getVariantKindForName(Split.second);
+      Variant = getTargetParser().getVariantKindForName(Split.second);
       if (Variant != MCSymbolRefExpr::VK_Invalid) {
         SymbolName = Split.first;
       } else if (MAI.doesAllowAtInName() && !MAI.useParensForSymbolVariant()) {
diff --git a/llvm/lib/MC/MCTargetOptions.cpp b/llvm/lib/MC/MCTargetOptions.cpp
index 07c6e752cb613d..bff4b8da2fb1b0 100644
--- a/llvm/lib/MC/MCTargetOptions.cpp
+++ b/llvm/lib/MC/MCTargetOptions.cpp
@@ -15,7 +15,7 @@ MCTargetOptions::MCTargetOptions()
     : MCRelaxAll(false), MCNoExecStack(false), MCFatalWarnings(false),
       MCNoWarn(false), MCNoDeprecatedWarn(false), MCNoTypeCheck(false),
       MCSaveTempLabels(false), MCIncrementalLinkerCompatible(false),
-      ShowMCEncoding(false), ShowMCInst(false), AsmVerbose(false),
+      FDPIC(false), ShowMCEncoding(false), ShowMCInst(false), AsmVerbose(false),
       PreserveAsmComments(true), Dwarf64(false),
       EmitDwarfUnwind(EmitDwarfUnwindType::Default),
       MCUseDwarfDirectory(DefaultDwarfDirectory),
diff --git a/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp b/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp
index 8a4923e4792fb5..fb8334d626cb8b 100644
--- a/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp
+++ b/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp
@@ -36,6 +36,7 @@ using namespace llvm;
 
 MCOPT_EXP(bool, RelaxAll)
 MCOPT(bool, IncrementalLinkerCompatible)
+MCOPT(bool, FDPIC)
 MCOPT(int, DwarfVersion)
 MCOPT(bool, Dwarf64)
 MCOPT(EmitDwarfUnwindType, EmitDwarfUnwind)
@@ -66,6 +67,9 @@ llvm::mc::RegisterMCTargetOptionsFlags::RegisterMCTargetOptionsFlags() {
           "emit an object file which can be used with an incremental linker"));
   MCBINDOPT(IncrementalLinkerCompatible);
 
+  static cl::opt<bool> FDPIC("fdpic", cl::desc("Use the FDPIC ABI"));
+  MCBINDOPT(FDPIC);
+
   static cl::opt<int> DwarfVersion("dwarf-version", cl::desc("Dwarf version"),
                                    cl::init(0));
   MCBINDOPT(DwarfVersion);
@@ -135,6 +139,7 @@ MCTargetOptions llvm::mc::InitMCTargetOptionsFromFlags() {
   MCTargetOptions Options;
   Options.MCRelaxAll = getRelaxAll();
   Options.MCIncrementalLinkerCompatible = getIncrementalLinkerCompatible();
+  Options.FDPIC = getFDPIC();
   Options.Dwarf64 = getDwarf64();
   Options.DwarfVersion = getDwarfVersion();
   Options.ShowMCInst = getShowMCInst();
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index de1ef2458152c8..9c1a28db592a1c 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -406,6 +406,7 @@ void ScalarEnumerationTraits<ELFYAML::ELF_ELFOSABI>::enumeration(
   ECase(ELFOSABI_AMDGPU_PAL);
   ECase(ELFOSABI_AMDGPU_MESA3D);
   ECase(ELFOSABI_ARM);
+  ECase(ELFOSABI_ARM_FDPIC);
   ECase(ELFOSABI_C6000_ELFABI);
   ECase(ELFOSABI_C6000_LINUX);
   ECase(ELFOSABI_STANDALONE);
diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
index c82ab57bdf80f1..37bfb76a494dee 100644
--- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
+++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
@@ -737,6 +737,9 @@ class ARMAsmParser : public MCTargetAsmParser {
   void ReportNearMisses(SmallVectorImpl<NearMissInfo> &NearMisses, SMLoc IDLoc,
                         OperandVector &Operands);
 
+  MCSymbolRefExpr::VariantKind
+  getVariantKindForName(StringRef Name) const override;
+
   void doBeforeLabelEmit(MCSymbol *Symbol, SMLoc IDLoc) override;
 
   void onLabelParsed(MCSymbol *Symbol) override;
@@ -11358,6 +11361,37 @@ bool ARMAsmParser::parseDirectiveARM(SMLoc L) {
   return false;
 }
 
+MCSymbolRefExpr::VariantKind
+ARMAsmParser::getVariantKindForName(StringRef Name) const {
+  return StringSwitch<MCSymbolRefExpr::VariantKind>(Name.lower())
+      .Case("funcdesc", MCSymbolRefExpr::VK_FUNCDESC)
+      .Case("got", MCSymbolRefExpr::VK_GOT)
+      .Case("got_prel", MCSymbolRefExpr::VK_ARM_GOT_PREL)
+      .Case("gotfuncdesc", MCSymbolRefExpr::VK_GOTFUNCDESC)
+      .Case("gotoff", MCSymbolRefExpr::VK_GOTOFF)
+      .Case("gotofffuncdesc", MCSymbolRefExpr::VK_GOTOFFFUNCDESC)
+      .Case("gottpoff", MCSymbolRefExpr::VK_GOTTPOFF)
+      .Case("gottpoff_fdpic", MCSymbolRefExpr::VK_GOTTPOFF_FDPIC)
+      .Case("imgrel", MCSymbolRefExpr::VK_COFF_IMGREL32)
+      .Case("none", MCSymbolRefExpr::VK_ARM_NONE)
+      .Case("plt", MCSymbolRefExpr::VK_PLT)
+      .Case("prel31", MCSymbolRefExpr::VK_ARM_PREL31)
+      .Case("sbrel", MCSymbolRefExpr::VK_ARM_SBREL)
+      .Case("secrel32", MCSymbolRefExpr::VK_SECREL)
+      .Case("target1", MCSymbolRefExpr::VK_ARM_TARGET1)
+      .Case("target2", MCSymbolRefExpr::VK_ARM_TARGET2)
+      .Case("tlscall", MCSymbolRefExpr::VK_TLSCALL)
+      .Case("tlsdesc", MCSymbolRefExpr::VK_TLSDESC)
+      .Case("tlsgd", MCSymbolRefExpr::VK_TLSGD)
+      .Case("tlsgd_fdpic", MCSymbolRefExpr::VK_TLSGD_FDPIC)
+      .Case("tlsld", MCSymbolRefExpr::VK_TLSLD)
+      .Case("tlsldm", MCSymbolRefExpr::VK_TLSLDM)
+      .Case("tlsldm_fdpic", MCSymbolRefExpr::VK_TLSLDM_FDPIC)
+      .Case("tlsldo", MCSymbolRefExpr::VK_ARM_TLSLDO)
+      .Case("tpoff", MCSymbolRefExpr::VK_TPOFF)
+      .Default(MCSymbolRefExpr::VK_Invalid);
+}
+
 void ARMAsmParser::doBeforeLabelEmit(MCSymbol *Symbol, SMLoc IDLoc) {
   // We need to flush the current implicit IT block on a label, because it is
   // not legal to branch into an IT block.
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
index 1d17bb349f24ba..6cd4badb7704b7 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
@@ -29,6 +29,7 @@
 #include "llvm/MC/MCSectionELF.h"
 #include "llvm/MC/MCSectionMachO.h"
 #include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
 #include "llvm/MC/MCValue.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/EndianStream.h"
@@ -1349,7 +1350,9 @@ static MCAsmBackend *createARMAsmBackend(const Target &T,
     return new ARMAsmBackendWinCOFF(T, STI.getTargetTriple().isThumb());
   case Triple::ELF:
     assert(TheTriple.isOSBinFormatELF() && "using ELF for non-ELF target");
-    uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TheTriple.getOS());
+    uint8_t OSABI = Options.FDPIC
+                        ? ELF::ELFOSABI_ARM_FDPIC
+                        : MCELFObjectTargetWriter::getOSABI(TheTriple.getOS());
     return new ARMAsmBackendELF(T, STI.getTargetTriple().isThumb(), OSABI,
                                 Endian);
   }
diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
index 44695a86c4e36c..baec4a5cb5215a 100644
--- a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
+++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
@@ -84,6 +84,11 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target,
   if (Kind >= FirstLiteralRelocationKind)
     return Kind - FirstLiteralRelocationKind;
   MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant();
+  auto CheckFDPIC = [&]() {
+    if (getOSABI() != ELF::ELFOSABI_ARM_FDPIC)
+      Ctx.reportError(Fixup.getLoc(),
+                      "relocation only supported in FDPIC mode");
+  };
 
   if (IsPCRel) {
     switch (Fixup.getTargetKind()) {
@@ -240,6 +245,24 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target,
       return ELF::R_ARM_TLS_LDM32;
     case MCSymbolRefExpr::VK_ARM_TLSDESCSEQ:
       return ELF::R_ARM_TLS_DESCSEQ;
+    case MCSymbolRefExpr::VK_FUNCDESC:
+      CheckFDPIC();
+      return ELF::R_ARM_FUNCDESC;
+    case MCSymbolRefExpr::VK_GOTFUNCDESC:
+      CheckFDPIC();
+      return ELF::R_ARM_GOTFUNCDESC;
+    case MCSymbolRefExpr::VK_GOTOFFFUNCDESC:
+      CheckFDPIC();
+      return ELF::R_ARM_GOTOFFFUNCDESC;
+    case MCSymbolRefExpr::VK_TLSGD_FDPIC:
+      CheckFDPIC();
+      return ELF::R_ARM_TLS_GD32_FDPIC;
+    case MCSymbolRefExpr::VK_TLSLDM_FDPIC:
+      CheckFDPIC();
+      return ELF::R_ARM_TLS_LDM32_FDPIC;
+    case MCSymbolRefExpr::VK_GOTTPOFF_FDPIC:
+      CheckFDPIC();
+      return ELF::R_ARM_TLS_IE32_FDPIC;
     }
   case ARM::fixup_arm_condbranch:
   case ARM::fixup_arm_uncondbranch:
diff --git a/llvm/test/MC/ARM/fdpic.s b/llvm/test/MC/ARM/fdpic.s
new file mode 100644
index 00000000000000..4f36393d9110e8
--- /dev/null
+++ b/llvm/test/MC/ARM/fdpic.s
@@ -0,0 +1,31 @@
+# RUN: llvm-mc -triple=armv7-linux-gnueabi %s | FileCheck %s --check-prefix=ASM
+# RUN: llvm-mc -filetype=obj -triple=armv7-linux-gnueabi --fdpic %s | llvm-readelf -h -r - | FileCheck %s
+
+# RUN: not llvm-mc -filetype=obj -triple=armv7-linux-gnueabi %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR
+
+# ASM:      .long f(FUNCDESC)
+# ASM-NEXT: .long f(GOTFUNCDESC)
+# ASM-NEXT: .long f(GOTOFFFUNCDESC)
+
+# CHECK:      OS/ABI: ARM FDPIC
+# CHECK:      Machine: ARM
+# CHECK:      Flags: 0x5000000
+
+# CHECK:      R_ARM_FUNCDESC        00000000 f
+# CHECK-NEXT: R_ARM_GOTFUNCDESC     00000000 f
+# CHECK-NEXT: R_ARM_GOTOFFFUNCDESC  00000000 f
+# CHECK-NEXT: R_ARM_TLS_GD32_FDPIC  00000000 tls
+# CHECK-NEXT: R_ARM_TLS_LDM32_FDPIC 00000000 tls
+# CHECK-NEXT: R_ARM_TLS_IE32_FDPIC  00000000 tls
+
+.data
+# ERR: [[#@LINE+1]]:7: error: relocation only supported in FDPIC mode
+.long f(FUNCDESC)
+# ERR: [[#@LINE+1]]:7: error: relocation only supported in FDPIC mode
+.long f(GOTFUNCDESC)
+# ERR: [[#@LINE+1]]:7: error: relocation only supported in FDPIC mode
+.long f(GOTOFFFUNCDESC)
+# ERR: [[#@LINE+1]]:7: error: relocation only supported in FDPIC mode
+.long tls(tlsgd_fdpic)
+.long tls(tlsldm_fdpic)
+.long tls(gottpoff_fdpic)
diff --git a/llvm/test/tools/llvm-readobj/ELF/file-header-os-abi.test b/llvm/test/tools/llvm-readobj/ELF/file-header-os-abi.test
index eb60d2a021af63..a48346d6b9c85e 100644
--- a/llvm/test/tools/llvm-readobj/ELF/file-header-os-abi.test
+++ b/llvm/test/tools/llvm-readobj/ELF/file-header-os-abi.test
@@ -192,6 +192,13 @@ FileHeader:
 # OSABI-ARM-LLVM: OS/ABI: ARM (0x61)
 # OSABI-ARM-GNU:  OS/ABI: ARM
 
+# RUN: yaml2obj %s -DOSABI=ELFOSABI_ARM_FDPIC -DMACHINE=EM_ARM -o %t.osabi.arm_fdpic
+# RUN: llvm-readobj --file-headers %t.osabi.arm_fdpic | FileCheck %s --match-full-lines --check-prefix=OSABI-ARMFDPIC-LLVM
+# RUN: llvm-readelf --file-headers %t.osabi.arm_fdpic | FileCheck %s --match-full-lines --check-prefix=OSABI-ARMFDPIC-GNU
+
+# OSABI-ARMFDPIC-LLVM: OS/ABI: ARM FDPIC (0x41)
+# OSABI-ARMFDPIC-GNU:  OS/ABI: ARM FDPIC
+
 ## Check all EM_TI_C6000 specific values.
 
 # RUN: yaml2obj %s -DOSABI=ELFOSABI_C6000_ELFABI -DMACHINE=EM_TI_C6000 -o %t.osabi.c6000.elfabi
diff --git a/llvm/test/tools/llvm-readobj/ELF/reloc-types-arm.test b/llvm/test/tools/llvm-readobj/ELF/reloc-types-arm.test
index 96d6cfed4df3e1..dafe01ba36afb1 100644
--- a/llvm/test/tools/llvm-readobj/ELF/reloc-types-arm.test
+++ b/llvm/test/tools/llvm-readobj/ELF/reloc-types-arm.test
@@ -135,6 +135,13 @@
 # CHECK: Type: R_ARM_THM_TLS_DESCSEQ16 (129)
 # CHECK: Type: R_ARM_THM_TLS_DESCSEQ32 (130)
 # CHECK: Type: R_ARM_IRELATIVE (160)
+# CHECK: Type: R_ARM_GOTFUNCDESC (161)
+# CHECK: Type: R_ARM_GOTOFFFUNCDESC (162)
+# CHECK: Type: R_ARM_FUNCDESC (163)
+# CHECK: Type: R_ARM_FUNCDESC_VALUE (164)
+# CHECK: Type: R_ARM_TLS_GD32_FDPIC (165)
+# CHECK: Type: R_ARM_TLS_LDM32_FDPIC (166)
+# CHECK: Type: R_ARM_TLS_IE32_FDPIC (167)
 
 --- !ELF
 FileHeader:
@@ -278,3 +285,10 @@ Sections:
       - Type: R_ARM_THM_TLS_DESCSEQ16
       - Type: R_ARM_THM_TLS_DESCSEQ32
       - Type: R_ARM_IRELATIVE
+      - Type: R_ARM_GOTFUNCDESC
+      - Type: R_ARM_GOTOFFFUNCDESC
+      - Type: R_ARM_FUNCDESC
+      - Type: R_ARM_FUNCDESC_VALUE
+      - Type: R_ARM_TLS_GD32_FDPIC
+      - Type: R_ARM_TLS_LDM32_FDPIC
+      - Type: R_ARM_TLS_IE32_FDPIC
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index fa39e04f4ce9c0..8b8c4aa8d842b5 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -1095,7 +1095,8 @@ const EnumEntry<unsigned> AMDGPUElfOSABI[] = {
 };
 
 const EnumEntry<unsigned> ARMElfOSABI[] = {
-  {"ARM", "ARM", ELF::ELFOSABI_ARM}
+    {"ARM", "ARM", ELF::ELFOSABI_ARM},
+    {"ARM FDPIC", "ARM FDPIC", ELF::ELFOSABI_ARM_FDPIC},
 };
 
 const EnumEntry<unsigned> C6000ElfOSABI[] = {

Copy link
Collaborator

@jh7370 jh7370 left a comment

Choose a reason for hiding this comment

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

BinaryFormat/llvm-readobj aspects looks fine to me, but the assembler side of things goes a bit over my head, so you'll need someone else to review that area.

Comment on lines +150 to +152
ELF_RELOC(R_ARM_TLS_GD32_FDPIC, 0xa5)
ELF_RELOC(R_ARM_TLS_LDM32_FDPIC, 0xa6)
ELF_RELOC(R_ARM_TLS_IE32_FDPIC, 0xa7)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I might be looking in the wrong place, but I couldn't see the values for these three relocations defined in the doc you link in the description?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for checking. https://github.com/mickael-guene/fdpic_doc doesn't define these values, but binutils-gdb defines the values (also referenced by projects like https://github.com/cutty/uld-fdpic/blob/master/include/elf.h). I've checked that llvm-readelf -r output matches readelf -r.

MaskRay added a commit that referenced this pull request Feb 19, 2024
Similar to #75661. Currently, there is only ELFOSABI_ARM, but I plan to
add ELFOSABI_ARM_FDPIC in a subsequent patch #82187
Copy link
Collaborator

@smithp35 smithp35 left a comment

Choose a reason for hiding this comment

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

I've checked over the implementation with binutils. Out of interest are you planning on implementing all of fdpic or just enough to get assembler/linker support working?

If you are there are some other GNU options that may be useful to look at as possible intermediate steps. In particular -mno-pic-data-is-text-relative https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html#index-mpic-data-is-text-relative this uses a single static base for the GOT which is sufficient for position independent executables but no shared library support.

@@ -223,6 +223,12 @@ class MCSymbolRefExpr : public MCExpr {
VK_SECREL,
VK_SIZE, // symbol@SIZE
VK_WEAKREF, // The link between the symbols in .weakref foo, bar
VK_FUNCDESC,
Copy link
Collaborator

Choose a reason for hiding this comment

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

While VK_TLSGD_FDPIC are somewhat self documenting, it may be worth a comment for VK_FUNCDESC to VK_GOTOFFUNCDESC to state that these are for FDPIC.

@@ -11358,6 +11361,37 @@ bool ARMAsmParser::parseDirectiveARM(SMLoc L) {
return false;
}

MCSymbolRefExpr::VariantKind
ARMAsmParser::getVariantKindForName(StringRef Name) const {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I can see a design trade-off here. By implementing only the expressions that are supported on Arm we find some problems earlier, however we may miss out on generic expressions added to the MCSymbolRefExpr::getVariantKindForName but not for Arm. I can see these getting out of sync.

Would it be worth calling the MCSymbolRefExpr::getVariantKindForName if the Name isn't found? Ideally we could refactor so that MCSymbolRefExpr::getVariantKindForName contains only generic and no target specific variant kinds.

Copy link
Member Author

@MaskRay MaskRay Feb 20, 2024

Choose a reason for hiding this comment

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

Ideally we could refactor so that MCSymbolRefExpr::getVariantKindForName contains only generic and no target specific variant kinds.

I agree with this. I believe that binutils doesn't have a generic operand modifier, so ideally MCSymbolRefExpr::getVariantKindForName should be a pure virtual function. We will therefore be able to report the unknown modifier error at parsing time, instead of at ELFObjectWriter.

Unfortunately, many architectures add arch-specific modifiers in this list. We should eventually move them to respective lib/Target/XXX files.

In this patch I am only focusing on the ARM specific operand modifiers.

.Case("tlsldm_fdpic", MCSymbolRefExpr::VK_TLSLDM_FDPIC)
.Case("tlsldo", MCSymbolRefExpr::VK_ARM_TLSLDO)
.Case("tpoff", MCSymbolRefExpr::VK_TPOFF)
.Default(MCSymbolRefExpr::VK_Invalid);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think Arm supports "tlddescseq" from checking this list against binutils. I don't think this was in the original list though.

Copy link
Member Author

@MaskRay MaskRay Feb 20, 2024

Choose a reason for hiding this comment

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

Yes, the binutils code contains tlsdescseq, but there is no (tlsdescseq) occurrence. GCC doesn't emit (tlsdescseq), either.
I suspect that the entry in binutils is not used.

There is a directive named .tlsdescseq (https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-ARM.txt), which is for TLS optimization. But GCC doesn't emit this directive.

Clang doesn't support -mtls-dialect=gnu2 for ARM.

@@ -84,6 +84,11 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target,
if (Kind >= FirstLiteralRelocationKind)
return Kind - FirstLiteralRelocationKind;
MCSymbolRefExpr::VariantKind Modifier = Target.getAccessVariant();
auto CheckFDPIC = [&]() {
if (getOSABI() != ELF::ELFOSABI_ARM_FDPIC)
Ctx.reportError(Fixup.getLoc(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the error message could be improved by giving out the string representation of the relocation code. For example:
relocation R_ARM_FUNCDESC only supported in FDPIC mode. I think this should be relatively easy to add.

@MaskRay
Copy link
Member Author

MaskRay commented Feb 20, 2024

I've checked over the implementation with binutils. Out of interest are you planning on implementing all of fdpic or just enough to get assembler/linker support working?

Yes, I plan to implement the codegen part to help me understand FDPIC better and the codegen part of LLVM...
(I need to study PPC ELFv1 function descriptors.)

If you are there are some other GNU options that may be useful to look at as possible intermediate steps. In particular -mno-pic-data-is-text-relative gcc.gnu.org/onlinedocs/gcc/ARM-Options.html#index-mpic-data-is-text-relative this uses a single static base for the GOT which is sufficient for position independent executables but no shared library support.

Thanks for the pointers. Yes, it looks like ARM added -mpic-data-is-text-relative first then s390x ported it.
The option is similar to -msep-data (m68k/bfin) and probably used for MMU-less systems without dynamic linking support. I have work-in-progress notes at https://maskray.me/blog/2024-02-20-mmu-less-systems-and-fdpic

Created using spr 1.3.4
Copy link
Collaborator

@smithp35 smithp35 left a comment

Choose a reason for hiding this comment

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

Thanks for the update, I'm happy with the changes. I would like to see more options for position independent code in embedded systems.

The other related embedded position independent option for microcontrollers that I'm aware of is -fropi and -frwpi which is already implemented in clang. This is relocation free with just one static data area accessed as an offset from r9. It works quite well for C code, but doesn't support C++ (vtables and RTTI need relocations) and some forms of static initialisation.

Copy link
Collaborator

@smithp35 smithp35 left a comment

Choose a reason for hiding this comment

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

Forgot to approve

@MaskRay
Copy link
Member Author

MaskRay commented Feb 21, 2024

Thanks for the update, I'm happy with the changes. I would like to see more options for position independent code in embedded systems.

The other related embedded position independent option for microcontrollers that I'm aware of is -fropi and -frwpi which is already implemented in clang. This is relocation free with just one static data area accessed as an offset from r9. It works quite well for C code, but doesn't support C++ (vtables and RTTI need relocations) and some forms of static initialisation.

Thanks for the comment. ROPI/RWPI seems similar to -mno-pic-data-is-text-relative.

In 2013, -mno-pic-data-is-text-relative, generalized from the ARM VxWorks RTP port, was added to assume that text and data segments don't have a fixed displacement. On non-VxWorks-RTP targets, -mno-pic-data-is-text-relative implies -msingle-pic-base:

Treat the register used for PIC addressing as read-only, rather than loading it in the prologue for each function. The runtime system is responsible for initializing this register with an appropriate value before execution begins.

r9 is used as the static base (`arm_pic_register) in the position-independent data model to access the data segment. Since r9 is not changed, dynamic linking seems unsupported as a DSO needs a different data segment.

@MaskRay MaskRay merged commit 2f900f7 into users/MaskRay/spr/main.armmc-support-fdpic-relocations Feb 21, 2024
5 checks passed
@MaskRay MaskRay deleted the users/MaskRay/spr/armmc-support-fdpic-relocations branch February 21, 2024 17:40
MaskRay added a commit that referenced this pull request Feb 21, 2024
Linux kernel fs/binfmt_elf_fdpic.c supports FDPIC for MMU-less systems.
GCC/binutils/qemu support FDPIC ABI for ARM
(https://github.com/mickael-guene/fdpic_doc).
_ARM FDPIC Toolchain and ABI_ provides a summary.

This patch implements FDPIC relocations to the integrated assembler.
There are 6 static relocations and 2 dynamic relocations, with
R_ARM_FUNCDESC as both static and dynamic.

gas requires `--fdpic` to assemble data relocations like `.word f(FUNCDESC)`.
This patch adds `MCTargetOptions::FDPIC` and reports an error if FDPIC
is not set.

Pull Request: #82187
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.

None yet

4 participants