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

CREL is a compact relocation format for the ELF object file format.

This patch adds integrated assembler support (using the RELA form),
which can be enabled with `clang -c -Wa,--crel,--experimental-crel`.
`-Wa,--experimental-crel` indicates that CREL is experimental and not
a standard. `-Wa,--experimental-crel` might be removed in the future.

* We take a section type code 20 from the generic range for `SHT_CREL`.
  We avoided using `SHT_LLVM_` or `SHT_GNU_` to maintain broader
  applicability for interested psABIs.
* In the extremely unlikely case the generic ABI assigns 20 to another
  purpose, and existing toolchains generate relocatable files using that
  code (unlikely if we ensure GNU and LLVM don't do this), any conflicts
  would be the user's responsibility to resolve.

This patch also adds llvm-readobj support (for both REL and RELA forms)
to facilitate testing the assembler. Additionally, yaml2obj gains
support for the RELA form to aid testing with llvm-readobj.

Currently, linking the generated relocatable file with GNU ld will fail
because GNU ld doesn't support CREL yet.

    unknown architecture of input file `a.o' is incompatible with i386:x86-64 
output

lld will also produce an error when attempting to link a CREL relocatable file.
The support will come in a future change.

Link: 
https://discourse.llvm.org/t/rfc-crel-a-compact-relocation-format-for-elf/77600


>From a0cfafb82db825512b0ca44778fa9d4bb435563d Mon Sep 17 00:00:00 2001
From: Fangrui Song <i...@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 340b08dd7e2a33..3229f77eef1fcc 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 9781fcaa4ff5e9..e9cea8967c1334 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 c9f7c4e5f718b4..55f0c9aff6a49f 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 22c3f8642ad8eb..ed37085d69ac53 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 0a2ea96de73824..0f4d8aa3ad7938 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 6796b43a155020..8ba45aeafd0569 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 00000000000000..0dc0507705d72d
--- /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 00000000000000..953f2a1f3d3bcb
--- /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 86afe22fac24cc..9d53cca7a0bf5b 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 1502d375f5c45d..d67a1e6de3678f 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 f296acc2ca4bb9..b5c546d8b67e73 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 0cf2806bd48040..98317712250bf9 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 fc35eea09c4b3e..837f148e8f4ab4 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 78b6b40cbddf67..26e38075f8db00 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 8cc09e7fd7d551..505ea9ede313af 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 4ab23e4ea81b1a..aa07f4cacc4aba 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 b8ef2654ed6e3b..60a5d016b67aad 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 31bfcdc3e4e79f..02279d6d90bc2e 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 0ac4e7a57759ac..f7be77a792b44d 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 b7118a543faed3..6c700619f1deda 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 045211c44b9079..e53bb06fefba2c 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 00000000000000..23303aeb53eaaa
--- /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 00000000000000..221af9837f90a6
--- /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 00000000000000..383a8e26b2a773
--- /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 89702b963c1113..1e5b9e2e3c2100 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 22d1855c01163f..87aab76de4c29b 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 8964877467f4a5..c788a40e677880 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 43f6465c0a823b..42621fa41d461c 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 00000000000000..c6006754614bb2
--- /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 2f8d98dee18a8b..1eb376fb5c1abd 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 67d451df2f7ff2..0aabde4ce1994a 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 a752cc4015293f..662bacf54a4efd 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)) {

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to