diff --git a/bolt/include/bolt/Core/BinarySection.h b/bolt/include/bolt/Core/BinarySection.h
--- a/bolt/include/bolt/Core/BinarySection.h
+++ b/bolt/include/bolt/Core/BinarySection.h
@@ -296,6 +296,16 @@
     return make_range(Relocations.begin(), Relocations.end());
   }
 
+  /// Iterate over all dynamic relocations for this section.
+  iterator_range<RelocationSetType::iterator> dynamicRelocations() {
+    return make_range(DynamicRelocations.begin(), DynamicRelocations.end());
+  }
+
+  /// Iterate over all dynamic relocations for this section.
+  iterator_range<RelocationSetType::const_iterator> dynamicRelocations() const {
+    return make_range(DynamicRelocations.begin(), DynamicRelocations.end());
+  }
+
   /// Does this section have any non-pending relocations?
   bool hasRelocations() const { return !Relocations.empty(); }
 
diff --git a/bolt/include/bolt/Core/Relocation.h b/bolt/include/bolt/Core/Relocation.h
--- a/bolt/include/bolt/Core/Relocation.h
+++ b/bolt/include/bolt/Core/Relocation.h
@@ -89,6 +89,9 @@
   /// Return true if relocation type is for thread local storage.
   static bool isTLS(uint64_t Type);
 
+  /// Return code for a NONE relocation
+  static uint64_t getNone();
+
   /// Return code for a PC-relative 4-byte relocation
   static uint64_t getPC32();
 
@@ -98,6 +101,10 @@
   /// Return true if this relocation is PC-relative. Return false otherwise.
   bool isPCRelative() const { return isPCRelative(Type); }
 
+  /// Return true if this relocation is R_*_RELATIVE type. Return false
+  /// otherwise.
+  bool isRelative() const { return isRelative(Type); }
+
   /// Emit relocation at a current \p Streamer' position. The caller is
   /// responsible for setting the position correctly.
   size_t emit(MCStreamer *Streamer) const;
diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -22,6 +22,7 @@
 #include "llvm/Support/Error.h"
 #include <map>
 #include <set>
+#include <unordered_map>
 
 namespace llvm {
 
@@ -124,7 +125,7 @@
   void processLKSMPLocks();
 
   /// Read relocations from a given section.
-  void readDynamicRelocations(const object::SectionRef &Section);
+  void readDynamicRelocations(const object::SectionRef &Section, bool IsJmpRel);
 
   /// Read relocations from a given section.
   void readRelocations(const object::SectionRef &Section);
@@ -201,6 +202,10 @@
   /// \p OldAddress address in the original binary.
   uint64_t getNewFunctionAddress(uint64_t OldAddress);
 
+  /// Return address of a function or moved data in the new binary
+  /// corresponding to \p OldAddress address in the original binary.
+  uint64_t getNewFunctionOrDataAddress(uint64_t OldAddress);
+
   /// Return value for the symbol \p Name in the output.
   uint64_t getNewValueForSymbol(const StringRef Name);
 
@@ -299,6 +304,14 @@
       const std::vector<uint32_t> &NewSectionIndex, WriteFuncTy Write,
       StrTabFuncTy AddToStrTab);
 
+  /// Get output index in dynamic symbol table.
+  uint32_t getOutputDynamicSymbolIndex(const MCSymbol *Symbol) {
+    auto It = SymbolIndex.find(Symbol);
+    if (It != SymbolIndex.end())
+      return It->second;
+    return 0;
+  }
+
   /// Add a notes section containing the BOLT revision and command line options.
   void addBoltInfoSection();
 
@@ -426,11 +439,19 @@
   /// Location and size of dynamic relocations.
   Optional<uint64_t> DynamicRelocationsAddress;
   uint64_t DynamicRelocationsSize{0};
+  uint64_t DynamicRelativeRelocationsCount{0};
 
   /// PLT relocations are special kind of dynamic relocations stored separately.
   Optional<uint64_t> PLTRelocationsAddress;
   uint64_t PLTRelocationsSize{0};
 
+  /// True if relocation of specified type came from .rela.plt
+  DenseMap<uint64_t, bool> IsJmpRelocation;
+
+  /// Index of specified symbol in the dynamic symbol table. NOTE Currently it
+  /// is filled and used only with the relocations-related symbols.
+  std::unordered_map<const MCSymbol *, uint32_t> SymbolIndex;
+
   /// Store all non-zero symbols in this map for a quick address lookup.
   std::map<uint64_t, llvm::object::SymbolRef> FileSymRefs;
 
diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp
--- a/bolt/lib/Core/Relocation.cpp
+++ b/bolt/lib/Core/Relocation.cpp
@@ -533,11 +533,7 @@
   return isGOTX86(Type);
 }
 
-bool Relocation::isNone(uint64_t Type) {
-  if (Arch == Triple::aarch64)
-    return Type == ELF::R_AARCH64_NONE;
-  return Type == ELF::R_X86_64_NONE;
-}
+bool Relocation::isNone(uint64_t Type) { return Type == getNone(); }
 
 bool Relocation::isRelative(uint64_t Type) {
   if (Arch == Triple::aarch64)
@@ -557,10 +553,10 @@
   return isTLSX86(Type);
 }
 
-bool Relocation::isPCRelative(uint64_t Type) {
+uint64_t Relocation::getNone() {
   if (Arch == Triple::aarch64)
-    return isPCRelativeAArch64(Type);
-  return isPCRelativeX86(Type);
+    return ELF::R_AARCH64_NONE;
+  return ELF::R_X86_64_NONE;
 }
 
 uint64_t Relocation::getPC32() {
@@ -575,6 +571,12 @@
   return ELF::R_X86_64_PC64;
 }
 
+bool Relocation::isPCRelative(uint64_t Type) {
+  if (Arch == Triple::aarch64)
+    return isPCRelativeAArch64(Type);
+  return isPCRelativeX86(Type);
+}
+
 size_t Relocation::emit(MCStreamer *Streamer) const {
   const size_t Size = getSizeForType(Type);
   MCContext &Ctx = Streamer->getContext();
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -1683,6 +1683,40 @@
   auto *ELF64BE = cast<ELF64BEObjectFile>(Obj);
   return getRelocationAddend(ELF64BE, Rel);
 }
+
+template <typename ELFT>
+uint32_t getRelocationSymbol(const ELFObjectFile<ELFT> *Obj,
+                             const RelocationRef &RelRef) {
+  using ELFShdrTy = typename ELFT::Shdr;
+  uint32_t Symbol = 0;
+  const ELFFile<ELFT> &EF = Obj->getELFFile();
+  DataRefImpl Rel = RelRef.getRawDataRefImpl();
+  const ELFShdrTy *RelocationSection = cantFail(EF.getSection(Rel.d.a));
+  switch (RelocationSection->sh_type) {
+  default:
+    llvm_unreachable("unexpected relocation section type");
+  case ELF::SHT_REL:
+    Symbol = Obj->getRel(Rel)->getSymbol(EF.isMips64EL());
+    break;
+  case ELF::SHT_RELA:
+    Symbol = Obj->getRela(Rel)->getSymbol(EF.isMips64EL());
+    break;
+  }
+
+  return Symbol;
+}
+
+uint32_t getRelocationSymbol(const ELFObjectFileBase *Obj,
+                             const RelocationRef &Rel) {
+  if (auto *ELF32LE = dyn_cast<ELF32LEObjectFile>(Obj))
+    return getRelocationSymbol(ELF32LE, Rel);
+  if (auto *ELF64LE = dyn_cast<ELF64LEObjectFile>(Obj))
+    return getRelocationSymbol(ELF64LE, Rel);
+  if (auto *ELF32BE = dyn_cast<ELF32BEObjectFile>(Obj))
+    return getRelocationSymbol(ELF32BE, Rel);
+  auto *ELF64BE = cast<ELF64BEObjectFile>(Obj);
+  return getRelocationSymbol(ELF64BE, Rel);
+}
 } // anonymous namespace
 
 bool RewriteInstance::analyzeRelocation(
@@ -1812,7 +1846,8 @@
     if (PLTRelSectionOrErr->getSize() != PLTRelocationsSize)
       report_error("section size mismatch for DT_PLTRELSZ",
                    errc::executable_format_error);
-    readDynamicRelocations(PLTRelSectionOrErr->getSectionRef());
+    readDynamicRelocations(PLTRelSectionOrErr->getSectionRef(),
+                           /*IsJmpRel*/ true);
   }
 
   // The rest of dynamic relocations - DT_RELA.
@@ -1825,7 +1860,8 @@
     if (DynamicRelSectionOrErr->getSize() != DynamicRelocationsSize)
       report_error("section size mismatch for DT_RELASZ",
                    errc::executable_format_error);
-    readDynamicRelocations(DynamicRelSectionOrErr->getSectionRef());
+    readDynamicRelocations(DynamicRelSectionOrErr->getSectionRef(),
+                           /*IsJmpRel*/ false);
   }
 }
 
@@ -2049,7 +2085,8 @@
   }
 }
 
-void RewriteInstance::readDynamicRelocations(const SectionRef &Section) {
+void RewriteInstance::readDynamicRelocations(const SectionRef &Section,
+                                             bool IsJmpRel) {
   assert(BinarySection(*BC, Section).isAllocatable() && "allocatable expected");
 
   LLVM_DEBUG({
@@ -2059,7 +2096,7 @@
   });
 
   for (const RelocationRef &Rel : Section.relocations()) {
-    uint64_t RType = Rel.getType();
+    const uint64_t RType = Rel.getType();
     if (Relocation::isNone(RType))
       continue;
 
@@ -2087,7 +2124,13 @@
              << " : + 0x" << Twine::utohexstr(Addend) << '\n'
     );
 
-    BC->addDynamicRelocation(Rel.getOffset(), Symbol, Rel.getType(), Addend);
+    if (IsJmpRel)
+      IsJmpRelocation[RType] = true;
+
+    if (Symbol)
+      SymbolIndex[Symbol] = getRelocationSymbol(InputFile, Rel);
+
+    BC->addDynamicRelocation(Rel.getOffset(), Symbol, RType, Addend);
   }
 }
 
@@ -4700,28 +4743,108 @@
 RewriteInstance::patchELFAllocatableRelaSections(ELFObjectFile<ELFT> *File) {
   using Elf_Rela = typename ELFT::Rela;
   raw_fd_ostream &OS = Out->os();
+  const ELFFile<ELFT> &EF = File->getELFFile();
 
-  for (BinarySection &RelaSection : BC->allocatableRelaSections()) {
-    for (const RelocationRef &Rel : RelaSection.getSectionRef().relocations()) {
-      uint64_t RType = Rel.getType();
-      if (!Relocation::isRelative(RType) && !Relocation::isIRelative(RType))
-        continue;
-      DataRefImpl DRI = Rel.getRawDataRefImpl();
-      const Elf_Rela *RelA = File->getRela(DRI);
-      auto Address = RelA->r_addend;
-      uint64_t NewAddress = getNewFunctionAddress(Address);
-      if (!NewAddress)
-        continue;
-      LLVM_DEBUG(dbgs() << "BOLT-DEBUG: patching (I)RELATIVE "
-                        << RelaSection.getName() << " entry 0x"
-                        << Twine::utohexstr(Address) << " with 0x"
-                        << Twine::utohexstr(NewAddress) << '\n');
-      Elf_Rela NewRelA = *RelA;
-      NewRelA.r_addend = NewAddress;
-      OS.pwrite(reinterpret_cast<const char *>(&NewRelA), sizeof(NewRelA),
-                reinterpret_cast<const char *>(RelA) - File->getData().data());
+  uint64_t RelDynOffset = 0, RelDynEndOffset = 0;
+  uint64_t RelPltOffset = 0, RelPltEndOffset = 0;
+
+  auto setSectionFileOffsets = [&](uint64_t Address, uint64_t &Start,
+                                   uint64_t &End) {
+    ErrorOr<BinarySection &> Section = BC->getSectionForAddress(Address);
+    Start = Section->getInputFileOffset();
+    End = Start + Section->getSize();
+  };
+
+  if (!DynamicRelocationsAddress && !PLTRelocationsAddress)
+    return;
+
+  if (DynamicRelocationsAddress)
+    setSectionFileOffsets(*DynamicRelocationsAddress, RelDynOffset,
+                          RelDynEndOffset);
+
+  if (PLTRelocationsAddress)
+    setSectionFileOffsets(*PLTRelocationsAddress, RelPltOffset,
+                          RelPltEndOffset);
+
+  DynamicRelativeRelocationsCount = 0;
+
+  auto writeRela = [&OS](const Elf_Rela *RelA, uint64_t &Offset) {
+    OS.pwrite(reinterpret_cast<const char *>(RelA), sizeof(*RelA), Offset);
+    Offset += sizeof(*RelA);
+  };
+
+  auto writeRelocations = [&](bool PatchRelative) {
+    for (BinarySection &Section : BC->allocatableSections()) {
+      for (const Relocation &Rel : Section.dynamicRelocations()) {
+        const bool IsRelative = Rel.isRelative();
+        if (PatchRelative != IsRelative)
+          continue;
+
+        if (IsRelative)
+          ++DynamicRelativeRelocationsCount;
+
+        Elf_Rela NewRelA;
+        uint64_t SectionAddress = Section.getOutputAddress();
+        SectionAddress =
+            SectionAddress == 0 ? Section.getAddress() : SectionAddress;
+        MCSymbol *Symbol = Rel.Symbol;
+        uint32_t SymbolIdx = 0;
+        uint64_t Addend = Rel.Addend;
+
+        if (Rel.Symbol) {
+          SymbolIdx = getOutputDynamicSymbolIndex(Symbol);
+        } else {
+          // Usually this case is used for R_*_(I)RELATIVE relocations
+          const uint64_t Address = getNewFunctionOrDataAddress(Addend);
+          if (Address)
+            Addend = Address;
+        }
+
+        NewRelA.setSymbolAndType(SymbolIdx, Rel.Type, EF.isMips64EL());
+        NewRelA.r_offset = SectionAddress + Rel.Offset;
+        NewRelA.r_addend = Addend;
+
+        const bool IsJmpRel =
+            !!(IsJmpRelocation.find(Rel.Type) != IsJmpRelocation.end());
+        uint64_t &Offset = IsJmpRel ? RelPltOffset : RelDynOffset;
+        const uint64_t &EndOffset =
+            IsJmpRel ? RelPltEndOffset : RelDynEndOffset;
+        if (!Offset || !EndOffset) {
+          errs() << "BOLT-ERROR: Invalid offsets for dynamic relocation\n";
+          exit(1);
+        }
+
+        if (Offset + sizeof(NewRelA) > EndOffset) {
+          errs() << "BOLT-ERROR: Offset overflow for dynamic relocation\n";
+          exit(1);
+        }
+
+        writeRela(&NewRelA, Offset);
+      }
     }
-  }
+  };
+
+  // The dynamic linker expects R_*_RELATIVE relocations to be emitted first
+  writeRelocations(/* PatchRelative */ true);
+  writeRelocations(/* PatchRelative */ false);
+
+  auto fillNone = [&](uint64_t &Offset, uint64_t EndOffset) {
+    if (!Offset)
+      return;
+
+    typename ELFObjectFile<ELFT>::Elf_Rela RelA;
+    RelA.setSymbolAndType(0, Relocation::getNone(), EF.isMips64EL());
+    RelA.r_offset = 0;
+    RelA.r_addend = 0;
+    while (Offset < EndOffset)
+      writeRela(&RelA, Offset);
+
+    assert(Offset == EndOffset && "Unexpected section overflow");
+  };
+
+  // Fill the rest of the sections with R_*_NONE relocations
+  fillNone(RelDynOffset, RelDynEndOffset);
+  fillNone(RelPltOffset, RelPltEndOffset);
 }
 
 template <typename ELFT>
@@ -4737,7 +4860,8 @@
     }
   }
   if (!GOTSection.getObject()) {
-    errs() << "BOLT-INFO: no .got section found\n";
+    if (!BC->IsStaticExecutable)
+      errs() << "BOLT-INFO: no .got section found\n";
     return;
   }
 
@@ -4796,6 +4920,9 @@
     default:
       ShouldPatch = false;
       break;
+    case ELF::DT_RELACOUNT:
+      NewDE.d_un.d_val = DynamicRelativeRelocationsCount;
+      break;
     case ELF::DT_INIT:
     case ELF::DT_FINI: {
       if (BC->HasRelocations) {
@@ -4898,14 +5025,21 @@
     case ELF::DT_PLTRELSZ:
       PLTRelocationsSize = Dyn.getVal();
       break;
+    case ELF::DT_RELACOUNT:
+      DynamicRelativeRelocationsCount = Dyn.getVal();
+      break;
     }
   }
 
-  if (!DynamicRelocationsAddress)
+  if (!DynamicRelocationsAddress || !DynamicRelocationsSize) {
+    DynamicRelocationsAddress.reset();
     DynamicRelocationsSize = 0;
+  }
 
-  if (!PLTRelocationsAddress)
+  if (!PLTRelocationsAddress || !PLTRelocationsSize) {
+    PLTRelocationsAddress.reset();
     PLTRelocationsSize = 0;
+  }
 }
 
 uint64_t RewriteInstance::getNewFunctionAddress(uint64_t OldAddress) {
@@ -4918,6 +5052,17 @@
   return Function->getOutputAddress();
 }
 
+uint64_t RewriteInstance::getNewFunctionOrDataAddress(uint64_t OldAddress) {
+  if (uint64_t Function = getNewFunctionAddress(OldAddress))
+    return Function;
+
+  const BinaryData *BD = BC->getBinaryDataAtAddress(OldAddress);
+  if (BD && BD->isMoved())
+    return BD->getOutputAddress();
+
+  return 0;
+}
+
 void RewriteInstance::rewriteFile() {
   std::error_code EC;
   Out = std::make_unique<ToolOutputFile>(opts::OutputFilename, EC,
@@ -5090,14 +5235,14 @@
   // Copy non-allocatable sections once allocatable part is finished.
   rewriteNoteSections();
 
-  // Patch dynamic section/segment.
-  patchELFDynamic();
-
   if (BC->HasRelocations) {
     patchELFAllocatableRelaSections();
     patchELFGOT();
   }
 
+  // Patch dynamic section/segment.
+  patchELFDynamic();
+
   // Update ELF book-keeping info.
   patchELFSectionHeaderTable();
 
diff --git a/bolt/test/runtime/AArch64/Inputs/runtime_relocs.c b/bolt/test/runtime/AArch64/Inputs/runtime_relocs.c
new file mode 100644
--- /dev/null
+++ b/bolt/test/runtime/AArch64/Inputs/runtime_relocs.c
@@ -0,0 +1,13 @@
+int a = 1;
+__attribute__((used)) int *b = &a; // R_*_ABS64
+
+static int c;
+__attribute__((used)) static int *d = &c; // R_*_RELATIVE
+
+__thread int t1 = 0;
+
+int inc(int var) {
+  ++a;  // R_*_GLOB_DAT
+  ++t1; // R_*_TLSDESC
+  return var + 1;
+}
diff --git a/bolt/test/runtime/AArch64/Inputs/tls_ld.yaml b/bolt/test/runtime/AArch64/Inputs/tls_ld.yaml
new file mode 100644
--- /dev/null
+++ b/bolt/test/runtime/AArch64/Inputs/tls_ld.yaml
@@ -0,0 +1,155 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_AARCH64
+  Entry:           0x590
+ProgramHeaders:
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R, PF_W ]
+    FirstSec:        .dynsym
+    LastSec:         .got
+    Align:           0x10000
+  - Type:            PT_DYNAMIC
+    Flags:           [ PF_W, PF_R ]
+    FirstSec:        .dynamic
+    LastSec:         .dynamic
+    VAddr:           0x10DF0
+    Align:           0x8
+  - Type:            PT_TLS
+    Flags:           [ PF_R ]
+    FirstSec:        .tbss
+    LastSec:         .tbss
+    VAddr:           0x10DE0
+    Align:           0x4
+  - Type:            PT_GNU_EH_FRAME
+    Flags:           [ PF_R ]
+    FirstSec:        .eh_frame_hdr
+    LastSec:         .eh_frame_hdr
+    VAddr:           0x6B8
+    Align:           0x4
+Sections:
+  - Name:            .dynsym
+    Type:            SHT_DYNSYM
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x250
+    Link:            .dynstr
+    AddressAlign:    0x8
+  - Name:            .dynstr
+    Type:            SHT_STRTAB
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x340
+    AddressAlign:    0x1
+  - Name:            .plt
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x540
+    AddressAlign:    0x10
+    EntSize:         0x10
+    Content:         F07BBFA99000009011FE47F910E23F9120021FD61F2003D51F2003D51F2003D5900000B0110240F91002009120021FD6900000B0110640F91022009120021FD6900000B0110A40F91042009120021FD6
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x590
+    AddressAlign:    0x8
+    Content:         8000009000F047F9400000B4F9FFFF17C0035FD61F2003D5800000B000800091810000B0218000913F0000EBC00000548100009021E447F9610000B4F00301AA00021FD6C0035FD6800000B000800091810000B021800091210000CB22FC7FD3410C818BFF0781EB21FC4193C00000548200009042E047F9620000B4F00302AA00021FD6C0035FD6FD7BBEA9FD030091F30B00F9930000B060824039400100358000009000DC47F9800000B4800000B0000C40F9C7FFFF97D8FFFF972000805260820039F30B40F9FD7BC2A8C0035FD6DEFFFF171F2003D5FD7BBEA9FD030091F30B00F9F303002A8000009000403F91BCFFFF971F2003D5E10300AA60060011F30B40F9220040B942040011220000B9FD7BC2A8C0035FD6
+  - Name:            .rela.text
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .text
+    Relocations:
+      - Offset:          0x5C4
+        Symbol:          t1
+        Type:            R_AARCH64_TLSDESC_LD64_LO12
+  - Name:            .eh_frame_hdr
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x6B8
+    AddressAlign:    0x4
+    Content:         011B033B3400000005000000F0FEFFFF4C00000020FFFFFF6000000060FFFFFF74000000A8FFFFFF98000000B0FFFFFFB0000000
+  - Name:            .eh_frame
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x6F0
+    AddressAlign:    0x8
+    Content:         1000000000000000017A520004781E011B0C1F0010000000180000009CFEFFFF3000000000000000100000002C000000B8FEFFFF40000000000000002000000040000000E4FEFFFF4800000000410E209D049E034293024EDEDDD30E00000000140000006400000008FFFFFF040000000000000000000000200000007C000000F8FEFFFF4000000000410E209D049E034293024CDEDDD30E0000000000000000
+  - Name:            .tbss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC, SHF_TLS ]
+    Address:         0x10DE0
+    AddressAlign:    0x4
+    Offset:          0xDE0
+    Size:            0x4
+  - Name:            .dynamic
+    Type:            SHT_DYNAMIC
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x10DF0
+    Link:            .dynstr
+    AddressAlign:    0x8
+    Entries:
+      - Tag:             DT_STRTAB
+        Value:           0x340
+      - Tag:             DT_SYMTAB
+        Value:           0x250
+      - Tag:             DT_PLTRELSZ
+        Value:           0x18
+      - Tag:             DT_PLTREL
+        Value:           0x7
+      - Tag:             DT_JMPREL
+        Value:           0x418
+      - Tag:             DT_NULL
+        Value:           0x0
+  - Name:            .got
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x10FB0
+    AddressAlign:    0x8
+    EntSize:         0x8
+    Content:         F00D010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+  - Name:            .rela.plt
+    Type:            SHT_RELA
+    Flags:           [ SHF_ALLOC, SHF_INFO_LINK ]
+    Address:         0x418
+    Link:            .dynsym
+    AddressAlign:    0x8
+    Info:            .got
+    Relocations:
+      - Offset:          0x10fb0
+        Symbol:          t1
+        Type:            R_AARCH64_TLSDESC
+  - Type:            SectionHeaderTable
+    Sections:
+      - Name:            .dynsym
+      - Name:            .dynstr
+      - Name:            .rela.plt
+      - Name:            .plt
+      - Name:            .text
+      - Name:            .rela.text
+      - Name:            .eh_frame_hdr
+      - Name:            .eh_frame
+      - Name:            .tbss
+      - Name:            .dynamic
+      - Name:            .got
+      - Name:            .symtab
+      - Name:            .strtab
+      - Name:            .shstrtab
+Symbols:
+  - Name:            .text
+    Type:            STT_SECTION
+    Section:         .text
+    Value:           0x590
+  - Name:            t1
+    Type:            STT_TLS
+    Section:         .tbss
+    Binding:         STB_GLOBAL
+    Size:            0x4
+DynamicSymbols:
+  - Name:            t1
+    Type:            STT_TLS
+    Section:         .tbss
+    Binding:         STB_GLOBAL
+    Size:            0x4
+...
diff --git a/bolt/test/runtime/AArch64/Inputs/tls_trad.yaml b/bolt/test/runtime/AArch64/Inputs/tls_trad.yaml
new file mode 100644
--- /dev/null
+++ b/bolt/test/runtime/AArch64/Inputs/tls_trad.yaml
@@ -0,0 +1,162 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_AARCH64
+  Entry:           0x590
+ProgramHeaders:
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R, PF_W ]
+    FirstSec:        .dynsym
+    LastSec:         .got
+    Align:           0x10000
+  - Type:            PT_DYNAMIC
+    Flags:           [ PF_W, PF_R ]
+    FirstSec:        .dynamic
+    LastSec:         .dynamic
+    VAddr:           0x10DF0
+    Align:           0x8
+  - Type:            PT_TLS
+    Flags:           [ PF_R ]
+    FirstSec:        .tbss
+    LastSec:         .tbss
+    VAddr:           0x10DE0
+    Align:           0x4
+  - Type:            PT_GNU_EH_FRAME
+    Flags:           [ PF_R ]
+    FirstSec:        .eh_frame_hdr
+    LastSec:         .eh_frame_hdr
+    VAddr:           0x6B8
+    Align:           0x4
+Sections:
+  - Name:            .dynsym
+    Type:            SHT_DYNSYM
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x250
+    Link:            .dynstr
+    AddressAlign:    0x8
+  - Name:            .dynstr
+    Type:            SHT_STRTAB
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x340
+    AddressAlign:    0x1
+  - Name:            .rela.dyn
+    Type:            SHT_RELA
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x400
+    Link:            .dynsym
+    AddressAlign:    0x8
+    Relocations:
+      - Offset:          0x10FD0
+        Symbol:          t1
+        Type:            R_AARCH64_TLS_DTPMOD64
+      - Offset:          0x10FD8
+        Symbol:          t1
+        Type:            R_AARCH64_TLS_DTPREL64
+  - Name:            .plt
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x540
+    AddressAlign:    0x10
+    EntSize:         0x10
+    Content:         F07BBFA99000009011FE47F910E23F9120021FD61F2003D51F2003D51F2003D5900000B0110240F91002009120021FD6900000B0110640F91022009120021FD6900000B0110A40F91042009120021FD6
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x590
+    AddressAlign:    0x8
+    Content:         8000009000F047F9400000B4F9FFFF17C0035FD61F2003D5800000B000800091810000B0218000913F0000EBC00000548100009021E447F9610000B4F00301AA00021FD6C0035FD6800000B000800091810000B021800091210000CB22FC7FD3410C818BFF0781EB21FC4193C00000548200009042E047F9620000B4F00302AA00021FD6C0035FD6FD7BBEA9FD030091F30B00F9930000B060824039400100358000009000DC47F9800000B4800000B0000C40F9C7FFFF97D8FFFF972000805260820039F30B40F9FD7BC2A8C0035FD6DEFFFF171F2003D5FD7BBEA9FD030091F30B00F9F303002A8000009000403F91BCFFFF971F2003D5E10300AA60060011F30B40F9220040B942040011220000B9FD7BC2A8C0035FD6
+  - Name:            .eh_frame_hdr
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x6B8
+    AddressAlign:    0x4
+    Content:         011B033B3400000005000000F0FEFFFF4C00000020FFFFFF6000000060FFFFFF74000000A8FFFFFF98000000B0FFFFFFB0000000
+  - Name:            .eh_frame
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x6F0
+    AddressAlign:    0x8
+    Content:         1000000000000000017A520004781E011B0C1F0010000000180000009CFEFFFF3000000000000000100000002C000000B8FEFFFF40000000000000002000000040000000E4FEFFFF4800000000410E209D049E034293024EDEDDD30E00000000140000006400000008FFFFFF040000000000000000000000200000007C000000F8FEFFFF4000000000410E209D049E034293024CDEDDD30E0000000000000000
+  - Name:            .tbss
+    Type:            SHT_NOBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC, SHF_TLS ]
+    Address:         0x10DE0
+    AddressAlign:    0x4
+    Offset:          0xDE0
+    Size:            0x4
+  - Name:            .dynamic
+    Type:            SHT_DYNAMIC
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x10DF0
+    Link:            .dynstr
+    AddressAlign:    0x8
+    Entries:
+      - Tag:             DT_STRTAB
+        Value:           0x340
+      - Tag:             DT_SYMTAB
+        Value:           0x250
+      - Tag:             DT_RELA
+        Value:           0x400
+      - Tag:             DT_RELASZ
+        Value:           0x30
+      - Tag:             DT_RELAENT
+        Value:           0x18
+      - Tag:             DT_RELACOUNT
+        Value:           0x3
+      - Tag:             DT_NULL
+        Value:           0x0
+  - Name:            .got
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_WRITE, SHF_ALLOC ]
+    Address:         0x10FB0
+    AddressAlign:    0x8
+    EntSize:         0x8
+    Content:         F00D010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+  - Name:            .rela.text
+    Type:            SHT_RELA
+    Flags:           [ SHF_INFO_LINK ]
+    Link:            .symtab
+    AddressAlign:    0x8
+    Info:            .text
+    Relocations:
+      - Offset:          0x678
+        Symbol:          t1
+        Type:            R_AARCH64_TLSGD_ADR_PAGE21
+      - Offset:          0x67C
+        Symbol:          t1
+        Type:            R_AARCH64_TLSGD_ADD_LO12_NC
+  - Type:            SectionHeaderTable
+    Sections:
+      - Name:            .dynsym
+      - Name:            .dynstr
+      - Name:            .rela.dyn
+      - Name:            .plt
+      - Name:            .text
+      - Name:            .rela.text
+      - Name:            .eh_frame_hdr
+      - Name:            .eh_frame
+      - Name:            .tbss
+      - Name:            .dynamic
+      - Name:            .got
+      - Name:            .symtab
+      - Name:            .strtab
+      - Name:            .shstrtab
+Symbols:
+  - Name:            .text
+    Type:            STT_SECTION
+    Section:         .text
+    Value:           0x590
+  - Name:            t1
+    Type:            STT_TLS
+    Section:         .tbss
+    Binding:         STB_GLOBAL
+    Size:            0x4
+DynamicSymbols:
+  - Name:            t1
+    Type:            STT_TLS
+    Section:         .tbss
+    Binding:         STB_GLOBAL
+    Size:            0x4
+...
diff --git a/bolt/test/runtime/AArch64/runtime_relocs.c b/bolt/test/runtime/AArch64/runtime_relocs.c
new file mode 100644
--- /dev/null
+++ b/bolt/test/runtime/AArch64/runtime_relocs.c
@@ -0,0 +1,64 @@
+// This test checks dynamic relocations support for aarch64.
+
+// RUN: %clang %cflags -pie -fPIC %S/Inputs/runtime_relocs.c \
+// RUN:    -shared -fuse-ld=lld -o %t.so -Wl,-q -Wl,-soname=rel.so
+// RUN: %clang %cflags -no-pie %s -fuse-ld=lld \
+// RUN:    -o %t.exe -Wl,-q %t.so
+// RUN: llvm-bolt %t.so -o %t.bolt.so -use-old-text=0 -lite=0
+// RUN: llvm-bolt %t.exe -o %t.bolt.exe -use-old-text=0 -lite=0
+// RUN: LD_PRELOAD=%t.bolt.so %t.bolt.exe
+
+// Check relocations in library:
+//
+// RUN: llvm-readelf -r %t.bolt.so | FileCheck %s -check-prefix=CHECKLIB
+//
+// CHECKLIB: 0000000600000401 R_AARCH64_GLOB_DAT     {{.*}} a + 0
+// CHECKLIB: 0000000700000407 R_AARCH64_TLSDESC      {{.*}} t1 + 0
+// CHECKLIB: 0000000600000101 R_AARCH64_ABS64        {{.*}} a + 0
+
+// Check relocations in executable:
+//
+// RUN: llvm-readelf -r %t.bolt.exe | FileCheck %s -check-prefix=CHECKEXE
+//
+// CHECKEXE: 0000000600000406 R_AARCH64_TLS_TPREL64  {{.*}} t1 + 0
+// CHECKEXE: 0000000800000400 R_AARCH64_COPY         {{.*}} a + 0
+// CHECKEXE: 0000000700000402 R_AARCH64_JUMP_SLOT    {{.*}} inc + 0
+
+// Check traditional TLS relocations R_AARCH64_TLS_DTPMOD64 and
+// R_AARCH64_TLS_DTPREL64 emitted correctly after bolt. Since these
+// relocations are obsolete and clang and lld does not support them,
+// the initial binary was built with gcc and ld with -mtls-dialect=trad flag.
+//
+// RUN: yaml2obj %p/Inputs/tls_trad.yaml &> %t.trad.so
+// RUN: llvm-bolt %t.trad.so -o %t.trad.bolt.so -use-old-text=0 -lite=0
+// RUN: llvm-readelf -r %t.trad.so | FileCheck %s -check-prefix=CHECKTRAD
+//
+// CHECKTRAD: 0000000100000404 R_AARCH64_TLS_DTPMOD64 {{.*}} t1 + 0
+// CHECKTRAD: 0000000100000405 R_AARCH64_TLS_DTPREL64 {{.*}} t1 + 0
+
+// The ld linker emits R_AARCH64_TLSDESC to .rela.plt section, check that
+// it is emitted correctly.
+//
+// RUN: yaml2obj %p/Inputs/tls_ld.yaml &> %t.ld.so
+// RUN: llvm-bolt %t.ld.so -o %t.ld.bolt.so -use-old-text=0 -lite=0
+// RUN: llvm-readelf -r %t.ld.bolt.so | FileCheck %s -check-prefix=CHECKLD
+//
+// CHECKLD: 0000000100000407 R_AARCH64_TLSDESC        {{.*}} t1 + 0
+
+extern int a; // R_*_COPY
+
+extern __thread int t1; // R_*_TLS_TPREL64
+
+int inc(int a); // R_*_JUMP_SLOT
+
+int dec(int a) { return a - 1; }
+
+void *resolver() { return dec; }
+
+int ifuncDec(int a) __attribute__((ifunc("resolver"))); // R_*_IRELATIVE
+
+int main() {
+  ++t1;
+  ifuncDec(a);
+  inc(a);
+}