[clang] [llvm] [MC,llvm-readobj,yaml2obj] Support CREL relocation format (PR #91280)

Fangrui Song via cfe-commits cfe-commits at lists.llvm.org
Thu May 9 23:37:21 PDT 2024


https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/91280

>From a0cfafb82db825512b0ca44778fa9d4bb435563d Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Mon, 6 May 2024 15:37:50 -0700
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
 =?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.5-bogner
---
 clang/include/clang/Basic/CodeGenOptions.def  |   1 +
 .../clang/Basic/DiagnosticDriverKinds.td      |   3 +
 clang/include/clang/Driver/Options.td         |   3 +
 clang/lib/CodeGen/BackendUtil.cpp             |   1 +
 clang/lib/Driver/ToolChains/Clang.cpp         |  18 ++
 clang/lib/Driver/ToolChains/CommonArgs.cpp    |  21 ++
 clang/test/Driver/crel.c                      |  24 +++
 clang/test/Misc/cc1as-crel.s                  |   6 +
 clang/tools/driver/cc1as_main.cpp             |   6 +
 .../include/llvm/BinaryFormat/DynamicTags.def |   2 +
 llvm/include/llvm/BinaryFormat/ELF.h          |   1 +
 llvm/include/llvm/MC/MCTargetOptions.h        |   3 +
 .../llvm/MC/MCTargetOptionsCommandFlags.h     |   1 +
 llvm/include/llvm/Object/ELF.h                |   5 +
 llvm/include/llvm/Object/ELFObjectFile.h      |  70 ++++++-
 llvm/include/llvm/Object/ELFTypes.h           |  25 +++
 llvm/lib/MC/ELFObjectWriter.cpp               | 110 ++++++++---
 llvm/lib/MC/MCTargetOptionsCommandFlags.cpp   |   6 +
 llvm/lib/Object/ELF.cpp                       |  63 ++++++
 llvm/lib/ObjectYAML/ELFEmitter.cpp            |  60 +++++-
 llvm/lib/ObjectYAML/ELFYAML.cpp               |   2 +
 llvm/test/MC/ELF/crel-32.s                    |  16 ++
 llvm/test/MC/ELF/crel.s                       | 100 ++++++++++
 llvm/test/tools/llvm-readobj/ELF/crel.test    | 180 ++++++++++++++++++
 .../tools/llvm-readobj/ELF/dynamic-reloc.test |  31 ++-
 .../llvm-readobj/ELF/relocation-errors.test   |  19 +-
 .../yaml2obj/ELF/dynamic-relocations.yaml     |   5 +-
 .../yaml2obj/ELF/reloc-sec-entry-size.yaml    |   5 +
 .../tools/yaml2obj/ELF/relocation-crel.yaml   |  63 ++++++
 .../ELF/relocation-missing-symbol.yaml        |   3 +-
 .../tools/yaml2obj/ELF/relocation-type.yaml   |   4 +-
 llvm/tools/llvm-readobj/ELFDumper.cpp         |  81 +++++++-
 32 files changed, 883 insertions(+), 55 deletions(-)
 create mode 100644 clang/test/Driver/crel.c
 create mode 100644 clang/test/Misc/cc1as-crel.s
 create mode 100644 llvm/test/MC/ELF/crel-32.s
 create mode 100644 llvm/test/MC/ELF/crel.s
 create mode 100644 llvm/test/tools/llvm-readobj/ELF/crel.test
 create mode 100644 llvm/test/tools/yaml2obj/ELF/relocation-crel.yaml

diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 340b08dd7e2a3..3229f77eef1fc 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -36,6 +36,7 @@ VALUE_CODEGENOPT(Name, Bits, Default)
 #endif
 
 CODEGENOPT(DisableIntegratedAS, 1, 0) ///< -no-integrated-as
+CODEGENOPT(Crel, 1, 0) ///< -Wa,--crel
 CODEGENOPT(RelaxELFRelocations, 1, 1) ///< -Wa,-mrelax-relocations={yes,no}
 CODEGENOPT(AsmVerbose        , 1, 0) ///< -dA, -fverbose-asm.
 CODEGENOPT(PreserveAsmComments, 1, 1) ///< -dA, -fno-preserve-as-comments.
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 9781fcaa4ff5e..e9cea8967c133 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -809,6 +809,9 @@ def warn_drv_missing_multilib : Warning<
 def note_drv_available_multilibs : Note<
   "available multilibs are:%0">;
 
+def err_drv_experimental_crel : Error<
+  "-Wa,--experimental-crel must be specified to use -Wa,--crel. CREL is experimental and takes a non-standard section type code">;
+
 def warn_android_unversioned_fallback : Warning<
   "Using unversioned Android target directory %0 for target %1. Unversioned"
   " directories will not be used in Clang 19. Provide a versioned directory"
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index c9f7c4e5f718b..55f0c9aff6a49 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -6958,6 +6958,9 @@ def massembler_no_warn : Flag<["-"], "massembler-no-warn">,
 def massembler_fatal_warnings : Flag<["-"], "massembler-fatal-warnings">,
   HelpText<"Make assembler warnings fatal">,
   MarshallingInfoFlag<CodeGenOpts<"FatalWarnings">>;
+def crel : Flag<["--"], "crel">,
+  HelpText<"Enable CREL relocation format (ELF only)">,
+  MarshallingInfoFlag<CodeGenOpts<"Crel">>;
 def mrelax_relocations_no : Flag<["-"], "mrelax-relocations=no">,
     HelpText<"Disable x86 relax relocations">,
     MarshallingInfoNegativeFlag<CodeGenOpts<"RelaxELFRelocations">>;
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 22c3f8642ad8e..ed37085d69ac5 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -474,6 +474,7 @@ static bool initTargetOptions(DiagnosticsEngine &Diags,
   Options.MCOptions.AsmVerbose = CodeGenOpts.AsmVerbose;
   Options.MCOptions.Dwarf64 = CodeGenOpts.Dwarf64;
   Options.MCOptions.PreserveAsmComments = CodeGenOpts.PreserveAsmComments;
+  Options.MCOptions.Crel = CodeGenOpts.Crel;
   Options.MCOptions.X86RelaxRelocations = CodeGenOpts.RelaxELFRelocations;
   Options.MCOptions.CompressDebugSections =
       CodeGenOpts.getCompressDebugSections();
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 0a2ea96de7382..0f4d8aa3ad793 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2467,6 +2467,8 @@ static void CollectArgsForIntegratedAssembler(Compilation &C,
   // arg after parsing the '-I' arg.
   bool TakeNextArg = false;
 
+  const llvm::Triple &Triple = C.getDefaultToolChain().getTriple();
+  bool Crel = false, ExperimentalCrel = false;
   bool UseRelaxRelocations = C.getDefaultToolChain().useRelaxRelocations();
   bool UseNoExecStack = false;
   const char *MipsTargetFeature = nullptr;
@@ -2590,6 +2592,12 @@ static void CollectArgsForIntegratedAssembler(Compilation &C,
                  Value == "-nocompress-debug-sections" ||
                  Value == "--nocompress-debug-sections") {
         CmdArgs.push_back(Value.data());
+      } else if (Value == "--crel") {
+        Crel = true;
+      } else if (Value == "--no-crel") {
+        Crel = false;
+      } else if (Value == "--experimental-crel") {
+        ExperimentalCrel = true;
       } else if (Value == "-mrelax-relocations=yes" ||
                  Value == "--mrelax-relocations=yes") {
         UseRelaxRelocations = true;
@@ -2655,6 +2663,16 @@ static void CollectArgsForIntegratedAssembler(Compilation &C,
   }
   if (ImplicitIt.size())
     AddARMImplicitITArgs(Args, CmdArgs, ImplicitIt);
+  if (Crel) {
+    if (!ExperimentalCrel)
+      D.Diag(diag::err_drv_experimental_crel);
+    if (Triple.isOSBinFormatELF() && !Triple.isMIPS()) {
+      CmdArgs.push_back("--crel");
+    } else {
+      D.Diag(diag::err_drv_unsupported_opt_for_target)
+          << "-Wa,--crel" << D.getTargetTriple();
+    }
+  }
   if (!UseRelaxRelocations)
     CmdArgs.push_back("-mrelax-relocations=no");
   if (UseNoExecStack)
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 6796b43a15502..8ba45aeafd056 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1072,6 +1072,27 @@ void tools::addLTOOptions(const ToolChain &ToolChain, const ArgList &Args,
 
   addMachineOutlinerArgs(D, Args, CmdArgs, ToolChain.getEffectiveTriple(),
                          /*IsLTO=*/true, PluginOptPrefix);
+
+  for (const Arg *A : Args.filtered(options::OPT_Wa_COMMA)) {
+    bool Crel = false;
+    for (StringRef V : A->getValues()) {
+      if (V == "--crel")
+        Crel = true;
+      else if (V == "--no-crel")
+        Crel = false;
+      else
+        continue;
+      A->claim();
+    }
+    if (Crel) {
+      if (Triple.isOSBinFormatELF() && !Triple.isMIPS()) {
+        CmdArgs.push_back(Args.MakeArgString(Twine(PluginOptPrefix) + "-crel"));
+      } else {
+        D.Diag(diag::err_drv_unsupported_opt_for_target)
+            << "-Wa,--crel" << D.getTargetTriple();
+      }
+    }
+  }
 }
 
 /// Adds the '-lcgpu' and '-lmgpu' libraries to the compilation to include the
diff --git a/clang/test/Driver/crel.c b/clang/test/Driver/crel.c
new file mode 100644
index 0000000000000..0dc0507705d72
--- /dev/null
+++ b/clang/test/Driver/crel.c
@@ -0,0 +1,24 @@
+// RUN: not %clang -### -c --target=x86_64 -Wa,--crel %s 2>&1 | FileCheck %s --check-prefix=NOEXP
+
+// NOEXP: error: -Wa,--experimental-crel must be specified to use -Wa,--crel. CREL is experimental and takes a non-standard section type code
+
+// RUN: %clang -### -c --target=x86_64 -Wa,--crel,--experimental-crel %s 2>&1 | FileCheck %s
+// RUN: %clang -### -c --target=x86_64 -Wa,--crel,--no-crel,--experimental-crel %s 2>&1 | FileCheck %s --check-prefix=NO
+// RUN: not %clang -### -c --target=arm64-apple-darwin -Wa,--crel,--experimental-crel %s 2>&1 | FileCheck %s --check-prefix=ERR
+// RUN: not %clang -### -c --target=mips64 -Wa,--crel,--experimental-crel %s 2>&1 | FileCheck %s --check-prefix=ERR
+
+// RUN: %clang -### -c --target=aarch64 -Werror -Wa,--crel,--experimental-crel -x assembler %s -Werror 2>&1 | FileCheck %s --check-prefix=ASM
+// RUN: not %clang -### -c --target=mips64 -Wa,--crel,--experimental-crel -x assembler %s 2>&1 | FileCheck %s --check-prefix=ERR
+
+// CHECK: "-cc1" {{.*}}"--crel"
+// NO:     "-cc1"
+// NO-NOT: "--crel"
+// ASM:   "-cc1as" {{.*}}"--crel"
+// ERR: error: unsupported option '-Wa,--crel' for target '{{.*}}'
+
+/// Don't bother with --experimental-crel for LTO.
+// RUN: %clang -### --target=x86_64-linux -Werror -flto -Wa,--crel %s 2>&1 | FileCheck %s --check-prefix=LTO
+// LTO:       "-plugin-opt=-crel"
+
+// RUN: touch %t.o
+// RUN: not %clang -### --target=mips64-linux-gnu -flto -Wa,--crel %t.o 2>&1 | FileCheck %s --check-prefix=ERR
diff --git a/clang/test/Misc/cc1as-crel.s b/clang/test/Misc/cc1as-crel.s
new file mode 100644
index 0000000000000..953f2a1f3d3bc
--- /dev/null
+++ b/clang/test/Misc/cc1as-crel.s
@@ -0,0 +1,6 @@
+// REQUIRES: x86-registered-target
+// RUN: %clang -cc1as -triple x86_64 %s -filetype obj --crel -o %t
+// RUN: llvm-readelf -S %t | FileCheck %s
+
+// CHECK: .crel.text CREL
+call foo
diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp
index 86afe22fac24c..9d53cca7a0bf5 100644
--- a/clang/tools/driver/cc1as_main.cpp
+++ b/clang/tools/driver/cc1as_main.cpp
@@ -164,6 +164,9 @@ struct AssemblerInvocation {
   LLVM_PREFERRED_TYPE(bool)
   unsigned EmitCompactUnwindNonCanonical : 1;
 
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned Crel : 1;
+
   /// The name of the relocation model to use.
   std::string RelocationModel;
 
@@ -204,6 +207,7 @@ struct AssemblerInvocation {
     EmbedBitcode = 0;
     EmitDwarfUnwind = EmitDwarfUnwindType::Default;
     EmitCompactUnwindNonCanonical = false;
+    Crel = false;
   }
 
   static bool CreateFromArgs(AssemblerInvocation &Res,
@@ -373,6 +377,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
 
   Opts.EmitCompactUnwindNonCanonical =
       Args.hasArg(OPT_femit_compact_unwind_non_canonical);
+  Opts.Crel = Args.hasArg(OPT_crel);
 
   Opts.AsSecureLogFile = Args.getLastArgValue(OPT_as_secure_log_file);
 
@@ -429,6 +434,7 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts,
   MCOptions.MCRelaxAll = Opts.RelaxAll;
   MCOptions.EmitDwarfUnwind = Opts.EmitDwarfUnwind;
   MCOptions.EmitCompactUnwindNonCanonical = Opts.EmitCompactUnwindNonCanonical;
+  MCOptions.Crel = Opts.Crel;
   MCOptions.X86RelaxRelocations = Opts.RelaxELFRelocations;
   MCOptions.CompressDebugSections = Opts.CompressDebugSections;
   MCOptions.AsSecureLogFile = Opts.AsSecureLogFile;
diff --git a/llvm/include/llvm/BinaryFormat/DynamicTags.def b/llvm/include/llvm/BinaryFormat/DynamicTags.def
index 1502d375f5c45..d67a1e6de3678 100644
--- a/llvm/include/llvm/BinaryFormat/DynamicTags.def
+++ b/llvm/include/llvm/BinaryFormat/DynamicTags.def
@@ -86,6 +86,8 @@ DYNAMIC_TAG(RELRSZ, 35)  // Size of Relr relocation table.
 DYNAMIC_TAG(RELR, 36)    // Address of relocation table (Relr entries).
 DYNAMIC_TAG(RELRENT, 37) // Size of a Relr relocation entry.
 
+DYNAMIC_TAG(CREL,  38)   // CREL relocation table
+
 DYNAMIC_TAG_MARKER(LOOS, 0x60000000)   // Start of environment specific tags.
 DYNAMIC_TAG_MARKER(HIOS, 0x6FFFFFFF)   // End of environment specific tags.
 DYNAMIC_TAG_MARKER(LOPROC, 0x70000000) // Start of processor specific tags.
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index f296acc2ca4bb..b5c546d8b67e7 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1079,6 +1079,7 @@ enum : unsigned {
   // Experimental support for SHT_RELR sections. For details, see proposal
   // at https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg
   SHT_RELR = 19,         // Relocation entries; only offsets.
+  SHT_CREL = 20,         // CREL relocation entries
   SHT_LOOS = 0x60000000, // Lowest operating system-specific type.
   // Android packed relocation section types.
   // https://android.googlesource.com/platform/bionic/+/6f12bfece5dcc01325e0abba56a46b1bcf991c69/tools/relocation_packer/src/elf_file.cc#37
diff --git a/llvm/include/llvm/MC/MCTargetOptions.h b/llvm/include/llvm/MC/MCTargetOptions.h
index 0cf2806bd4804..98317712250bf 100644
--- a/llvm/include/llvm/MC/MCTargetOptions.h
+++ b/llvm/include/llvm/MC/MCTargetOptions.h
@@ -61,6 +61,9 @@ class MCTargetOptions {
 
   bool Dwarf64 : 1;
 
+  // Use CREL relocation format for ELF.
+  bool Crel = false;
+
   // If true, prefer R_X86_64_[REX_]GOTPCRELX to R_X86_64_GOTPCREL on x86-64
   // ELF.
   bool X86RelaxRelocations = true;
diff --git a/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h b/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h
index fc35eea09c4b3..837f148e8f4ab 100644
--- a/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h
+++ b/llvm/include/llvm/MC/MCTargetOptionsCommandFlags.h
@@ -49,6 +49,7 @@ bool getNoDeprecatedWarn();
 
 bool getNoTypeCheck();
 
+bool getCrel();
 bool getX86RelaxRelocations();
 
 std::string getABIName();
diff --git a/llvm/include/llvm/Object/ELF.h b/llvm/include/llvm/Object/ELF.h
index 78b6b40cbddf6..26e38075f8db0 100644
--- a/llvm/include/llvm/Object/ELF.h
+++ b/llvm/include/llvm/Object/ELF.h
@@ -321,6 +321,11 @@ class ELFFile {
 
   std::vector<Elf_Rel> decode_relrs(Elf_Relr_Range relrs) const;
 
+  uint64_t crelHeader(ArrayRef<uint8_t> Content) const;
+  using RelsOrRelas = std::pair<std::vector<Elf_Rel>, std::vector<Elf_Rela>>;
+  Expected<RelsOrRelas> decodeCrel(ArrayRef<uint8_t> Content) const;
+  Expected<RelsOrRelas> crels(const Elf_Shdr &Sec) const;
+
   Expected<std::vector<Elf_Rela>> android_relas(const Elf_Shdr &Sec) const;
 
   /// Iterate over program header table.
diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h
index 8cc09e7fd7d55..505ea9ede313a 100644
--- a/llvm/include/llvm/Object/ELFObjectFile.h
+++ b/llvm/include/llvm/Object/ELFObjectFile.h
@@ -29,6 +29,7 @@
 #include "llvm/Support/ELFAttributes.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/LEB128.h"
 #include "llvm/Support/MemoryBufferRef.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/TargetParser/SubtargetFeature.h"
@@ -292,6 +293,9 @@ template <class ELFT> class ELFObjectFile : public ELFObjectFileBase {
   const Elf_Shdr *DotSymtabSec = nullptr; // Symbol table section.
   const Elf_Shdr *DotSymtabShndxSec = nullptr; // SHT_SYMTAB_SHNDX section.
 
+  // Hold CREL relocations for SectionRef::relocations().
+  mutable SmallVector<SmallVector<Elf_Crel, 0>, 0> Crels;
+
   Error initContent() override;
 
   void moveSymbolNext(DataRefImpl &Symb) const override;
@@ -446,6 +450,7 @@ template <class ELFT> class ELFObjectFile : public ELFObjectFileBase {
 
   const Elf_Rel *getRel(DataRefImpl Rel) const;
   const Elf_Rela *getRela(DataRefImpl Rela) const;
+  Elf_Crel getCrel(DataRefImpl Rel) const;
 
   Expected<const Elf_Sym *> getSymbol(DataRefImpl Sym) const {
     return EF.template getEntry<Elf_Sym>(Sym.d.a, Sym.d.b);
@@ -1022,6 +1027,40 @@ ELFObjectFile<ELFT>::section_rel_begin(DataRefImpl Sec) const {
   uintptr_t SHT = reinterpret_cast<uintptr_t>((*SectionsOrErr).begin());
   RelData.d.a = (Sec.p - SHT) / EF.getHeader().e_shentsize;
   RelData.d.b = 0;
+  if (reinterpret_cast<const Elf_Shdr *>(Sec.p)->sh_type == ELF::SHT_CREL) {
+    if (RelData.d.a + 1 > Crels.size())
+      Crels.resize(RelData.d.a + 1);
+    if (Crels[RelData.d.a].empty()) {
+      ArrayRef<uint8_t> Content = cantFail(getSectionContents(Sec));
+      DataExtractor Data(Content, true, 8); // endian/class is irrelevant
+      DataExtractor::Cursor Cur(0);
+
+      const auto Hdr = Data.getULEB128(Cur);
+      const size_t Count = Hdr / 8, FlagBits = Hdr & 4 ? 3 : 2, Shift = Hdr % 4;
+      uintX_t Offset = 0, Addend = 0;
+      uint32_t Symidx = 0, Type = 0;
+      for (size_t i = 0; i != Count; ++i) {
+        const uint8_t B = Data.getU8(Cur);
+        Offset += B >> FlagBits;
+        if (B >= 0x80)
+          Offset +=
+              (Data.getULEB128(Cur) << (7 - FlagBits)) - (0x80 >> FlagBits);
+        if (B & 1)
+          Symidx += Data.getSLEB128(Cur);
+        if (B & 2)
+          Type += Data.getSLEB128(Cur);
+        if (B & 4)
+          Addend += Data.getSLEB128(Cur);
+        if (!Cur)
+          break;
+        Crels[RelData.d.a].push_back(
+            Elf_Crel{Offset << Shift, uint32_t(Symidx), Type,
+                     std::make_signed_t<typename ELFT::uint>(Addend)});
+      }
+      if (!Cur)
+        consumeError(Cur.takeError());
+    }
+  }
   return relocation_iterator(RelocationRef(RelData, this));
 }
 
@@ -1030,9 +1069,13 @@ relocation_iterator
 ELFObjectFile<ELFT>::section_rel_end(DataRefImpl Sec) const {
   const Elf_Shdr *S = reinterpret_cast<const Elf_Shdr *>(Sec.p);
   relocation_iterator Begin = section_rel_begin(Sec);
+  DataRefImpl RelData = Begin->getRawDataRefImpl();
+  if (S->sh_type == ELF::SHT_CREL) {
+    RelData.d.b = Crels[RelData.d.a].size();
+    return relocation_iterator(RelocationRef(RelData, this));
+  }
   if (S->sh_type != ELF::SHT_RELA && S->sh_type != ELF::SHT_REL)
     return Begin;
-  DataRefImpl RelData = Begin->getRawDataRefImpl();
   const Elf_Shdr *RelSec = getRelSection(RelData);
 
   // Error check sh_link here so that getRelocationSymbol can just use it.
@@ -1050,7 +1093,7 @@ Expected<section_iterator>
 ELFObjectFile<ELFT>::getRelocatedSection(DataRefImpl Sec) const {
   const Elf_Shdr *EShdr = getSection(Sec);
   uintX_t Type = EShdr->sh_type;
-  if (Type != ELF::SHT_REL && Type != ELF::SHT_RELA)
+  if (Type != ELF::SHT_REL && Type != ELF::SHT_RELA && Type != ELF::SHT_CREL)
     return section_end();
 
   Expected<const Elf_Shdr *> SecOrErr = EF.getSection(EShdr->sh_info);
@@ -1070,7 +1113,9 @@ symbol_iterator
 ELFObjectFile<ELFT>::getRelocationSymbol(DataRefImpl Rel) const {
   uint32_t symbolIdx;
   const Elf_Shdr *sec = getRelSection(Rel);
-  if (sec->sh_type == ELF::SHT_REL)
+  if (sec->sh_type == ELF::SHT_CREL)
+    symbolIdx = getCrel(Rel).r_symidx;
+  else if (sec->sh_type == ELF::SHT_REL)
     symbolIdx = getRel(Rel)->getSymbol(EF.isMips64EL());
   else
     symbolIdx = getRela(Rel)->getSymbol(EF.isMips64EL());
@@ -1087,6 +1132,8 @@ ELFObjectFile<ELFT>::getRelocationSymbol(DataRefImpl Rel) const {
 template <class ELFT>
 uint64_t ELFObjectFile<ELFT>::getRelocationOffset(DataRefImpl Rel) const {
   const Elf_Shdr *sec = getRelSection(Rel);
+  if (sec->sh_type == ELF::SHT_CREL)
+    return getCrel(Rel).r_offset;
   if (sec->sh_type == ELF::SHT_REL)
     return getRel(Rel)->r_offset;
 
@@ -1096,6 +1143,8 @@ uint64_t ELFObjectFile<ELFT>::getRelocationOffset(DataRefImpl Rel) const {
 template <class ELFT>
 uint64_t ELFObjectFile<ELFT>::getRelocationType(DataRefImpl Rel) const {
   const Elf_Shdr *sec = getRelSection(Rel);
+  if (sec->sh_type == ELF::SHT_CREL)
+    return getCrel(Rel).r_type;
   if (sec->sh_type == ELF::SHT_REL)
     return getRel(Rel)->getType(EF.isMips64EL());
   else
@@ -1117,9 +1166,11 @@ void ELFObjectFile<ELFT>::getRelocationTypeName(
 template <class ELFT>
 Expected<int64_t>
 ELFObjectFile<ELFT>::getRelocationAddend(DataRefImpl Rel) const {
-  if (getRelSection(Rel)->sh_type != ELF::SHT_RELA)
-    return createError("Section is not SHT_RELA");
-  return (int64_t)getRela(Rel)->r_addend;
+  if (getRelSection(Rel)->sh_type == ELF::SHT_RELA)
+    return (int64_t)getRela(Rel)->r_addend;
+  if (getRelSection(Rel)->sh_type == ELF::SHT_CREL)
+    return (int64_t)getCrel(Rel).r_addend;
+  return createError("Section is not SHT_RELA");
 }
 
 template <class ELFT>
@@ -1142,6 +1193,13 @@ ELFObjectFile<ELFT>::getRela(DataRefImpl Rela) const {
   return *Ret;
 }
 
+template <class ELFT>
+typename ELFObjectFile<ELFT>::Elf_Crel
+ELFObjectFile<ELFT>::getCrel(DataRefImpl Rel) const {
+  assert(getRelSection(Rel)->sh_type == ELF::SHT_CREL);
+  return Crels[Rel.d.a][Rel.d.b];
+}
+
 template <class ELFT>
 Expected<ELFObjectFile<ELFT>>
 ELFObjectFile<ELFT>::create(MemoryBufferRef Object, bool InitContent) {
diff --git a/llvm/include/llvm/Object/ELFTypes.h b/llvm/include/llvm/Object/ELFTypes.h
index 4ab23e4ea81b1..aa07f4cacc4ab 100644
--- a/llvm/include/llvm/Object/ELFTypes.h
+++ b/llvm/include/llvm/Object/ELFTypes.h
@@ -32,6 +32,7 @@ template <class ELFT> struct Elf_Sym_Impl;
 template <class ELFT> struct Elf_Dyn_Impl;
 template <class ELFT> struct Elf_Phdr_Impl;
 template <class ELFT, bool isRela> struct Elf_Rel_Impl;
+template <bool Is64> struct Elf_Crel_Impl;
 template <class ELFT> struct Elf_Verdef_Impl;
 template <class ELFT> struct Elf_Verdaux_Impl;
 template <class ELFT> struct Elf_Verneed_Impl;
@@ -62,6 +63,7 @@ template <endianness E, bool Is64> struct ELFType {
   using Phdr = Elf_Phdr_Impl<ELFType<E, Is64>>;
   using Rel = Elf_Rel_Impl<ELFType<E, Is64>, false>;
   using Rela = Elf_Rel_Impl<ELFType<E, Is64>, true>;
+  using Crel = Elf_Crel_Impl<Is64>;
   using Relr = packed<uint>;
   using Verdef = Elf_Verdef_Impl<ELFType<E, Is64>>;
   using Verdaux = Elf_Verdaux_Impl<ELFType<E, Is64>>;
@@ -117,6 +119,7 @@ using ELF64BE = ELFType<llvm::endianness::big, true>;
   using Elf_Phdr = typename ELFT::Phdr;                                        \
   using Elf_Rel = typename ELFT::Rel;                                          \
   using Elf_Rela = typename ELFT::Rela;                                        \
+  using Elf_Crel = typename ELFT::Crel;                                        \
   using Elf_Relr = typename ELFT::Relr;                                        \
   using Elf_Verdef = typename ELFT::Verdef;                                    \
   using Elf_Verdaux = typename ELFT::Verdaux;                                  \
@@ -385,6 +388,7 @@ template <endianness Endianness>
 struct Elf_Rel_Impl<ELFType<Endianness, false>, false> {
   LLVM_ELF_IMPORT_TYPES(Endianness, false)
   static const bool IsRela = false;
+  static const bool IsCrel = false;
   Elf_Addr r_offset; // Location (file byte offset, or program virtual addr)
   Elf_Word r_info;   // Symbol table index and type of relocation to apply
 
@@ -421,6 +425,7 @@ struct Elf_Rel_Impl<ELFType<Endianness, false>, true>
     : public Elf_Rel_Impl<ELFType<Endianness, false>, false> {
   LLVM_ELF_IMPORT_TYPES(Endianness, false)
   static const bool IsRela = true;
+  static const bool IsCrel = false;
   Elf_Sword r_addend; // Compute value for relocatable field by adding this
 };
 
@@ -428,6 +433,7 @@ template <endianness Endianness>
 struct Elf_Rel_Impl<ELFType<Endianness, true>, false> {
   LLVM_ELF_IMPORT_TYPES(Endianness, true)
   static const bool IsRela = false;
+  static const bool IsCrel = false;
   Elf_Addr r_offset; // Location (file byte offset, or program virtual addr)
   Elf_Xword r_info;  // Symbol table index and type of relocation to apply
 
@@ -474,9 +480,28 @@ struct Elf_Rel_Impl<ELFType<Endianness, true>, true>
     : public Elf_Rel_Impl<ELFType<Endianness, true>, false> {
   LLVM_ELF_IMPORT_TYPES(Endianness, true)
   static const bool IsRela = true;
+  static const bool IsCrel = false;
   Elf_Sxword r_addend; // Compute value for relocatable field by adding this.
 };
 
+template <bool Is64> struct Elf_Crel_Impl {
+  using uint = std::conditional_t<Is64, uint64_t, uint32_t>;
+  static const bool IsRela = true;
+  static const bool IsCrel = true;
+  uint r_offset;
+  uint32_t r_symidx;
+  uint32_t r_type;
+  std::conditional_t<Is64, int64_t, int32_t> r_addend;
+
+  // Dummy bool parameter is for compatibility with Elf_Rel_Impl.
+  uint32_t getType(bool) const { return r_type; }
+  uint32_t getSymbol(bool) const { return r_symidx; }
+  void setSymbolAndType(uint32_t s, unsigned char t, bool) {
+    r_symidx = s;
+    r_type = t;
+  }
+};
+
 template <class ELFT>
 struct Elf_Ehdr_Impl {
   LLVM_ELF_IMPORT_TYPES_ELFT(ELFT)
diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp
index b8ef2654ed6e3..60a5d016b67aa 100644
--- a/llvm/lib/MC/ELFObjectWriter.cpp
+++ b/llvm/lib/MC/ELFObjectWriter.cpp
@@ -39,6 +39,7 @@
 #include "llvm/MC/StringTableBuilder.h"
 #include "llvm/Support/Alignment.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Compression.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/EndianStream.h"
@@ -259,7 +260,7 @@ class ELFObjectWriter : public MCObjectWriter {
   void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
                         const MCFragment *Fragment, const MCFixup &Fixup,
                         MCValue Target, uint64_t &FixedValue) override;
-  bool usesRela(const MCSectionELF &Sec) const;
+  bool usesRela(const MCTargetOptions *, const MCSectionELF &Sec) const;
 
   void executePostLayoutBinding(MCAssembler &Asm,
                                 const MCAsmLayout &Layout) override;
@@ -831,7 +832,15 @@ MCSectionELF *ELFWriter::createRelocationSection(MCContext &Ctx,
     Flags = ELF::SHF_GROUP;
 
   const StringRef SectionName = Sec.getName();
-  const bool Rela = OWriter.usesRela(Sec);
+  const MCTargetOptions *TO = Ctx.getTargetOptions();
+  if (TO && TO->Crel) {
+    MCSectionELF *RelaSection =
+        Ctx.createELFRelSection(".crel" + SectionName, ELF::SHT_CREL, Flags,
+                                /*EntrySize=*/1, Sec.getGroup(), &Sec);
+    return RelaSection;
+  }
+
+  const bool Rela = OWriter.usesRela(TO, Sec);
   unsigned EntrySize;
   if (Rela)
     EntrySize = is64Bit() ? sizeof(ELF::Elf64_Rela) : sizeof(ELF::Elf32_Rela);
@@ -934,10 +943,50 @@ void ELFWriter::WriteSecHdrEntry(uint32_t Name, uint32_t Type, uint64_t Flags,
   WriteWord(EntrySize); // sh_entsize
 }
 
+template <class uint>
+static void encodeCrel(ArrayRef<ELFRelocationEntry> Relocs, raw_ostream &os) {
+  uint OffsetMask = 8, Offset = 0, Addend = 0;
+  uint32_t Symidx = 0, Type = 0;
+  // hdr & 4 indicates 3 flag bits in delta offset and flags members.
+  for (unsigned i = 0, e = Relocs.size(); i != e; ++i)
+    OffsetMask |= Relocs[i].Offset;
+  const int Shift = llvm::countr_zero(OffsetMask);
+  encodeULEB128(Relocs.size() * 8 + /*addend_bit=*/4 + Shift, os);
+  for (const ELFRelocationEntry &Entry : Relocs) {
+    // The delta offset and flags member may be larger than uint64_t. Special
+    // case the first byte (3 flag bits and 4 offset bits). Other ULEB128 bytes
+    // encode the remaining delta offset bits.
+    auto DeltaOffset = static_cast<uint>((Entry.Offset - Offset) >> Shift);
+    Offset = Entry.Offset;
+    uint32_t CurSymidx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
+    uint8_t B = (DeltaOffset << 3) + (Symidx != CurSymidx) +
+                (Type != Entry.Type ? 2 : 0) + (Addend != Entry.Addend ? 4 : 0);
+    if (DeltaOffset < 0x10) {
+      os << char(B);
+    } else {
+      os << char(B | 0x80);
+      encodeULEB128(DeltaOffset >> 4, os);
+    }
+    if (B & 1) {
+      encodeSLEB128(static_cast<int32_t>(CurSymidx - Symidx), os);
+      Symidx = CurSymidx;
+    }
+    if (B & 2) {
+      encodeSLEB128(static_cast<int32_t>(Entry.Type - Type), os);
+      Type = Entry.Type;
+    }
+    if (B & 4) {
+      encodeSLEB128(std::make_signed_t<uint>(Entry.Addend - Addend), os);
+      Addend = Entry.Addend;
+    }
+  }
+}
+
 void ELFWriter::writeRelocations(const MCAssembler &Asm,
                                        const MCSectionELF &Sec) {
   std::vector<ELFRelocationEntry> &Relocs = OWriter.Relocations[&Sec];
-  const bool Rela = OWriter.usesRela(Sec);
+  const MCTargetOptions *TO = Asm.getContext().getTargetOptions();
+  const bool Rela = OWriter.usesRela(TO, Sec);
 
   // Sort the relocation entries. MIPS needs this.
   OWriter.TargetObjectWriter->sortRelocs(Asm, Relocs);
@@ -977,24 +1026,29 @@ void ELFWriter::writeRelocations(const MCAssembler &Asm,
         }
       }
     }
-    return;
-  }
-  for (const ELFRelocationEntry &Entry : Relocs) {
-    uint32_t Symidx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
-    if (is64Bit()) {
-      write(Entry.Offset);
-      ELF::Elf64_Rela ERE;
-      ERE.setSymbolAndType(Symidx, Entry.Type);
-      write(ERE.r_info);
-      if (Rela)
-        write(Entry.Addend);
-    } else {
-      write(uint32_t(Entry.Offset));
-      ELF::Elf32_Rela ERE;
-      ERE.setSymbolAndType(Symidx, Entry.Type);
-      write(ERE.r_info);
-      if (Rela)
-        write(uint32_t(Entry.Addend));
+  } else if (TO && TO->Crel) {
+    if (is64Bit())
+      encodeCrel<uint64_t>(Relocs, W.OS);
+    else
+      encodeCrel<uint32_t>(Relocs, W.OS);
+  } else {
+    for (const ELFRelocationEntry &Entry : Relocs) {
+      uint32_t Symidx = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
+      if (is64Bit()) {
+        write(Entry.Offset);
+        ELF::Elf64_Rela ERE;
+        ERE.setSymbolAndType(Symidx, Entry.Type);
+        write(ERE.r_info);
+        if (Rela)
+          write(Entry.Addend);
+      } else {
+        write(uint32_t(Entry.Offset));
+        ELF::Elf32_Rela ERE;
+        ERE.setSymbolAndType(Symidx, Entry.Type);
+        write(ERE.r_info);
+        if (Rela)
+          write(uint32_t(Entry.Addend));
+      }
     }
   }
 }
@@ -1014,7 +1068,8 @@ void ELFWriter::writeSection(const SectionIndexMapTy &SectionIndexMap,
     llvm_unreachable("SHT_DYNAMIC in a relocatable object");
 
   case ELF::SHT_REL:
-  case ELF::SHT_RELA: {
+  case ELF::SHT_RELA:
+  case ELF::SHT_CREL: {
     sh_link = SymbolTableIndex;
     assert(sh_link && ".symtab not found");
     const MCSection *InfoSection = Section.getLinkedToSection();
@@ -1443,6 +1498,7 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
   uint64_t C = Target.getConstant();
   uint64_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
   MCContext &Ctx = Asm.getContext();
+  const MCTargetOptions *TO = Ctx.getTargetOptions();
 
   if (const MCSymbolRefExpr *RefB = Target.getSymB()) {
     const auto &SymB = cast<MCSymbolELF>(RefB->getSymbol());
@@ -1498,7 +1554,7 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
   FixedValue = !RelocateWithSymbol && SymA && !SymA->isUndefined()
                    ? C + Layout.getSymbolOffset(*SymA)
                    : C;
-  if (usesRela(FixupSection)) {
+  if (usesRela(TO, FixupSection)) {
     Addend = FixedValue;
     FixedValue = 0;
   }
@@ -1527,9 +1583,11 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
   Relocations[&FixupSection].push_back(Rec);
 }
 
-bool ELFObjectWriter::usesRela(const MCSectionELF &Sec) const {
-  return hasRelocationAddend() &&
-         Sec.getType() != ELF::SHT_LLVM_CALL_GRAPH_PROFILE;
+bool ELFObjectWriter::usesRela(const MCTargetOptions *TO,
+                               const MCSectionELF &Sec) const {
+  return (hasRelocationAddend() &&
+          Sec.getType() != ELF::SHT_LLVM_CALL_GRAPH_PROFILE) ||
+         (TO && TO->Crel);
 }
 
 bool ELFObjectWriter::isSymbolRefDifferenceFullyResolvedImpl(
diff --git a/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp b/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp
index 31bfcdc3e4e79..02279d6d90bc2 100644
--- a/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp
+++ b/llvm/lib/MC/MCTargetOptionsCommandFlags.cpp
@@ -46,6 +46,7 @@ MCOPT(bool, FatalWarnings)
 MCOPT(bool, NoWarn)
 MCOPT(bool, NoDeprecatedWarn)
 MCOPT(bool, NoTypeCheck)
+MCOPT(bool, Crel)
 MCOPT(bool, X86RelaxRelocations)
 MCOPT(std::string, ABIName)
 MCOPT(std::string, AsSecureLogFile)
@@ -123,6 +124,10 @@ llvm::mc::RegisterMCTargetOptionsFlags::RegisterMCTargetOptionsFlags() {
       "no-type-check", cl::desc("Suppress type errors (Wasm)"));
   MCBINDOPT(NoTypeCheck);
 
+  static cl::opt<bool> Crel("crel",
+                            cl::desc("Use CREL relocation format for ELF"));
+  MCBINDOPT(Crel);
+
   static cl::opt<bool> X86RelaxRelocations(
       "x86-relax-relocations",
       cl::desc(
@@ -156,6 +161,7 @@ MCTargetOptions llvm::mc::InitMCTargetOptionsFromFlags() {
   Options.MCNoWarn = getNoWarn();
   Options.MCNoDeprecatedWarn = getNoDeprecatedWarn();
   Options.MCNoTypeCheck = getNoTypeCheck();
+  Options.Crel = getCrel();
   Options.X86RelaxRelocations = getX86RelaxRelocations();
   Options.EmitDwarfUnwind = getEmitDwarfUnwind();
   Options.EmitCompactUnwindNonCanonical = getEmitCompactUnwindNonCanonical();
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index 0ac4e7a57759a..f7be77a792b44 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -303,6 +303,7 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) {
     STRINGIFY_ENUM_CASE(ELF, SHT_GROUP);
     STRINGIFY_ENUM_CASE(ELF, SHT_SYMTAB_SHNDX);
     STRINGIFY_ENUM_CASE(ELF, SHT_RELR);
+    STRINGIFY_ENUM_CASE(ELF, SHT_CREL);
     STRINGIFY_ENUM_CASE(ELF, SHT_ANDROID_REL);
     STRINGIFY_ENUM_CASE(ELF, SHT_ANDROID_RELA);
     STRINGIFY_ENUM_CASE(ELF, SHT_ANDROID_RELR);
@@ -392,6 +393,68 @@ ELFFile<ELFT>::decode_relrs(Elf_Relr_Range relrs) const {
   return Relocs;
 }
 
+template <class ELFT>
+uint64_t ELFFile<ELFT>::crelHeader(ArrayRef<uint8_t> Content) const {
+  DataExtractor Data(Content, true, 8); // endian/class is irrelevant
+  DataExtractor::Cursor Cur(0);
+  uint64_t Hdr = Data.getULEB128(Cur);
+  // In case of an error, return 0 and postpone error reporting to decodeCrel.
+  consumeError(Cur.takeError());
+  return Hdr;
+}
+
+template <class ELFT>
+Expected<typename ELFFile<ELFT>::RelsOrRelas>
+ELFFile<ELFT>::decodeCrel(ArrayRef<uint8_t> Content) const {
+  DataExtractor Data(Content, true, 8); // endian/class is irrelevant
+  DataExtractor::Cursor Cur(0);
+  const uint64_t Hdr = Data.getULEB128(Cur);
+  const size_t Count = Hdr / 8, FlagBits = Hdr & 4 ? 3 : 2, Shift = Hdr % 4;
+  std::vector<Elf_Rel> Rels;
+  std::vector<Elf_Rela> Relas;
+  if (Hdr & 4)
+    Relas.resize(Count);
+  else
+    Rels.resize(Count);
+  typename ELFT::uint Offset = 0, Addend = 0;
+  uint32_t Symidx = 0, Type = 0;
+  for (size_t I = 0; I != Count; ++I) {
+    // For ELFCLASS64, decode a 65-bit integer where bit 0 indicates whether
+    // symidx/type are equal to the previous entry's. The remaining 64 bits
+    // encode the delta offset relative to the previous offset.
+    const uint8_t B = Data.getU8(Cur);
+    Offset += B >> FlagBits;
+    if (B >= 0x80)
+      Offset += (Data.getULEB128(Cur) << (7 - FlagBits)) - (0x80 >> FlagBits);
+    if (B & 1)
+      Symidx += Data.getSLEB128(Cur);
+    if (B & 2)
+      Type += Data.getSLEB128(Cur);
+    if (B & 4 & Hdr)
+      Addend += Data.getSLEB128(Cur);
+    if (Hdr & 4) {
+      Relas[I].r_offset = Offset << Shift;
+      Relas[I].setSymbolAndType(Symidx, Type, false);
+      Relas[I].r_addend = Addend;
+    } else {
+      Rels[I].r_offset = Offset << Shift;
+      Rels[I].setSymbolAndType(Symidx, Type, false);
+    }
+  }
+  if (!Cur)
+    return std::move(Cur.takeError());
+  return std::make_pair(std::move(Rels), std::move(Relas));
+}
+
+template <class ELFT>
+Expected<typename ELFFile<ELFT>::RelsOrRelas>
+ELFFile<ELFT>::crels(const Elf_Shdr &Sec) const {
+  Expected<ArrayRef<uint8_t>> ContentsOrErr = getSectionContents(Sec);
+  if (!ContentsOrErr)
+    return ContentsOrErr.takeError();
+  return decodeCrel(*ContentsOrErr);
+}
+
 template <class ELFT>
 Expected<std::vector<typename ELFT::Rela>>
 ELFFile<ELFT>::android_relas(const Elf_Shdr &Sec) const {
diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp
index b7118a543faed..6c700619f1ded 100644
--- a/llvm/lib/ObjectYAML/ELFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp
@@ -123,6 +123,12 @@ class ContiguousBlobAccumulator {
     return encodeULEB128(Val, OS);
   }
 
+  unsigned writeSLEB128(int64_t Val) {
+    if (!checkLimit(10))
+      return 0;
+    return encodeSLEB128(Val, OS);
+  }
+
   template <typename T> void write(T Val, llvm::endianness E) {
     if (checkLimit(sizeof(T)))
       support::endian::write<T>(OS, Val, E);
@@ -1269,7 +1275,8 @@ void ELFState<ELFT>::writeSectionContent(
     Elf_Shdr &SHeader, const ELFYAML::RelocationSection &Section,
     ContiguousBlobAccumulator &CBA) {
   assert((Section.Type == llvm::ELF::SHT_REL ||
-          Section.Type == llvm::ELF::SHT_RELA) &&
+          Section.Type == llvm::ELF::SHT_RELA ||
+          Section.Type == llvm::ELF::SHT_CREL) &&
          "Section type is not SHT_REL nor SHT_RELA");
 
   if (!Section.RelocatableSec.empty())
@@ -1278,29 +1285,68 @@ void ELFState<ELFT>::writeSectionContent(
   if (!Section.Relocations)
     return;
 
+  const bool IsCrel = Section.Type == llvm::ELF::SHT_CREL;
   const bool IsRela = Section.Type == llvm::ELF::SHT_RELA;
+  typename ELFT::uint OffsetMask = 8, Offset = 0, Addend = 0;
+  uint32_t Symidx = 0, Type = 0;
+  uint64_t CurrentOffset = CBA.getOffset();
+  if (IsCrel)
+    for (const ELFYAML::Relocation &Rel : *Section.Relocations)
+      OffsetMask |= Rel.Offset;
+  const int Shift = llvm::countr_zero(OffsetMask);
+  if (IsCrel)
+    CBA.writeULEB128(Section.Relocations->size() * 8 + 4 + Shift);
   for (const ELFYAML::Relocation &Rel : *Section.Relocations) {
     const bool IsDynamic = Section.Link && (*Section.Link == ".dynsym");
-    unsigned SymIdx =
+    uint32_t CurSymidx =
         Rel.Symbol ? toSymbolIndex(*Rel.Symbol, Section.Name, IsDynamic) : 0;
-    if (IsRela) {
+    if (IsCrel) {
+      // For 64-bit uint, encode a 65-bit integer where bit 0 indicates whether
+      // symidx/type are equal to the previous entry's. The remaining 64 bits
+      // encode the delta offset.
+      auto DeltaOffset =
+          (static_cast<typename ELFT::uint>(Rel.Offset) - Offset) >> Shift;
+      Offset = Rel.Offset;
+      uint8_t B =
+          DeltaOffset * 8 + (Symidx != CurSymidx) + (Type != Rel.Type ? 2 : 0) +
+          (Addend != static_cast<typename ELFT::uint>(Rel.Addend) ? 4 : 0);
+      if (DeltaOffset < 0x10) {
+        CBA.write(B);
+      } else {
+        CBA.write(B | 0x80);
+        CBA.writeULEB128(DeltaOffset >> 4);
+      }
+      if (B & 1) {
+        CBA.writeSLEB128(
+            std::make_signed_t<typename ELFT::uint>(CurSymidx - Symidx));
+        Symidx = CurSymidx;
+      }
+      if (B & 2) {
+        CBA.writeSLEB128(static_cast<int32_t>(Rel.Type - Type));
+        Type = Rel.Type;
+      }
+      if (B & 4) {
+        CBA.writeSLEB128(
+            std::make_signed_t<typename ELFT::uint>(Rel.Addend - Addend));
+        Addend = Rel.Addend;
+      }
+    } else if (IsRela) {
       Elf_Rela REntry;
       zero(REntry);
       REntry.r_offset = Rel.Offset;
       REntry.r_addend = Rel.Addend;
-      REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc));
+      REntry.setSymbolAndType(CurSymidx, Rel.Type, isMips64EL(Doc));
       CBA.write((const char *)&REntry, sizeof(REntry));
     } else {
       Elf_Rel REntry;
       zero(REntry);
       REntry.r_offset = Rel.Offset;
-      REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc));
+      REntry.setSymbolAndType(CurSymidx, Rel.Type, isMips64EL(Doc));
       CBA.write((const char *)&REntry, sizeof(REntry));
     }
   }
 
-  SHeader.sh_size = (IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel)) *
-                    Section.Relocations->size();
+  SHeader.sh_size = CBA.getOffset() - CurrentOffset;
 }
 
 template <class ELFT>
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index 045211c44b907..e53bb06fefba2 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -686,6 +686,7 @@ void ScalarEnumerationTraits<ELFYAML::ELF_SHT>::enumeration(
   ECase(SHT_GROUP);
   ECase(SHT_SYMTAB_SHNDX);
   ECase(SHT_RELR);
+  ECase(SHT_CREL);
   ECase(SHT_ANDROID_REL);
   ECase(SHT_ANDROID_RELA);
   ECase(SHT_ANDROID_RELR);
@@ -1620,6 +1621,7 @@ void MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::mapping(
     break;
   case ELF::SHT_REL:
   case ELF::SHT_RELA:
+  case ELF::SHT_CREL:
     if (!IO.outputting())
       Section.reset(new ELFYAML::RelocationSection());
     sectionMapping(IO, *cast<ELFYAML::RelocationSection>(Section.get()));
diff --git a/llvm/test/MC/ELF/crel-32.s b/llvm/test/MC/ELF/crel-32.s
new file mode 100644
index 0000000000000..23303aeb53eaa
--- /dev/null
+++ b/llvm/test/MC/ELF/crel-32.s
@@ -0,0 +1,16 @@
+# REQUIRES: powerpc-registered-target
+# RUN: llvm-mc -filetype=obj -crel -triple=ppc %s -o %t.o
+# RUN: llvm-readelf -Sr %t.o | FileCheck %s
+
+# CHECK:      [ 3] .data           PROGBITS      00000000 {{.*}} 000008 00  WA  0   0  1
+# CHECK-NEXT: [ 4] .crel.data      CREL          00000000 {{.*}} 00000a 01   I  5   3  1
+
+# CHECK:      Relocation section '.crel.data' at offset {{.*}} contains 2 entries:
+# CHECK-NEXT:  Offset     Info    Type                Sym. Value  Symbol's Name + Addend
+# CHECK-NEXT: 00000000  {{.*}}   R_PPC_NONE             00000000   a0 - 1
+# CHECK-NEXT: 00000004  {{.*}}   R_PPC_ADDR32           00000000   a3 + 4000
+
+.data
+.reloc .+0, BFD_RELOC_NONE, a0-1
+.reloc .+4, BFD_RELOC_32, a3+0x4000
+.quad 0
diff --git a/llvm/test/MC/ELF/crel.s b/llvm/test/MC/ELF/crel.s
new file mode 100644
index 0000000000000..221af9837f90a
--- /dev/null
+++ b/llvm/test/MC/ELF/crel.s
@@ -0,0 +1,100 @@
+# RUN: llvm-mc -filetype=obj -crel -triple=x86_64 %s -o %t.o
+# RUN: llvm-readelf -Sr -x .crelrodata2 -x .crelrodata16 %t.o | FileCheck %s
+
+# RUN: %if aarch64-registered-target %{ llvm-mc -filetype=obj -crel -triple=aarch64_be %s -o %t.be.o %}
+# RUN: %if aarch64-registered-target %{ llvm-readelf -r %t.be.o | FileCheck %s --check-prefix=A64BE %}
+
+# CHECK:      [ 4] .data             PROGBITS      0000000000000000 {{.*}} 000008 00  WA  0   0  1
+# CHECK-NEXT: [ 5] .crel.data        CREL          0000000000000000 {{.*}} 00002a 01   I 14   4  1
+# CHECK-NEXT: [ 6] .rodata           PROGBITS      0000000000000000 {{.*}} 00002b 00   A  0   0  1
+# CHECK-NEXT: [ 7] .crel.rodata      CREL          0000000000000000 {{.*}} 000010 01   I 14   6  1
+# CHECK-NEXT: [ 8] rodata2           PROGBITS      0000000000000000 {{.*}} 000008 00   A  0   0  1
+# CHECK-NEXT: [ 9] .crelrodata2      CREL          0000000000000000 {{.*}} 000005 01   I 14   8  1
+# CHECK-NEXT: [10] rodata16          PROGBITS      0000000000000000 {{.*}} 000010 00   A  0   0  1
+# CHECK-NEXT: [11] .crelrodata16     CREL          0000000000000000 {{.*}} 000004 01   I 14  10  1
+# CHECK-NEXT: [12] noalloc           PROGBITS      0000000000000000 {{.*}} 000004 00      0   0  1
+# CHECK-NEXT: [13] .crelnoalloc      CREL          0000000000000000 {{.*}} 000005 01   I 14  12  1
+# CHECK-NEXT: [14] .symtab           SYMTAB
+
+# CHECK:      Relocation section '.crel.data' at offset {{.*}} contains 7 entries:
+# CHECK-NEXT:     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# CHECK-NEXT: 0000000000000000  {{.*}}           R_X86_64_NONE          0000000000000000 a0 + 0
+# CHECK-NEXT: 0000000000000001  {{.*}}           R_X86_64_NONE          0000000000000000 a1 - 1
+# CHECK-NEXT: 0000000000000002  {{.*}}           R_X86_64_NONE          0000000000000000 a2 - 1
+# CHECK-NEXT: 0000000000000003  {{.*}}           R_X86_64_32            0000000000000000 a3 + 4000
+# CHECK-NEXT: 0000000000000004  {{.*}}           R_X86_64_64            0000000000000000 a0 - 8000000000000000
+# CHECK-NEXT: 0000000000000005  {{.*}}           R_X86_64_64            0000000000000000 a1 + 7fffffffffffffff
+# CHECK-NEXT: 0000000000000005  {{.*}}           R_X86_64_32            0000000000000000 a1 - 1
+# CHECK-EMPTY:
+# CHECK-NEXT: Relocation section '.crel.rodata' at offset {{.*}} contains 4 entries:
+# CHECK-NEXT:     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# CHECK-NEXT: 0000000000000000  {{.*}}           R_X86_64_PC32          0000000000000000 foo + 0
+# CHECK-NEXT: 000000000000000f  {{.*}}           R_X86_64_PC32          0000000000000000 foo + 3f
+# CHECK-NEXT: 000000000000001f  {{.*}}           R_X86_64_PC64          0000000000000000 foo + 7f
+# CHECK-NEXT: 0000000000000027  {{.*}}           R_X86_64_PC32          0000000000000000 _start - 1f81
+# CHECK-EMPTY:
+# CHECK-NEXT: Relocation section '.crelrodata2' at offset {{.*}} contains 2 entries:
+# CHECK-NEXT:     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# CHECK-NEXT: 0000000000000002  {{.*}}           R_X86_64_32            0000000000000000 .data + 0
+# CHECK-NEXT: 0000000000000008  {{.*}}           R_X86_64_32            0000000000000000 .data + 0
+# CHECK-EMPTY:
+# CHECK-NEXT: Relocation section '.crelrodata16' at offset {{.*}} contains 1 entries:
+# CHECK-NEXT:     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# CHECK-NEXT: 0000000000000008  {{.*}}           R_X86_64_64            0000000000000000 rodata16 + 0
+# CHECK-EMPTY:
+# CHECK-NEXT: Relocation section '.crelnoalloc' at offset {{.*}} contains 1 entries:
+# CHECK-NEXT:     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# CHECK-NEXT: 0000000000000000  {{.*}}           R_X86_64_32            0000000000000000 .text + 4
+
+## count * 8 + 4 + shift = 2*8+4+1 = 21
+# CHECK:      Hex dump of section '.crelrodata2':
+# CHECK-NEXT: 0x00000000 150b020a 18                         .
+## count * 8 + 4 + shift = 1*8+4+3 = 15
+# CHECK:      Hex dump of section '.crelrodata16':
+# CHECK-NEXT: 0x00000000 0f0b0301 .
+
+# A64BE:      0000000000000000  {{.*}}           R_AARCH64_NONE         0000000000000000 a0 + 0
+# A64BE-NEXT: 0000000000000001  {{.*}}           R_AARCH64_NONE         0000000000000000 a1 - 1
+# A64BE-NEXT: 0000000000000002  {{.*}}           R_AARCH64_NONE         0000000000000000 a2 - 1
+# A64BE-NEXT: 0000000000000003  {{.*}}           R_AARCH64_ABS32        0000000000000000 a3 + 4000
+# A64BE-NEXT: 0000000000000004  {{.*}}           R_AARCH64_ABS64        0000000000000000 a0 - 8000000000000000
+# A64BE-NEXT: 0000000000000005  {{.*}}           R_AARCH64_ABS64        0000000000000000 a1 + 7fffffffffffffff
+# A64BE-NEXT: 0000000000000005  {{.*}}           R_AARCH64_ABS32        0000000000000000 a1 - 1
+
+.globl _start
+_start:
+  ret
+
+.section .text.1,"ax"
+  ret
+
+.data
+.reloc .+0, BFD_RELOC_NONE, a0
+.reloc .+1, BFD_RELOC_NONE, a1-1
+.reloc .+2, BFD_RELOC_NONE, a2-1
+.reloc .+3, BFD_RELOC_32, a3+0x4000
+.reloc .+4, BFD_RELOC_64, a0-0x8000000000000000
+.reloc .+5, BFD_RELOC_64, a1+0x7fffffffffffffff
+.reloc .+5, BFD_RELOC_32, a1-1
+.quad 0
+
+.section .rodata,"a"
+.long foo - .
+.space 15-4
+.long foo - . + 63  // offset+=15
+.space 16-4
+.quad foo - . + 127  // offset+=16
+.long _start - . - 8065
+
+.section rodata2,"a"
+.space 2
+.reloc ., BFD_RELOC_32, .data
+.space 6
+.reloc ., BFD_RELOC_32, .data
+
+.section rodata16,"a"
+.quad 0
+.quad rodata16
+
+.section noalloc
+.long .text + 4
diff --git a/llvm/test/tools/llvm-readobj/ELF/crel.test b/llvm/test/tools/llvm-readobj/ELF/crel.test
new file mode 100644
index 0000000000000..383a8e26b2a77
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/crel.test
@@ -0,0 +1,180 @@
+# RUN: yaml2obj --docnum=1 %s -o %t
+# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=LLVM --match-full-lines
+# RUN: llvm-readelf -r %t | FileCheck %s --check-prefix=GNU --match-full-lines
+
+# LLVM:      Relocations [
+# LLVM-NEXT:   Section ([[#]]) .crel.text {
+# LLVM-NEXT:     0x1 R_X86_64_32 g1 0x1
+# LLVM-NEXT:     0x2 R_X86_64_64 l1 0x2
+# LLVM-NEXT:     0x0 R_X86_64_32S g1 0xFFFFFFFFFFFFFFFF
+# LLVM-NEXT:     0x4 R_X86_64_32S .text 0x8000000000000000
+# LLVM-NEXT:   }
+# LLVM-NEXT:   Section ([[#]]) .crelnonalloc {
+# LLVM-NEXT:     0x10 R_X86_64_64 g1 0x1
+# LLVM-NEXT:     0x20 R_X86_64_64 g2 0x2
+# LLVM-NEXT:   }
+# LLVM-NEXT: ]
+
+# GNU:      Relocation section '.crel.text' at offset 0x48 contains 4 entries:
+# GNU-NEXT:     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# GNU-NEXT: 0000000000000001  000000030000000a R_X86_64_32            0000000000000000 g1 + 1
+# GNU-NEXT: 0000000000000002  0000000200000001 R_X86_64_64            0000000000000000 l1 + 2
+# GNU-NEXT: 0000000000000000  000000030000000b R_X86_64_32S           0000000000000000 g1 - 1
+# GNU-NEXT: 0000000000000004  000000010000000b R_X86_64_32S           0000000000000000 .text - 8000000000000000
+# GNU-EMPTY:
+# GNU-NEXT: Relocation section '.crelnonalloc' at offset 0xa2 contains 2 entries:
+# GNU-NEXT:     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# GNU-NEXT: 0000000000000010  0000000300000001 R_X86_64_64            0000000000000000 g1 + 1
+# GNU-NEXT: 0000000000000020  0000000400000001 R_X86_64_64            0000000000000000 g2 + 2
+
+--- !ELF
+FileHeader: !FileHeader
+  Class: ELFCLASS64
+  Data: ELFDATA2LSB
+  Type: ET_REL
+  Machine: EM_X86_64
+
+Sections:
+- Name: .text
+  Type: SHT_PROGBITS
+  Content: "0000000000000000"
+  Flags: [SHF_ALLOC]
+- Name: .crel.text
+  Type: SHT_CREL
+  Info: .text
+  Link: .symtab
+  Relocations:
+    - Offset: 0x1
+      Symbol: g1
+      Type:   R_X86_64_32
+      Addend: 1
+    - Offset: 0x2
+      Symbol: l1
+      Type:   R_X86_64_64
+      Addend: 2
+    - Offset: 0x0
+      Symbol: g1
+      Type:   R_X86_64_32S
+      Addend: 0xffffffffffffffff
+    - Offset: 0x4
+      Symbol: .text
+      Type:   R_X86_64_32S
+      Addend: 0x8000000000000000
+- Name: nonalloc
+  Type: SHT_PROGBITS
+  Size: 0x30
+- Name: .crelnonalloc
+  Type: SHT_CREL
+  Info: nonalloc
+  Link: .symtab
+  Relocations:
+    - Offset: 0x10
+      Symbol: g1
+      Type:   R_X86_64_64
+      Addend: 1
+    - Offset: 0x20
+      Symbol: g2
+      Type:   R_X86_64_64
+      Addend: 2
+
+Symbols:
+  - Name: .text
+    Type: STT_SECTION
+    Section: .text
+  - Name:    l1
+  - Name:    g1
+    Section: .text
+    Value:   0x0
+    Size:    4
+    Binding: STB_GLOBAL
+  - Name:    g2
+    Binding: STB_GLOBAL
+
+## Check relocation formatting on ELFCLASS32 as well.
+# RUN: yaml2obj --docnum=2 %s -o %t2
+# RUN: llvm-readobj -r %t2 | FileCheck %s --check-prefix=LLVM2 --match-full-lines
+# RUN: llvm-readelf -r %t2 | FileCheck %s --check-prefix=GNU2 --match-full-lines
+
+# LLVM2:      Relocations [
+# LLVM2-NEXT:   Section (2) .crel.text {
+# LLVM2-NEXT:     0x8 R_386_PC32 l1 0x1
+# LLVM2-NEXT:     0x4 R_386_32 g1 0x0
+# LLVM2-NEXT:   }
+# LLVM2-NEXT: ]
+
+# GNU2:      Relocation section '.crel.text' at offset {{.*}} contains 2 entries:
+# GNU2-NEXT:  Offset     Info    Type                Sym. Value  Symbol's Name + Addend
+# GNU2-NEXT: 00000008  00000102 R_386_PC32             00000000   l1 + 1
+# GNU2-NEXT: 00000004  00000201 R_386_32               00000000   g1 + 0
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS32
+  Data:    ELFDATA2LSB
+  Type:    ET_REL
+  Machine: EM_386
+Sections:
+- Name: .text
+  Type: SHT_PROGBITS
+  Size: 0x10
+- Name: .crel.text
+  Type: SHT_CREL
+  Info: .text
+  Link: .symtab
+  Relocations:
+    - Offset: 0x8
+      Symbol: l1
+      Type:   R_386_PC32
+      Addend: 1
+    - Offset: 0x4
+      Symbol: g1
+      Type:   R_386_32
+Symbols:
+  - Name:    l1
+  - Name:    g1
+    Binding: STB_GLOBAL
+
+## Check CREL with implicit addends.
+# RUN: yaml2obj --docnum=3 %s -o %t3
+# RUN: llvm-readobj -r %t3 | FileCheck %s --check-prefix=LLVM3 --match-full-lines
+# RUN: llvm-readelf -r %t3 | FileCheck %s --check-prefix=GNU3 --match-full-lines
+
+# LLVM3:      Relocations [
+# LLVM3-NEXT:   Section (3) .crel.data {
+# LLVM3-NEXT:     0x1F R_X86_64_32 g1
+# LLVM3-NEXT:     0x3F R_X86_64_64 g1
+# LLVM3-NEXT:     0x0 R_X86_64_32S l1
+# LLVM3-NEXT:   }
+# LLVM3-NEXT: ]
+
+# GNU3:      Relocation section '.crel.data' at offset {{.*}} contains 3 entries:
+# GNU3-NEXT:     Offset             Info             Type               Symbol's Value  Symbol's Name
+# GNU3-NEXT: 000000000000001f  000000030000000a R_X86_64_32            0000000000000000 g1
+# GNU3-NEXT: 000000000000003f  0000000300000001 R_X86_64_64            0000000000000000 g1
+# GNU3-NEXT: 0000000000000000  000000020000000b R_X86_64_32S           0000000000000000 l1
+--- !ELF
+FileHeader:
+  Class:     ELFCLASS64
+  Data:      ELFDATA2LSB
+  Type:      ET_REL
+  Machine:   EM_X86_64
+Sections:
+  - Name:    .text
+    Type:    SHT_PROGBITS
+  - Name:    .data
+    Type:    SHT_PROGBITS
+  - Name:    .crel.data
+    Type:    SHT_CREL
+    Flags:   [ SHF_INFO_LINK ]
+    Link:    .symtab
+    Info:    .data
+    Content: 187f030a82017787feffffffffffffff077f0a
+Symbols:
+  - Name:    .text
+    Type:    STT_SECTION
+    Section: .text
+  - Name:    l1
+    Section: .text
+  - Name:    g1
+    Section: .text
+    Binding: STB_GLOBAL
diff --git a/llvm/test/tools/llvm-readobj/ELF/dynamic-reloc.test b/llvm/test/tools/llvm-readobj/ELF/dynamic-reloc.test
index 89702b963c111..1e5b9e2e3c210 100644
--- a/llvm/test/tools/llvm-readobj/ELF/dynamic-reloc.test
+++ b/llvm/test/tools/llvm-readobj/ELF/dynamic-reloc.test
@@ -32,12 +32,17 @@ FileHeader:
 # RUN:     --match-full-lines --check-prefixes=GNU-RELOCS,GNU-PLTRELA
 
 # LLVM-RELOCS:      Dynamic Relocations {
+# LLVM-RELOCS-NEXT:   0x8 R_X86_64_64 foo 0x0
 # LLVM-RELOCS-NEXT:   0x1 R_X86_64_NONE foo 0x0
 # LLVM-RELOCS-NEXT:   0x2 R_X86_64_NONE foo
-# LLVM-RELOCS-NEXT:   0x4 R_X86_64_RELATIVE
+# LLVM-RELOCS-NEXT:   0x4 R_X86_64_RELATIVE -
 # LLVM-RELOCS-NEXT:   0x8 R_X86_64_NONE foo
 # LLVM-RELOCS-NEXT: }
 
+#       GNU-RELOCS:'CREL' relocation section at offset 0xa8:
+#  GNU-RELOCS-NEXT:    Offset             Info             Type               Symbol's Value  Symbol's Name
+#  GNU-RELOCS-NEXT:0000000000000008  0000000100000001 R_X86_64_64            0000000000000000 foo + 0
+# GNU-RELOCS-EMPTY:
 #       GNU-RELOCS:'RELA' relocation section at offset 0x78 contains 24 bytes:
 #  GNU-RELOCS-NEXT:    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
 #  GNU-RELOCS-NEXT:0000000000000001  0000000100000000 R_X86_64_NONE          0000000000000000 foo + 0
@@ -50,10 +55,10 @@ FileHeader:
 #  GNU-RELOCS-NEXT:    Offset             Info             Type               Symbol's Value  Symbol's Name
 #  GNU-RELOCS-NEXT:0000000000000004  0000000000000008 R_X86_64_RELATIVE                 {{$}}
 # GNU-RELOCS-EMPTY:
-#  GNU-PLTREL-NEXT:'PLT' relocation section at offset 0xa8 contains 16 bytes:
+#  GNU-PLTREL-NEXT:'PLT' relocation section at offset 0xac contains 16 bytes:
 #  GNU-PLTREL-NEXT:    Offset             Info             Type               Symbol's Value  Symbol's Name
 #  GNU-PLTREL-NEXT:0000000000000008  0000000100000000 R_X86_64_NONE          0000000000000000 foo
-# GNU-PLTRELA-NEXT:'PLT' relocation section at offset 0xa8 contains 24 bytes:
+# GNU-PLTRELA-NEXT:'PLT' relocation section at offset 0xac contains 24 bytes:
 # GNU-PLTRELA-NEXT:    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
 # GNU-PLTRELA-NEXT:0000000000000008  0000000100000000 R_X86_64_NONE          0000000000000000 foo + 0
 # GNU-RELOCS-EMPTY:
@@ -82,6 +87,12 @@ Sections:
     Type:    SHT_RELR
     Flags:   [ SHF_ALLOC ]
     Entries: [ 0x0000000000000004 ]
+  - Name:    .crel.dyn
+    Type:    SHT_CREL
+    Relocations:
+      - Type:   R_X86_64_64
+        Offset: 0x8
+        Symbol: foo
   - Name:    .plt
     Type:    [[PLTTYPE=SHT_REL]]
     Relocations:
@@ -111,9 +122,12 @@ Sections:
         Value: 0x8
       - Tag:   DT_RELRENT
         Value: 0x8
-## 0x30 == offset of .plt section in the segment.
-      - Tag:   DT_JMPREL
+## 0x30 == offset of .crel.dyn section in the segment.
+      - Tag:   DT_CREL
         Value: 0x30
+## 0x34 == offset of .plt section in the segment.
+      - Tag:   DT_JMPREL
+        Value: 0x34
       - Tag:   DT_PLTREL
         Value: [[DTPLTREL=17]] ## 17 == DT_REL
       - Tag:   DT_PLTRELSZ
@@ -140,6 +154,7 @@ ProgramHeaders:
 
 # PLTRELUNKNOWN-LLVM:      warning: '[[FILE]]': unknown DT_PLTREL value of 255
 # PLTRELUNKNOWN-LLVM:      Dynamic Relocations {
+# PLTRELUNKNOWN-LLVM-NEXT:   0x8 R_X86_64_64 foo 0x0
 # PLTRELUNKNOWN-LLVM-NEXT:   0x1 R_X86_64_NONE foo 0x0
 # PLTRELUNKNOWN-LLVM-NEXT:   0x2 R_X86_64_NONE foo{{$}}
 # PLTRELUNKNOWN-LLVM-NEXT:   0x4 R_X86_64_RELATIVE -{{$}}
@@ -148,6 +163,10 @@ ProgramHeaders:
 
 # PLTRELUNKNOWN-GNU:        warning: '[[FILE]]': unknown DT_PLTREL value of 255
 # PLTRELUNKNOWN-GNU-EMPTY:
+# PLTRELUNKNOWN-GNU-NEXT:   'CREL' relocation section at offset 0xa8:
+# PLTRELUNKNOWN-GNU-NEXT:       Offset             Info             Type               Symbol's Value  Symbol's Name
+# PLTRELUNKNOWN-GNU-NEXT:   0000000000000008  0000000100000001 R_X86_64_64            0000000000000000 foo + 0
+# PLTRELUNKNOWN-GNU-EMPTY:
 # PLTRELUNKNOWN-GNU-NEXT:   'RELA' relocation section at offset 0x78 contains 24 bytes:
 # PLTRELUNKNOWN-GNU-NEXT:       Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
 # PLTRELUNKNOWN-GNU-NEXT:   0000000000000001  0000000100000000 R_X86_64_NONE          0000000000000000 foo + 0
@@ -160,6 +179,6 @@ ProgramHeaders:
 # PLTRELUNKNOWN-GNU-NEXT:       Offset             Info             Type               Symbol's Value  Symbol's Name
 # PLTRELUNKNOWN-GNU-NEXT:   0000000000000004  0000000000000008 R_X86_64_RELATIVE
 # PLTRELUNKNOWN-GNU-EMPTY:
-# PLTRELUNKNOWN-GNU-NEXT:   'PLT' relocation section at offset 0xa8 contains 16 bytes:
+# PLTRELUNKNOWN-GNU-NEXT:   'PLT' relocation section at offset 0xac contains 16 bytes:
 # PLTRELUNKNOWN-GNU-NEXT:       Offset             Info             Type               Symbol's Value  Symbol's Name
 # PLTRELUNKNOWN-GNU-NEXT:   warning: '[[FILE]]': invalid DT_PLTRELSZ value (0x10) or PLTREL entry size (0x0)
diff --git a/llvm/test/tools/llvm-readobj/ELF/relocation-errors.test b/llvm/test/tools/llvm-readobj/ELF/relocation-errors.test
index 22d1855c01163..87aab76de4c29 100644
--- a/llvm/test/tools/llvm-readobj/ELF/relocation-errors.test
+++ b/llvm/test/tools/llvm-readobj/ELF/relocation-errors.test
@@ -37,6 +37,23 @@
 # GNU-NEXT:      Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
 # GNU-NEXT:  warning: '[[FILE]]': unable to print relocation 0 in SHT_RELA section with index 4: invalid sh_type for symbol table, expected SHT_SYMTAB or SHT_DYNSYM
 
+# RUN: yaml2obj -DTYPE=SHT_CREL %s -o %t64.crel
+# RUN: llvm-readelf --relocations %t64.crel 2>&1 | FileCheck %s -DFILE=%t64.crel --check-prefix=CREL-GNU
+
+# CREL-GNU:       Relocation section '.rel.text' at offset 0x41 contains 7 entries:
+# CREL-GNU-NEXT:      Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# CREL-GNU-NEXT:  warning: '[[FILE]]': unable to print relocation 0 in SHT_CREL section with index 3: unable to read an entry with index 4278124286 from SHT_NULL section with index 0: section [index 0] has invalid sh_entsize: expected 24, but got 0
+# CREL-GNU-NEXT:  warning: '[[FILE]]': unable to print relocation 1 in SHT_CREL section with index 3: unable to read an entry with index 4278124286 from SHT_NULL section with index 0: section [index 0] has invalid sh_entsize: expected 24, but got 0
+# CREL-GNU-NEXT:  0000000000000002  0000000000000000 R_X86_64_NONE                     0
+# CREL-GNU-NEXT:  warning: '[[FILE]]': unable to print relocation 3 in SHT_CREL section with index 3: unable to read an entry with index 2 from SHT_NULL section with index 0: section [index 0] has invalid sh_entsize: expected 24, but got 0
+# CREL-GNU-NEXT:  warning: '[[FILE]]': unable to print relocation 4 in SHT_CREL section with index 3: unable to read an entry with index 4 from SHT_NULL section with index 0: section [index 0] has invalid sh_entsize: expected 24, but got 0
+# CREL-GNU-NEXT:  warning: '[[FILE]]': unable to print relocation 5 in SHT_CREL section with index 3: unable to read an entry with index 3 from SHT_NULL section with index 0: section [index 0] has invalid sh_entsize: expected 24, but got 0
+# CREL-GNU-NEXT:  warning: '[[FILE]]': unable to print relocation 6 in SHT_CREL section with index 3: unable to read an entry with index 5 from SHT_NULL section with index 0: section [index 0] has invalid sh_entsize: expected 24, but got 0
+# CREL-GNU-EMPTY:
+# CREL-GNU-NEXT:  Relocation section '.rela.text' at offset 0x5a contains 1 entries:
+# CREL-GNU-NEXT:      Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# CREL-GNU-NEXT:  warning: '[[FILE]]': unable to print relocation 0 in SHT_RELA section with index 4: invalid sh_type for symbol table, expected SHT_SYMTAB or SHT_DYNSYM
+
 --- !ELF
 FileHeader:
   Class:   ELFCLASS64
@@ -51,7 +68,7 @@ Sections:
     Type:   SHT_PROGBITS
     ShName: 0xFEFEFEFE
   - Name: .rel.text
-    Type: SHT_REL
+    Type: [[TYPE=SHT_REL]]
     Info: .text
     Relocations:
 ## Case 1: There is no symbol with index 0xFEFEFEFE.
diff --git a/llvm/test/tools/yaml2obj/ELF/dynamic-relocations.yaml b/llvm/test/tools/yaml2obj/ELF/dynamic-relocations.yaml
index 8964877467f4a..c788a40e67788 100644
--- a/llvm/test/tools/yaml2obj/ELF/dynamic-relocations.yaml
+++ b/llvm/test/tools/yaml2obj/ELF/dynamic-relocations.yaml
@@ -4,6 +4,9 @@
 # RUN: yaml2obj %s -o %t
 # RUN: llvm-readelf -r %t | FileCheck %s
 
+# RUN: yaml2obj -DTYPE=SHT_CREL %s -o %t.crel
+# RUN: llvm-readelf -r %t.crel | FileCheck %s
+
 # CHECK:      Relocation section '.rela.dyn' at offset {{.*}} contains 2 entries:
 # CHECK-NEXT:     Offset             Info             Type      Symbol's Value  Symbol's Name
 # CHECK-NEXT: 0000000000000000  0000000100000000 R_X86_64_NONE 0000000012345678 dynamic
@@ -24,7 +27,7 @@ Sections:
   - Name: .data
     Type: SHT_PROGBITS
   - Name: .rela.dyn
-    Type: SHT_REL
+    Type: [[TYPE=SHT_REL]]
     Link: .dynsym
     Info: .data
     Relocations:
diff --git a/llvm/test/tools/yaml2obj/ELF/reloc-sec-entry-size.yaml b/llvm/test/tools/yaml2obj/ELF/reloc-sec-entry-size.yaml
index 43f6465c0a823..42621fa41d461 100644
--- a/llvm/test/tools/yaml2obj/ELF/reloc-sec-entry-size.yaml
+++ b/llvm/test/tools/yaml2obj/ELF/reloc-sec-entry-size.yaml
@@ -12,6 +12,7 @@
 # ELF64: .relr.default RELR 0000000000000000 000040 000000 08
 # ELF64: .rela.custom  RELA 0000000000000000 000040 000000 ff
 # ELF64: .rel.custom   REL  0000000000000000 000040 000000 ff
+# ELF64: .crel.custom CREL 0000000000000000 000040 000000 ff
 # ELF64: .relr.custom  RELR 0000000000000000 000040 000000 ff
 
 # ELF32: Name          Type  Address  Off    Size   ES
@@ -20,6 +21,7 @@
 # ELF32: .relr.default RELR  00000000 000034 000000 04
 # ELF32: .rela.custom  RELA  00000000 000034 000000 ff
 # ELF32: .rel.custom   REL   00000000 000034 000000 ff
+# ELF32: .crel.custom CREL 00000000 000034 000000 ff
 # ELF32: .relr.custom  RELR  00000000 000034 000000 ff
 
 --- !ELF
@@ -42,6 +44,9 @@ Sections:
   - Name:    .rel.custom
     Type:    SHT_REL
     EntSize: 0xFF
+  - Name:    .crel.custom
+    Type:    SHT_CREL
+    EntSize: 0xFF
   - Name:    .relr.custom
     Type:    SHT_RELR
     EntSize: 0xFF
diff --git a/llvm/test/tools/yaml2obj/ELF/relocation-crel.yaml b/llvm/test/tools/yaml2obj/ELF/relocation-crel.yaml
new file mode 100644
index 0000000000000..c6006754614bb
--- /dev/null
+++ b/llvm/test/tools/yaml2obj/ELF/relocation-crel.yaml
@@ -0,0 +1,63 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-readelf -r %t | FileCheck %s
+
+# CHECK:      Relocation section '.crel.text' at offset {{.*}} contains 7 entries:
+# CHECK-NEXT:     Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
+# CHECK-NEXT: 0000000000000001  0000000100000004 R_X86_64_PLT32         0000000000000000 a0 - 4
+# CHECK-NEXT: 0000000000000005  0000000200000004 R_X86_64_PLT32         0000000000000000 a1 - 4
+# CHECK-NEXT: 000000000000000a  0000000300000004 R_X86_64_PLT32         0000000000000000 a2 + 0
+# CHECK-NEXT: 0000000000000010  0000000200000001 R_X86_64_64            0000000000000000 a1 - 4
+# CHECK-NEXT: 0000000000000018  0000000100000001 R_X86_64_64            0000000000000000 a0 + 80
+# CHECK-NEXT: 0000000000000020  0000000000000008 R_X86_64_RELATIVE                 8000000000000000
+# CHECK-NEXT: 0000000000000028  0000000400000001 R_X86_64_64            0000000000000000 a3 + 7fffffffffffffff
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_REL
+  Machine: EM_X86_64
+Sections:
+  - Name: .text
+    Type: SHT_PROGBITS
+  - Name: .crel.text
+    Type: SHT_CREL
+    Info: .text
+    Link: .symtab
+    Relocations:
+      - Offset: 1
+        Type: R_X86_64_PLT32
+        Symbol: a0
+        Addend: -4
+      - Offset: 5
+        Type: R_X86_64_PLT32
+        Symbol: a1
+        Addend: -4
+      - Offset: 10
+        Type: R_X86_64_PLT32
+        Symbol: a2
+        Addend: 0
+      - Offset: 16
+        Type: R_X86_64_64
+        Symbol: a1
+        Addend: -4
+      - Offset: 24
+        Type: R_X86_64_64
+        Symbol: a0
+        Addend: 128
+      - Offset: 32
+        Type: R_X86_64_RELATIVE
+        Addend: 0x8000000000000000
+      - Offset: 40
+        Type: R_X86_64_64
+        Symbol: a3
+        Addend: 0x7fffffffffffffff
+Symbols:
+  - Name: a0
+    Binding: STB_GLOBAL
+  - Name: a1
+    Binding: STB_GLOBAL
+  - Name: a2
+    Binding: STB_GLOBAL
+  - Name: a3
+    Binding: STB_GLOBAL
diff --git a/llvm/test/tools/yaml2obj/ELF/relocation-missing-symbol.yaml b/llvm/test/tools/yaml2obj/ELF/relocation-missing-symbol.yaml
index 2f8d98dee18a8..1eb376fb5c1ab 100644
--- a/llvm/test/tools/yaml2obj/ELF/relocation-missing-symbol.yaml
+++ b/llvm/test/tools/yaml2obj/ELF/relocation-missing-symbol.yaml
@@ -2,6 +2,7 @@
 ## does not exist.
 
 # RUN: not yaml2obj %s -o %t 2>&1 | FileCheck %s
+# RUN: not yaml2obj -DTYPE=SHT_CREL %s -o %t 2>&1 | FileCheck %s
 
 ## Check we are able to report multiple errors.
 
@@ -18,7 +19,7 @@ Sections:
   - Name: .text
     Type: SHT_PROGBITS
   - Name: .rela.text
-    Type: SHT_RELA
+    Type: [[TYPE=SHT_RELA]]
     Info: .text
     Link: .symtab
     Relocations:
diff --git a/llvm/test/tools/yaml2obj/ELF/relocation-type.yaml b/llvm/test/tools/yaml2obj/ELF/relocation-type.yaml
index 67d451df2f7ff..0aabde4ce1994 100644
--- a/llvm/test/tools/yaml2obj/ELF/relocation-type.yaml
+++ b/llvm/test/tools/yaml2obj/ELF/relocation-type.yaml
@@ -3,6 +3,8 @@
 ## Show that yaml2obj is able to produce relocations for an unknown e_machine kind properly.
 # RUN: yaml2obj %s -o %t1 -DMACHINE=0x1234
 # RUN: llvm-readelf %t1 --relocations | FileCheck %s -DFIRST=Unknown -DSECOND=Unknown
+# RUN: yaml2obj %s -o %t1 -DMACHINE=0x1234 -DTYPE=SHT_CREL
+# RUN: llvm-readelf %t1 --relocations | FileCheck %s -DFIRST=Unknown -DSECOND=Unknown
 
 # CHECK: Relocation section '.rela.text' at offset 0x40 contains 4 entries:
 # CHECK:      Offset            Info             Type
@@ -23,7 +25,7 @@ FileHeader:
   Machine: [[MACHINE]]
 Sections:
   - Name: .rela.text
-    Type: SHT_RELA
+    Type: [[TYPE=SHT_RELA]]
     Relocations:
 ## Test a few noticeable possible values: 0, 1, max(int8_t)=127, max(uint8_t)=0xFF=-1
       - Offset: 0x9
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index a752cc4015293..662bacf54a4ef 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -378,6 +378,7 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
 
   DynRegionInfo DynRelRegion;
   DynRegionInfo DynRelaRegion;
+  DynRegionInfo DynCrelRegion;
   DynRegionInfo DynRelrRegion;
   DynRegionInfo DynPLTRelRegion;
   std::optional<DynRegionInfo> DynSymRegion;
@@ -1903,7 +1904,7 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> &O,
                            ScopedPrinter &Writer)
     : ObjDumper(Writer, O.getFileName()), ObjF(O), Obj(O.getELFFile()),
       FileName(O.getFileName()), DynRelRegion(O, *this),
-      DynRelaRegion(O, *this), DynRelrRegion(O, *this),
+      DynRelaRegion(O, *this), DynCrelRegion(O, *this), DynRelrRegion(O, *this),
       DynPLTRelRegion(O, *this), DynSymTabShndxRegion(O, *this),
       DynamicTable(O, *this) {
   if (!O.IsContentValid())
@@ -2062,6 +2063,9 @@ template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() {
       DynRelaRegion.EntSize = Dyn.getVal();
       DynRelaRegion.EntSizePrintName = "DT_RELAENT value";
       break;
+    case ELF::DT_CREL:
+      DynCrelRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr());
+      break;
     case ELF::DT_SONAME:
       SONameOffset = Dyn.getVal();
       break;
@@ -2102,6 +2106,8 @@ template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() {
         DynPLTRelRegion.EntSize = sizeof(Elf_Rel);
       else if (Dyn.getVal() == DT_RELA)
         DynPLTRelRegion.EntSize = sizeof(Elf_Rela);
+      else if (Dyn.getVal() == DT_CREL)
+        DynPLTRelRegion.EntSize = 1;
       else
         reportUniqueWarning(Twine("unknown DT_PLTREL value of ") +
                             Twine((uint64_t)Dyn.getVal()));
@@ -2421,6 +2427,8 @@ std::string ELFDumper<ELFT>::getDynamicEntry(uint64_t Type,
       return "REL";
     if (Value == DT_RELA)
       return "RELA";
+    if (Value == DT_CREL)
+      return "CREL";
     [[fallthrough]];
   case DT_PLTGOT:
   case DT_HASH:
@@ -2435,6 +2443,7 @@ std::string ELFDumper<ELFT>::getDynamicEntry(uint64_t Type,
   case DT_FINI_ARRAY:
   case DT_PREINIT_ARRAY:
   case DT_DEBUG:
+  case DT_CREL:
   case DT_VERDEF:
   case DT_VERNEED:
   case DT_VERSYM:
@@ -3840,14 +3849,15 @@ void GNUELFDumper<ELFT>::printRelRelaReloc(const Relocation<ELFT> &R,
 
 template <class ELFT>
 static void printRelocHeaderFields(formatted_raw_ostream &OS, unsigned SType,
-                                   const typename ELFT::Ehdr &EHeader) {
+                                   const typename ELFT::Ehdr &EHeader,
+                                   uint64_t CrelHdr = 0) {
   bool IsRela = SType == ELF::SHT_RELA || SType == ELF::SHT_ANDROID_RELA;
   if (ELFT::Is64Bits)
     OS << "    Offset             Info             Type               Symbol's "
           "Value  Symbol's Name";
   else
     OS << " Offset     Info    Type                Sym. Value  Symbol's Name";
-  if (IsRela)
+  if (IsRela || (SType == ELF::SHT_CREL && (CrelHdr & 4)))
     OS << " + Addend";
   OS << "\n";
 }
@@ -3857,7 +3867,10 @@ void GNUELFDumper<ELFT>::printDynamicRelocHeader(unsigned Type, StringRef Name,
                                                  const DynRegionInfo &Reg) {
   uint64_t Offset = Reg.Addr - this->Obj.base();
   OS << "\n'" << Name.str().c_str() << "' relocation section at offset 0x"
-     << utohexstr(Offset, /*LowerCase=*/true) << " contains " << Reg.Size << " bytes:\n";
+     << utohexstr(Offset, /*LowerCase=*/true);
+  if (Type != ELF::SHT_CREL)
+    OS << " contains " << Reg.Size << " bytes";
+  OS << ":\n";
   printRelocHeaderFields<ELFT>(OS, Type, this->Obj.getHeader());
 }
 
@@ -3865,7 +3878,8 @@ template <class ELFT>
 static bool isRelocationSec(const typename ELFT::Shdr &Sec,
                             const typename ELFT::Ehdr &EHeader) {
   return Sec.sh_type == ELF::SHT_REL || Sec.sh_type == ELF::SHT_RELA ||
-         Sec.sh_type == ELF::SHT_RELR || Sec.sh_type == ELF::SHT_ANDROID_REL ||
+         Sec.sh_type == ELF::SHT_RELR || Sec.sh_type == ELF::SHT_CREL ||
+         Sec.sh_type == ELF::SHT_ANDROID_REL ||
          Sec.sh_type == ELF::SHT_ANDROID_RELA ||
          Sec.sh_type == ELF::SHT_ANDROID_RELR ||
          (EHeader.e_machine == EM_AARCH64 &&
@@ -3891,6 +3905,14 @@ template <class ELFT> void GNUELFDumper<ELFT>::printRelocations() {
       return RelasOrErr->size();
     }
 
+    if (Sec.sh_type == ELF::SHT_CREL) {
+      Expected<ArrayRef<uint8_t>> ContentsOrErr =
+          this->Obj.getSectionContents(Sec);
+      if (!ContentsOrErr)
+        return ContentsOrErr.takeError();
+      return this->Obj.crelHeader(*ContentsOrErr) / 8;
+    }
+
     if (PrintAsRelr(Sec)) {
       Expected<Elf_Relr_Range> RelrsOrErr = this->Obj.relrs(Sec);
       if (!RelrsOrErr)
@@ -3924,7 +3946,13 @@ template <class ELFT> void GNUELFDumper<ELFT>::printRelocations() {
     if (PrintAsRelr(Sec)) {
       printRelr(Sec);
     } else {
-      printRelocHeaderFields<ELFT>(OS, Sec.sh_type, this->Obj.getHeader());
+      uint64_t CrelHdr = 0;
+      if (auto ContentsOrErr = this->Obj.getSectionContents(Sec))
+        CrelHdr = this->Obj.crelHeader(*ContentsOrErr);
+      else
+        consumeError(ContentsOrErr.takeError());
+      printRelocHeaderFields<ELFT>(OS, Sec.sh_type, this->Obj.getHeader(),
+                                   CrelHdr);
       this->printRelocationsHelper(Sec);
     }
   }
@@ -4888,6 +4916,34 @@ void ELFDumper<ELFT>::printRelocationsHelper(const Elf_Shdr &Sec) {
 
 template <class ELFT> void ELFDumper<ELFT>::printDynamicRelocationsHelper() {
   const bool IsMips64EL = this->Obj.isMips64EL();
+  auto DumpCrelRegion = [&](DynRegionInfo &Region) {
+    // While the size is unknown, a valid CREL has at least one byte. We can
+    // check whether Addr is in bounds, and then decode CREL until the file
+    // end.
+    Region.Size = Region.EntSize = 1;
+    if (!Region.template getAsArrayRef<uint8_t>().empty()) {
+      const uint64_t Offset =
+          Region.Addr -
+          (const uint8_t *)ObjF.getMemoryBufferRef().getBufferStart();
+      const uint64_t ObjSize = ObjF.getMemoryBufferRef().getBufferSize();
+      auto RelsOrRelas =
+          Obj.decodeCrel(ArrayRef<uint8_t>(Region.Addr, ObjSize - Offset));
+      if (!RelsOrRelas) {
+        reportUniqueWarning(toString(RelsOrRelas.takeError()));
+      } else {
+        for (const Elf_Rel &R : RelsOrRelas->first)
+          printDynamicReloc(Relocation<ELFT>(R, false));
+        for (const Elf_Rela &R : RelsOrRelas->second)
+          printDynamicReloc(Relocation<ELFT>(R, false));
+      }
+    }
+  };
+
+  if (this->DynCrelRegion.Addr) {
+    printDynamicRelocHeader(ELF::SHT_CREL, "CREL", this->DynCrelRegion);
+    DumpCrelRegion(this->DynCrelRegion);
+  }
+
   if (this->DynRelaRegion.Size > 0) {
     printDynamicRelocHeader(ELF::SHT_RELA, "RELA", this->DynRelaRegion);
     for (const Elf_Rela &Rela :
@@ -4916,6 +4972,8 @@ template <class ELFT> void ELFDumper<ELFT>::printDynamicRelocationsHelper() {
       for (const Elf_Rela &Rela :
            this->DynPLTRelRegion.template getAsArrayRef<Elf_Rela>())
         printDynamicReloc(Relocation<ELFT>(Rela, IsMips64EL));
+    } else if (this->DynPLTRelRegion.EntSize == 1) {
+      DumpCrelRegion(this->DynPLTRelRegion);
     } else {
       printDynamicRelocHeader(ELF::SHT_REL, "PLT", this->DynPLTRelRegion);
       for (const Elf_Rel &Rel :
@@ -6410,6 +6468,17 @@ void ELFDumper<ELFT>::forEachRelocationDo(
                 /*SymTab=*/nullptr);
     break;
   }
+  case ELF::SHT_CREL: {
+    if (auto RelsOrRelas = Obj.crels(Sec)) {
+      for (const Elf_Rel &R : RelsOrRelas->first)
+        RelRelaFn(Relocation<ELFT>(R, false), RelNdx++, Sec, SymTab);
+      for (const Elf_Rela &R : RelsOrRelas->second)
+        RelRelaFn(Relocation<ELFT>(R, false), RelNdx++, Sec, SymTab);
+    } else {
+      Warn(RelsOrRelas.takeError());
+    }
+    break;
+  }
   case ELF::SHT_ANDROID_REL:
   case ELF::SHT_ANDROID_RELA:
     if (Expected<std::vector<Elf_Rela>> RelasOrErr = Obj.android_relas(Sec)) {



More information about the cfe-commits mailing list