Merge changes from topic 'rebase_r256229'

* changes:
  Fix up mclinker so that it builds/runs for LLVM rebase to r256229.
  Rebase mclinker for LLVM update to r256229.
diff --git a/include/mcld/Fragment/Fragment.h b/include/mcld/Fragment/Fragment.h
index 76a5028..3e84811 100644
--- a/include/mcld/Fragment/Fragment.h
+++ b/include/mcld/Fragment/Fragment.h
@@ -24,7 +24,7 @@
 /** \class Fragment
  *  \brief Fragment is the minimun linking unit of MCLinker.
  */
-class Fragment : public llvm::ilist_node<Fragment> {
+class Fragment : public llvm::ilist_node_with_parent<Fragment, SectionData> {
  public:
   enum Type { Alignment, Fillment, Region, Target, Stub, Null };
 
diff --git a/include/mcld/Fragment/Stub.h b/include/mcld/Fragment/Stub.h
index 94f58e9..3ef72ec 100644
--- a/include/mcld/Fragment/Stub.h
+++ b/include/mcld/Fragment/Stub.h
@@ -20,6 +20,8 @@
 
 namespace mcld {
 
+class BranchIsland;
+class IRBuilder;
 class Relocation;
 class ResolveInfo;
 
@@ -63,9 +65,15 @@
 
   /// isMyDuty - return true when the pReloc is problematic and the stub is able
   /// to fix it!
-  virtual bool isMyDuty(const class Relocation& pReloc,
+  virtual bool isMyDuty(const Relocation& pReloc,
                         uint64_t pSource,
-                        uint64_t pTargetSymValue) const = 0;
+                        uint64_t pTargetSymValue) const {
+    return false;
+  }
+
+  virtual bool isMyDuty(const FragmentRef& pFragRef) const {
+    return false;
+  }
 
   /// name - name of this stub
   virtual const std::string& name() const = 0;
@@ -96,6 +104,16 @@
 
   const_fixup_iterator fixup_end() const { return m_FixupList.end(); }
 
+  size_t fixup_size() const { return m_FixupList.size(); }
+
+  virtual void applyFixup(Relocation& pSrcReloc,
+                          IRBuilder& pBuilder,
+                          BranchIsland& pIsland);
+
+  virtual void applyFixup(FragmentRef& pSrcFragRef,
+                          IRBuilder& pBuilder,
+                          BranchIsland& pIsland);
+
   /// ----- modifiers ----- ///
   void setSymInfo(ResolveInfo* pSymInfo);
 
@@ -113,6 +131,9 @@
   /// addFixup - add a fixup from a existing fixup of the prototype
   void addFixup(const Fixup& pFixup);
 
+  const FixupListType& getFixupList() const { return m_FixupList; }
+  FixupListType&       getFixupList()       { return m_FixupList; }
+
  private:
   /// doClone - when adding a backend stub, we should implement this function
   virtual Stub* doClone() = 0;
diff --git a/include/mcld/GeneralOptions.h b/include/mcld/GeneralOptions.h
index 787c5eb..5b9c92b 100644
--- a/include/mcld/GeneralOptions.h
+++ b/include/mcld/GeneralOptions.h
@@ -251,11 +251,6 @@
 
   bool genUnwindInfo() const { return m_bGenUnwindInfo; }
 
-  // -G, max GP size option
-  void setGPSize(int gpsize) { m_GPSize = gpsize; }
-
-  int getGPSize() const { return m_GPSize; }
-
   HashStyle getHashStyle() const { return m_HashStyle; }
 
   bool hasGNUHash() const {
@@ -388,7 +383,6 @@
   bool m_bPrintICFSections : 1;   // --print-icf-sections
   ICF m_ICF;
   size_t m_ICFIterations;
-  uint32_t m_GPSize;  // -G, --gpsize
   StripSymbolMode m_StripSymbols;
   RpathList m_RpathList;
   ScriptList m_ScriptList;
diff --git a/include/mcld/IRBuilder.h b/include/mcld/IRBuilder.h
index b68b8fa..e613dac 100644
--- a/include/mcld/IRBuilder.h
+++ b/include/mcld/IRBuilder.h
@@ -353,6 +353,9 @@
   /// @return Total size of the inserted fragments.
   static uint64_t AppendEhFrame(EhFrame::CIE& pCIE, EhFrame& pEhFrame);
 
+  /// CreateLocalSymbol - Create a local symbol at the given FragmentRef.
+  ResolveInfo* CreateLocalSymbol(FragmentRef& pFragRef);
+
   /// AddSymbol - To add a symbol to the input file.
   /// This function create a new symbol and insert it into the input file. If
   /// mcld::Module has another symbol with the same name, then this function
diff --git a/include/mcld/LD/BranchIsland.h b/include/mcld/LD/BranchIsland.h
index c57e5fb..1e7ccda 100644
--- a/include/mcld/LD/BranchIsland.h
+++ b/include/mcld/LD/BranchIsland.h
@@ -74,6 +74,8 @@
   const_reloc_iterator reloc_end() const { return m_Relocations.end(); }
 
   /// observers
+  SectionData* getParent() const { return m_Entry.getParent(); }
+
   uint64_t offset() const;
 
   size_t size() const;
@@ -91,6 +93,8 @@
   /// addStub - add a stub into the island
   bool addStub(const Stub* pPrototype, const Relocation& pReloc, Stub& pStub);
 
+  void addStub(Stub& pStub);
+
   /// addRelocation - add a relocation into island
   bool addRelocation(Relocation& pReloc);
 
@@ -159,7 +163,7 @@
   Fragment& m_Entry;  // entry fragment of the island
   Fragment* m_pExit;  // exit fragment of the island
   Fragment* m_pRear;  // rear fragment of the island
-  size_t m_MaxSize;
+  const size_t m_MaxSize;
   std::string m_Name;
   StubMapType m_StubMap;
   /// m_Relocations - list of relocations created for stubs in this island
diff --git a/include/mcld/LD/BranchIslandFactory.h b/include/mcld/LD/BranchIslandFactory.h
index 575d40f..545173d 100644
--- a/include/mcld/LD/BranchIslandFactory.h
+++ b/include/mcld/LD/BranchIslandFactory.h
@@ -28,11 +28,10 @@
   /// ctor
   /// @param pMaxFwdBranchRange - the max forward branch range of the target
   /// @param pMaxBwdBranchRange - the max backward branch range of the target
-  /// @param pMaxIslandSize - a predifned value (64KB here) to decide the max
-  ///                         size of the island
+  /// @param pMaxIslandSize - the group size to place stubs between sections
   BranchIslandFactory(int64_t pMaxFwdBranchRange,
                       int64_t pMaxBwdBranchRange,
-                      size_t pMaxIslandSize = 65536U);
+                      size_t pMaxIslandSize);
 
   ~BranchIslandFactory();
 
diff --git a/include/mcld/LD/DiagCommonKinds.inc b/include/mcld/LD/DiagCommonKinds.inc
index 8340cd8..ac44022 100644
--- a/include/mcld/LD/DiagCommonKinds.inc
+++ b/include/mcld/LD/DiagCommonKinds.inc
@@ -181,8 +181,8 @@
      "unable to write output file %0")
 DIAG(warn_unsupported_option,
      DiagnosticEngine::Warning,
-     "Option `%0' is not implemented yet!",
-     "Option `%0' is not implemented yet!")
+     "%0: unsupported option",
+     "%0: unsupported option")
 DIAG(warn_shared_textrel,
      DiagnosticEngine::Warning,
      "Add DT_TEXTREL in a shared object!",
@@ -223,7 +223,3 @@
      DiagnosticEngine::Fatal,
      "missing text section for '%0' in file '%1'",
      "missing text section for '%0' in file '%1'")
-DIAG(eh_missing_exidx_section,
-     DiagnosticEngine::Fatal,
-     "missing .ARM.exidx section for '%0' in file '%1'",
-     "missing .ARM.exidx section for '%0' in file '%1'")
diff --git a/include/mcld/LD/DiagLayouts.inc b/include/mcld/LD/DiagLayouts.inc
index c41b775..e0e6d09 100644
--- a/include/mcld/LD/DiagLayouts.inc
+++ b/include/mcld/LD/DiagLayouts.inc
@@ -32,3 +32,9 @@
      DiagnosticEngine::Debug,
      "ICF folding section `%0' of `%1' into `%2' of `%3'",
      "ICF folding section `%0' of `%1' into `%2' of `%3'")
+DIAG(err_no_space_to_place_stubs,
+     DiagnosticEngine::Error,
+     "There is no space left to place stubs. Current stub group size: %0\n"
+     "Use --stub-group-size option to increase the group size.",
+     "There is no space left to place stubs. Current stub group size: %0\n"
+     "Use --stub-group-size option to increase the group size.")
diff --git a/include/mcld/LD/DiagMips.inc b/include/mcld/LD/DiagMips.inc
new file mode 100644
index 0000000..48c0169
--- /dev/null
+++ b/include/mcld/LD/DiagMips.inc
@@ -0,0 +1,53 @@
+// Mips specific errors and warnings on object file incompatibilities
+DIAG(error_Mips_incompatible_class,
+     DiagnosticEngine::Fatal,
+     "target '%0' is incompatible with '%1' in %2",
+     "target '%0' is incompatible with '%1' in %2")
+DIAG(error_Mips_inconsistent_arch,
+     DiagnosticEngine::Error,
+     "target arch '%0' is inconsist with the '%1' in %2",
+     "target arch '%0' is inconsist with the '%1' in %2")
+DIAG(error_Mips_abiflags_invalid_size,
+     DiagnosticEngine::Error,
+     "invalid size of .MIPS.abiflags section in %0",
+     "invalid size of .MIPS.abiflags section in %0")
+DIAG(error_Mips_abiflags_invalid_version,
+     DiagnosticEngine::Error,
+     "unexpected .MIPS.abiflags section version number '%0' in %1",
+     "unexpected .MIPS.abiflags section version number '%0' in %1")
+DIAG(error_Mips_inconsistent_abi,
+     DiagnosticEngine::Error,
+     "target ABI is incompatible with ABI in %0",
+     "target ABI is incompatible with ABI in %0")
+DIAG(error_Mips_inconsistent_mnan,
+     DiagnosticEngine::Error,
+     "target -mnan=%0 flag is incompatible with -mnan=%1 in %2",
+     "target -mnan=%0 flag is incompatible with -mnan=%1 in %2")
+DIAG(error_Mips_inconsistent_fp64,
+     DiagnosticEngine::Error,
+     "target -mfp flag is incompatible with -mfp in %0",
+     "target -mfp flag is incompatible with -mfp in %0")
+DIAG(error_Mips_m16_unsupported,
+     DiagnosticEngine::Error,
+     "MIPS16 extension is unsupported: %0",
+     "MIPS16 extension is unsupported: %0")
+DIAG(warn_Mips_abicalls_linking,
+     DiagnosticEngine::Warning,
+     "conflicting linking abicalls and non-abicalls files on %0.",
+     "conflicting linking abicalls and non-abicalls files on %0.")
+DIAG(warn_Mips_fp_abi_incompatible,
+     DiagnosticEngine::Warning,
+     "FP ABI %0 is incompatible with %1 used by %2",
+     "FP ABI %0 is incompatible with %1 used by %2")
+DIAG(warn_Mips_isa_incompatible,
+     DiagnosticEngine::Warning,
+     "inconsistent ISA between .MIPS.abiflags and ELF header e_flags field: %0",
+     "inconsistent ISA between .MIPS.abiflags and ELF header e_flags field: %0")
+DIAG(warn_Mips_isa_ext_incompatible,
+     DiagnosticEngine::Warning,
+     "inconsistent ISA extensions between .MIPS.abiflags and ELF header e_flags field: %0",
+     "inconsistent ISA extensions between .MIPS.abiflags and ELF header e_flags field: %0")
+DIAG(warn_Mips_ases_incompatible,
+     DiagnosticEngine::Warning,
+     "inconsistent ASEs between .MIPS.abiflags and ELF header e_flags field: %0",
+     "inconsistent ASEs between .MIPS.abiflags and ELF header e_flags field: %0")
diff --git a/include/mcld/LD/DiagnosticInfos.h b/include/mcld/LD/DiagnosticInfos.h
index 804394e..a82fda9 100644
--- a/include/mcld/LD/DiagnosticInfos.h
+++ b/include/mcld/LD/DiagnosticInfos.h
@@ -24,6 +24,7 @@
 #include "mcld/LD/DiagLayouts.inc"
 #include "mcld/LD/DiagGOTPLT.inc"
 #include "mcld/LD/DiagLDScript.inc"
+#include "mcld/LD/DiagMips.inc"
 #undef DIAG
   NUM_OF_BUILDIN_DIAGNOSTIC_INFO
 };
diff --git a/include/mcld/LD/ELFSegmentFactory.h b/include/mcld/LD/ELFSegmentFactory.h
index 12a5831..05c6747 100644
--- a/include/mcld/LD/ELFSegmentFactory.h
+++ b/include/mcld/LD/ELFSegmentFactory.h
@@ -57,6 +57,10 @@
   /// @param pType - p_type in ELF program header
   ELFSegment* produce(uint32_t pType, uint32_t pFlag = llvm::ELF::PF_R);
 
+  ELFSegment* insert(iterator pPosition,
+                     uint32_t pType,
+                     uint32_t pFlag = llvm::ELF::PF_R);
+
   void erase(iterator pSegment);
 
  private:
diff --git a/include/mcld/LD/SectionData.h b/include/mcld/LD/SectionData.h
index 22cda34..82027c2 100644
--- a/include/mcld/LD/SectionData.h
+++ b/include/mcld/LD/SectionData.h
@@ -61,6 +61,10 @@
 
   bool empty() const { return m_Fragments.empty(); }
 
+  static FragmentListType SectionData::*getSublistAccess(Fragment *) {
+    return &SectionData::m_Fragments;
+  }
+
   reference front() { return m_Fragments.front(); }
   const_reference front() const { return m_Fragments.front(); }
   reference back() { return m_Fragments.back(); }
diff --git a/include/mcld/LD/StubFactory.h b/include/mcld/LD/StubFactory.h
index 2a093d0..b3adf98 100644
--- a/include/mcld/LD/StubFactory.h
+++ b/include/mcld/LD/StubFactory.h
@@ -17,6 +17,7 @@
 
 class BranchIslandFactory;
 class IRBuilder;
+class FragmentRef;
 class Relocation;
 class Stub;
 
@@ -37,12 +38,18 @@
                IRBuilder& pBuilder,
                BranchIslandFactory& pBRIslandFactory);
 
+  Stub* create(FragmentRef& pFragRef,
+               IRBuilder& pBuilder,
+               BranchIslandFactory& pBRIslandFactory);
+
  private:
   /// findPrototype - find if there is a registered stub prototype for the given
   ///                 relocation
   Stub* findPrototype(const Relocation& pReloc,
                       const uint64_t pSource,
-                      uint64_t pTargetSymValue);
+                      uint64_t pTargetSymValue) const;
+
+  Stub* findPrototype(const FragmentRef& pFragRef) const;
 
  private:
   typedef std::vector<Stub*> StubPoolType;
diff --git a/include/mcld/Script/ScriptScanner.h b/include/mcld/Script/ScriptScanner.h
index 4a572a1..da54750 100644
--- a/include/mcld/Script/ScriptScanner.h
+++ b/include/mcld/Script/ScriptScanner.h
@@ -10,7 +10,11 @@
 #define MCLD_SCRIPT_SCRIPTSCANNER_H_
 
 #ifndef __FLEX_LEXER_H
-#include "FlexLexer.h"
+#ifdef ANDROID
+#include "mcld/Script/FlexLexer.h"
+#else
+#include <FlexLexer.h>
+#endif
 #endif
 
 #ifndef YY_DECL
diff --git a/include/mcld/Target/GNUInfo.h b/include/mcld/Target/GNUInfo.h
index 2a55688..d09e5d9 100644
--- a/include/mcld/Target/GNUInfo.h
+++ b/include/mcld/Target/GNUInfo.h
@@ -35,8 +35,7 @@
   virtual uint8_t ABIVersion() const { return 0x0; }
 
   /// defaultTextSegmentAddr - target should specify its own default start
-  /// address
-  /// of the text segment. esp. for exec.
+  /// address of the text segment. esp. for exec.
   virtual uint64_t defaultTextSegmentAddr() const { return 0x0; }
 
   /// flags - the value of ElfXX_Ehdr::e_flags
@@ -47,7 +46,6 @@
 
   /// dyld - the name of the default dynamic linker
   /// target may override this function if needed.
-  /// @ref gnu ld, bfd/elf32-i386.c:521
   virtual const char* dyld() const { return "/usr/lib/libc.so.1"; }
 
   /// isDefaultExecStack - target should specify whether the stack is default
@@ -62,6 +60,9 @@
   /// here. If target favors the different size, please override this function
   virtual uint64_t abiPageSize() const { return 0x1000; }
 
+  /// stubGroupSize - the default group size to place stubs between sections.
+  virtual unsigned stubGroupSize() const { return 0x10000; }
+
  protected:
   const llvm::Triple& m_Triple;
 };
diff --git a/include/mcld/Target/GNULDBackend.h b/include/mcld/Target/GNULDBackend.h
index 212d081..a396084 100644
--- a/include/mcld/Target/GNULDBackend.h
+++ b/include/mcld/Target/GNULDBackend.h
@@ -189,6 +189,9 @@
   /// Different concrete target backend may overlap this function.
   virtual bool allocateCommonSymbols(Module& pModule);
 
+  /// mergeFlags - update set of ELF header flags
+  virtual void mergeFlags(Input& pInput, const char* ELF_hdr) {}
+
   /// updateSectionFlags - update pTo's flags when merging pFrom
   /// update the output section flags based on input section flags.
   virtual bool updateSectionFlags(LDSection& pTo, const LDSection& pFrom);
@@ -307,11 +310,14 @@
 
   /// maxFwdBranchOffset - return the max forward branch offset of the backend.
   /// Target can override this function if needed.
-  virtual int64_t maxFwdBranchOffset() { return INT64_MAX; }
+  virtual int64_t maxFwdBranchOffset() const { return INT64_MAX; }
 
   /// maxBwdBranchOffset - return the max backward branch offset of the backend.
   /// Target can override this function if needed.
-  virtual int64_t maxBwdBranchOffset() { return 0; }
+  virtual int64_t maxBwdBranchOffset() const { return 0; }
+
+  /// stubGroupSize - return the group size to place stubs between sections.
+  virtual unsigned stubGroupSize() const;
 
   /// checkAndSetHasTextRel - check pSection flag to set HasTextRel
   void checkAndSetHasTextRel(const LDSection& pSection);
diff --git a/include/mcld/TargetOptions.h b/include/mcld/TargetOptions.h
index 23a376b..2c7d973 100644
--- a/include/mcld/TargetOptions.h
+++ b/include/mcld/TargetOptions.h
@@ -58,12 +58,37 @@
   bool is32Bits() const { return (32 == m_BitClass); }
   bool is64Bits() const { return (64 == m_BitClass); }
 
+  // -G, max GP size option
+  void setGPSize(unsigned pGPSize) { m_GPSize = pGPSize; }
+
+  unsigned getGPSize() const { return m_GPSize; }
+
+  void setStubGroupSize(unsigned pSize) { m_StubGroupSize = pSize; }
+
+  unsigned getStubGroupSize() const { return m_StubGroupSize; }
+
+  void setFixCA53Erratum835769(bool pEnable = true) {
+    m_FixCA53Erratum835769 = pEnable;
+  }
+
+  bool fixCA53Erratum835769() const { return m_FixCA53Erratum835769; }
+
+  void setFixCA53Erratum843419(bool pEnable = true) {
+    m_FixCA53Erratum843419 = pEnable;
+  }
+
+  bool fixCA53Erratum843419() const { return m_FixCA53Erratum843419; }
+
  private:
   llvm::Triple m_Triple;
   std::string m_ArchName;
   std::string m_TargetCPU;
   Endian m_Endian;
   unsigned int m_BitClass;
+  unsigned m_GPSize;  // -G, --gpsize
+  unsigned m_StubGroupSize;
+  bool m_FixCA53Erratum835769 : 1;
+  bool m_FixCA53Erratum843419 : 1;
 };
 
 }  // namespace mcld
diff --git a/lib/Core/GeneralOptions.cpp b/lib/Core/GeneralOptions.cpp
index 1996acf..c7d0dde 100644
--- a/lib/Core/GeneralOptions.cpp
+++ b/lib/Core/GeneralOptions.cpp
@@ -61,7 +61,6 @@
       m_bPrintICFSections(false),
       m_ICF(ICF::None),
       m_ICFIterations(2),
-      m_GPSize(8),
       m_StripSymbols(StripSymbolMode::KeepAllSymbols),
       m_HashStyle(HashStyle::SystemV) {
 }
diff --git a/lib/Core/IRBuilder.cpp b/lib/Core/IRBuilder.cpp
index 252fc43..4c6eab8 100644
--- a/lib/Core/IRBuilder.cpp
+++ b/lib/Core/IRBuilder.cpp
@@ -601,6 +601,36 @@
   return relocation;
 }
 
+ResolveInfo* IRBuilder::CreateLocalSymbol(FragmentRef& pFragRef) {
+  // Create and add symbol to the name pool.
+  ResolveInfo* resolveInfo =
+      m_Module.getNamePool().createSymbol(/* pName */"",
+                                          /* pIsDyn */false,
+                                          ResolveInfo::Section,
+                                          ResolveInfo::Define,
+                                          ResolveInfo::Local,
+                                          /* pSize */0,
+                                          ResolveInfo::Hidden);
+  if (resolveInfo == nullptr) {
+    return nullptr;
+  }
+
+  // Create input symbol.
+  LDSymbol* inputSym = LDSymbol::Create(*resolveInfo);
+  if (inputSym == nullptr) {
+    return nullptr;
+  }
+
+  inputSym->setFragmentRef(FragmentRef::Create(*pFragRef.frag(),
+                                               pFragRef.offset()));
+  inputSym->setValue(/* pValue */0);
+
+  // The output symbol is simply an alias to the input symbol.
+  resolveInfo->setSymPtr(inputSym);
+
+  return resolveInfo;
+}
+
 /// AddSymbol - define an output symbol and override it immediately
 template <>
 LDSymbol* IRBuilder::AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>(
diff --git a/lib/Core/TargetOptions.cpp b/lib/Core/TargetOptions.cpp
index c7678ba..e830710 100644
--- a/lib/Core/TargetOptions.cpp
+++ b/lib/Core/TargetOptions.cpp
@@ -13,11 +13,21 @@
 //===----------------------------------------------------------------------===//
 // TargetOptions
 //===----------------------------------------------------------------------===//
-TargetOptions::TargetOptions() : m_Endian(Unknown), m_BitClass(0) {
+TargetOptions::TargetOptions()
+    : m_Endian(Unknown),
+      m_BitClass(0),
+      m_GPSize(8),
+      m_StubGroupSize(0),
+      m_FixCA53Erratum835769(false) {
 }
 
 TargetOptions::TargetOptions(const std::string& pTriple)
-    : m_Triple(pTriple), m_Endian(Unknown), m_BitClass(0) {
+    : m_Triple(pTriple),
+      m_Endian(Unknown),
+      m_BitClass(0),
+      m_GPSize(8),
+      m_StubGroupSize(0),
+      m_FixCA53Erratum835769(false) {
 }
 
 TargetOptions::~TargetOptions() {
diff --git a/lib/Fragment/Stub.cpp b/lib/Fragment/Stub.cpp
index b658ece..53fe776 100644
--- a/lib/Fragment/Stub.cpp
+++ b/lib/Fragment/Stub.cpp
@@ -8,6 +8,13 @@
 //===----------------------------------------------------------------------===//
 #include "mcld/Fragment/Stub.h"
 
+#include "mcld/IRBuilder.h"
+#include "mcld/Fragment/Relocation.h"
+#include "mcld/LD/BranchIsland.h"
+#include "mcld/LD/ResolveInfo.h"
+
+#include <cassert>
+
 namespace mcld {
 
 Stub::Stub() : Fragment(Fragment::Stub), m_pSymInfo(NULL) {
@@ -22,6 +29,49 @@
   m_pSymInfo = pSymInfo;
 }
 
+void Stub::applyFixup(Relocation& pSrcReloc,
+                      IRBuilder& pBuilder,
+                      BranchIsland& pIsland) {
+  // build a name for stub symbol
+  std::string sym_name("__");
+  sym_name.append(pSrcReloc.symInfo()->name())
+          .append("_")
+          .append(name())
+          .append("@")
+          .append(pIsland.name());
+
+  // create LDSymbol for the stub
+  LDSymbol* symbol =
+      pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>(
+          sym_name,
+          ResolveInfo::Function,
+          ResolveInfo::Define,
+          ResolveInfo::Local,
+          size(),
+          initSymValue(),
+          FragmentRef::Create(*this, initSymValue()),
+          ResolveInfo::Default);
+  setSymInfo(symbol->resolveInfo());
+
+  // add relocations of this stub (i.e., set the branch target of the stub)
+  for (fixup_iterator it = fixup_begin(), ie = fixup_end(); it != ie; ++it) {
+    Relocation* reloc =
+        Relocation::Create((*it)->type(),
+                           *(FragmentRef::Create(*this, (*it)->offset())),
+                           (*it)->addend());
+    reloc->setSymInfo(pSrcReloc.symInfo());
+    pIsland.addRelocation(*reloc);
+  }
+}
+
+void Stub::applyFixup(FragmentRef& pSrcFragRef,
+                      IRBuilder& pBuilder,
+                      BranchIsland& pIsland) {
+  // If applying fixups is based on the source FragmentRef, each target stub
+  // probably should override this function.
+  assert(0 && "target stub should override this function");
+}
+
 void Stub::addFixup(DWord pOffset, SWord pAddend, Type pType) {
   assert(pOffset < size());
   m_FixupList.push_back(new Fixup(pOffset, pAddend, pType));
diff --git a/lib/LD/BranchIsland.cpp b/lib/LD/BranchIsland.cpp
index 0405263..e3237df 100644
--- a/lib/LD/BranchIsland.cpp
+++ b/lib/LD/BranchIsland.cpp
@@ -17,9 +17,9 @@
 
 namespace mcld {
 
-//==========================
+//============================================================================//
 // BranchIsland
-
+//============================================================================//
 BranchIsland::BranchIsland(Fragment& pEntryFrag, size_t pMaxSize, size_t pIndex)
     : m_Entry(pEntryFrag),
       m_pExit(pEntryFrag.getNextNode()),
@@ -123,6 +123,30 @@
   return !exist;
 }
 
+void BranchIsland::addStub(Stub& pStub) {
+  bool exist = false;
+  Key key(&pStub, pStub.symInfo()->outSymbol(), 0);
+  m_StubMap.insert(key, exist);
+
+  m_pRear = &pStub;
+  SectionData* sd = m_Entry.getParent();
+
+  // insert alignment fragment
+  // TODO: check if we can reduce this alignment fragment for some cases
+  AlignFragment* align_frag =
+      new AlignFragment(pStub.alignment(), 0x0, 1u, pStub.alignment() - 1);
+  align_frag->setParent(sd);
+  sd->getFragmentList().insert(end(), align_frag);
+  align_frag->setOffset(align_frag->getPrevNode()->getOffset() +
+                        align_frag->getPrevNode()->size());
+
+  // insert stub fragment
+  pStub.setParent(sd);
+  sd->getFragmentList().insert(end(), &pStub);
+  pStub.setOffset(pStub.getPrevNode()->getOffset() +
+                  pStub.getPrevNode()->size());
+}
+
 /// addRelocation - add a relocation into island
 bool BranchIsland::addRelocation(Relocation& pReloc) {
   m_Relocations.push_back(&pReloc);
diff --git a/lib/LD/BranchIslandFactory.cpp b/lib/LD/BranchIslandFactory.cpp
index cace800..bc9f603 100644
--- a/lib/LD/BranchIslandFactory.cpp
+++ b/lib/LD/BranchIslandFactory.cpp
@@ -38,25 +38,27 @@
 /// group - group fragments and create islands when needed
 /// @param pSectionData - the SectionData holds fragments need to be grouped
 void BranchIslandFactory::group(Module& pModule) {
-  /* FIXME: Currently only support relaxing .text section! */
-  LDSection* text = pModule.getSection(".text");
-  if (text != NULL && text->hasSectionData()) {
-    SectionData& sd = *text->getSectionData();
-    uint64_t group_end = m_MaxFwdBranchRange;
-    for (SectionData::iterator it = sd.begin(), ie = sd.end(); it != ie; ++it) {
-      if ((*it).getOffset() + (*it).size() > group_end) {
-        Fragment* frag = (*it).getPrevNode();
-        while (frag != NULL && frag->getKind() == Fragment::Alignment) {
-          frag = frag->getPrevNode();
-        }
-        if (frag != NULL) {
-          produce(*frag);
-          group_end = (*it).getOffset() + m_MaxFwdBranchRange;
+  for (Module::iterator sect = pModule.begin(), sectEnd = pModule.end();
+       sect != sectEnd; ++sect) {
+    if (((*sect)->kind() == LDFileFormat::TEXT) && (*sect)->hasSectionData()) {
+      SectionData& sd = *((*sect)->getSectionData());
+      uint64_t group_end = m_MaxFwdBranchRange;
+      for (SectionData::iterator it = sd.begin(), ie = sd.end(); it != ie;
+           ++it) {
+        if ((*it).getOffset() + (*it).size() > group_end) {
+          Fragment* frag = (*it).getPrevNode();
+          while (frag != NULL && frag->getKind() == Fragment::Alignment) {
+            frag = frag->getPrevNode();
+          }
+          if (frag != NULL) {
+            produce(*frag);
+            group_end = (*it).getOffset() + m_MaxFwdBranchRange;
+          }
         }
       }
+      if (getIslands(sd.back()).first == NULL)
+        produce(sd.back());
     }
-    if (getIslands(sd.back()).first == NULL)
-      produce(sd.back());
   }
 }
 
@@ -78,11 +80,15 @@
   BranchIsland* bwd = NULL;
   for (iterator it = begin(), ie = end(), prev = ie; it != ie;
        prev = it, ++it) {
+    if (pFragment.getParent() != (*it).getParent()) {
+      continue;
+    }
+
     if ((pFragment.getOffset() < (*it).offset()) &&
         ((pFragment.getOffset() + m_MaxFwdBranchRange) >= (*it).offset())) {
       fwd = &*it;
 
-      if (prev != ie) {
+      if ((prev != ie) && (pFragment.getParent() == (*prev).getParent())) {
         int64_t bwd_off = (int64_t)pFragment.getOffset() + m_MaxBwdBranchRange;
         if ((pFragment.getOffset() > (*prev).offset()) &&
             (bwd_off <= (int64_t)(*prev).offset())) {
diff --git a/lib/LD/DiagnosticInfos.cpp b/lib/LD/DiagnosticInfos.cpp
index 080c318..0ba2e9c 100644
--- a/lib/LD/DiagnosticInfos.cpp
+++ b/lib/LD/DiagnosticInfos.cpp
@@ -51,6 +51,7 @@
 #include "mcld/LD/DiagLayouts.inc"  // NOLINT [build/include] [4]
 #include "mcld/LD/DiagGOTPLT.inc"  // NOLINT [build/include] [4]
 #include "mcld/LD/DiagLDScript.inc"  // NOLINT [build/include] [4]
+#include "mcld/LD/DiagMips.inc"  // NOLINT [build/include] [4]
 #undef DIAG
     {0, DiagnosticEngine::None, 0, 0}};
 
@@ -69,6 +70,7 @@
 #include "mcld/LD/DiagLayouts.inc"  // NOLINT [build/include] [4]
 #include "mcld/LD/DiagGOTPLT.inc"  // NOLINT [build/include] [4]
 #include "mcld/LD/DiagLDScript.inc"  // NOLINT [build/include] [4]
+#include "mcld/LD/DiagMips.inc"  // NOLINT [build/include] [4]
 #undef DIAG
     {0, DiagnosticEngine::None, 0, 0}};
 
diff --git a/lib/LD/ELFObjectReader.cpp b/lib/LD/ELFObjectReader.cpp
index aeefd37..bcd031c 100644
--- a/lib/LD/ELFObjectReader.cpp
+++ b/lib/LD/ELFObjectReader.cpp
@@ -100,6 +100,7 @@
   llvm::StringRef region =
       pInput.memArea()->request(pInput.fileOffset(), hdr_size);
   const char* ELF_hdr = region.begin();
+  m_Backend.mergeFlags(pInput, ELF_hdr);
   bool result = m_pELFReader->readSectionHeaders(pInput, ELF_hdr);
   return result;
 }
diff --git a/lib/LD/ELFSegmentFactory.cpp b/lib/LD/ELFSegmentFactory.cpp
index de2d175..678bbfc 100644
--- a/lib/LD/ELFSegmentFactory.cpp
+++ b/lib/LD/ELFSegmentFactory.cpp
@@ -80,6 +80,12 @@
   return back();
 }
 
+ELFSegment* ELFSegmentFactory::insert(iterator pPosition,
+                                      uint32_t pType,
+                                      uint32_t pFlag) {
+  return *(m_Segments.insert(pPosition, ELFSegment::Create(pType, pFlag)));
+}
+
 void ELFSegmentFactory::erase(iterator pSegment) {
   m_Segments.erase(pSegment);
 }
diff --git a/lib/LD/EhFrameReader.cpp b/lib/LD/EhFrameReader.cpp
index 383c1ee..f26a5cd 100644
--- a/lib/LD/EhFrameReader.cpp
+++ b/lib/LD/EhFrameReader.cpp
@@ -197,10 +197,14 @@
     return false;
   }
   // skip the Return Address Register
-  if (cie_end - handler < 1) {
-    return false;
+  if (version == 1) {
+    if (cie_end - handler < 1)
+      return false;
+    ++handler;
+  } else {
+    if (!skip_LEB128(&handler, cie_end))
+      return false;
   }
-  ++handler;
 
   llvm::StringRef augment((const char*)aug_str_front);
 
diff --git a/lib/LD/IdenticalCodeFolding.cpp b/lib/LD/IdenticalCodeFolding.cpp
index 527dccb..5ba0fc1 100644
--- a/lib/LD/IdenticalCodeFolding.cpp
+++ b/lib/LD/IdenticalCodeFolding.cpp
@@ -232,22 +232,21 @@
 
   // Get the static content from relocs.
   if (reloc_sect != NULL && reloc_sect->hasRelocData()) {
-    RelocData::iterator rel, relEnd = reloc_sect->getRelocData()->end();
-    for (rel = reloc_sect->getRelocData()->begin(); rel != relEnd; ++rel) {
+    for (Relocation& rel : *reloc_sect->getRelocData()) {
       llvm::format_object<Relocation::Type,
                           Relocation::Address,
                           Relocation::Address,
                           Relocation::Address> rel_info("%x%llx%llx%llx",
-                                                        rel->type(),
-                                                        rel->symValue(),
-                                                        rel->addend(),
-                                                        rel->place());
+                                                        rel.type(),
+                                                        rel.symValue(),
+                                                        rel.addend(),
+                                                        rel.place());
       char rel_str[48];
       rel_info.print(rel_str, sizeof(rel_str));
       content.append(rel_str);
 
       // Handle the recursive call.
-      LDSymbol* sym = rel->symInfo()->outSymbol();
+      LDSymbol* sym = rel.symInfo()->outSymbol();
       if ((sym->type() == ResolveInfo::Function) && sym->hasFragRef()) {
         LDSection* def = &sym->fragRef()->frag()->getParent()->getSection();
         if (def == sect) {
@@ -255,12 +254,12 @@
         }
       }
 
-      if (!pBackend.isSymbolPreemptible(*rel->symInfo()) && sym->hasFragRef() &&
+      if (!pBackend.isSymbolPreemptible(*rel.symInfo()) && sym->hasFragRef() &&
           (pKeptSections.find(
                &sym->fragRef()->frag()->getParent()->getSection()) !=
            pKeptSections.end())) {
         // Mark this reloc as a variable.
-        variable_relocs.push_back(rel);
+        variable_relocs.push_back(&rel);
       } else {
         // TODO: Support inlining merge sections if possible (target-dependent).
         if ((sym->binding() == ResolveInfo::Local) ||
diff --git a/lib/LD/StubFactory.cpp b/lib/LD/StubFactory.cpp
index 75de0cf..6b8c81c 100644
--- a/lib/LD/StubFactory.cpp
+++ b/lib/LD/StubFactory.cpp
@@ -9,6 +9,7 @@
 #include "mcld/LD/StubFactory.h"
 
 #include "mcld/IRBuilder.h"
+#include "mcld/Fragment/FragmentRef.h"
 #include "mcld/Fragment/Relocation.h"
 #include "mcld/Fragment/Stub.h"
 #include "mcld/LD/BranchIsland.h"
@@ -58,77 +59,71 @@
       stub = islands.second->findStub(prototype, pReloc);
     }
 
-    if (stub != NULL) {
-      // reset the branch target to the stub instead!
-      pReloc.setSymInfo(stub->symInfo());
-    } else {
+    if (stub == NULL) {
       // find if there is such a stub in the forward island.
       stub = islands.first->findStub(prototype, pReloc);
-      if (stub != NULL) {
-        // reset the branch target to the stub instead!
-        pReloc.setSymInfo(stub->symInfo());
-      } else {
+      if (stub == NULL) {
         // create a stub from the prototype
         stub = prototype->clone();
 
-        // build a name for stub symbol
-        std::string name("__");
-        name.append(pReloc.symInfo()->name())
-            .append("_")
-            .append(stub->name())
-            .append("@")
-            .append(islands.first->name());
-
-        // create LDSymbol for the stub
-        LDSymbol* symbol =
-            pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>(
-                name,
-                ResolveInfo::Function,
-                ResolveInfo::Define,
-                ResolveInfo::Local,
-                stub->size(),          // size
-                stub->initSymValue(),  // value
-                FragmentRef::Create(*stub, stub->initSymValue()),
-                ResolveInfo::Default);
-        stub->setSymInfo(symbol->resolveInfo());
-
-        // add relocations of this stub (i.e., set the branch target of the
-        // stub)
-        for (Stub::fixup_iterator it = stub->fixup_begin(),
-                                  ie = stub->fixup_end();
-             it != ie;
-             ++it) {
-          Relocation* reloc =
-              Relocation::Create((*it)->type(),
-                                 *(FragmentRef::Create(*stub, (*it)->offset())),
-                                 (*it)->addend());
-          reloc->setSymInfo(pReloc.symInfo());
-          islands.first->addRelocation(*reloc);
-        }
+        // apply fixups in this new stub
+        stub->applyFixup(pReloc, pBuilder, *islands.first);
 
         // add stub to the forward branch island
         islands.first->addStub(prototype, pReloc, *stub);
-
-        // reset the branch target of the input reloc to this stub instead!
-        pReloc.setSymInfo(stub->symInfo());
       }
     }
   }
   return stub;
 }
 
+Stub* StubFactory::create(FragmentRef& pFragRef,
+                          IRBuilder& pBuilder,
+                          BranchIslandFactory& pBRIslandFactory) {
+  Stub* prototype = findPrototype(pFragRef);
+  if (prototype == NULL) {
+    return NULL;
+  } else {
+    std::pair<BranchIsland*, BranchIsland*> islands =
+        pBRIslandFactory.getIslands(*(pFragRef.frag()));
+    // early exit if we can not find the forward island.
+    if (islands.first == NULL) {
+      return NULL;
+    } else {
+      // create a stub from the prototype
+      Stub* stub = prototype->clone();
+
+      // apply fixups in this new stub
+      stub->applyFixup(pFragRef, pBuilder, *islands.first);
+
+      // add stub to the forward branch island
+      islands.first->addStub(*stub);
+
+      return stub;
+    }  // (islands.first == NULL)
+  }  // if (prototype == NULL)
+}
+
 /// findPrototype - find if there is a registered stub prototype for the given
 /// relocation
 Stub* StubFactory::findPrototype(const Relocation& pReloc,
                                  uint64_t pSource,
-                                 uint64_t pTargetSymValue) {
-  for (StubPoolType::iterator it = m_StubPool.begin(), ie = m_StubPool.end();
-       it != ie;
-       ++it) {
+                                 uint64_t pTargetSymValue) const {
+  for (StubPoolType::const_iterator it = m_StubPool.begin(),
+                                    ie = m_StubPool.end(); it != ie; ++it) {
     if ((*it)->isMyDuty(pReloc, pSource, pTargetSymValue))
       return (*it);
   }
   return NULL;
 }
 
+Stub* StubFactory::findPrototype(const FragmentRef& pFragRef) const {
+  for (StubPoolType::const_iterator it = m_StubPool.begin(),
+                                    ie = m_StubPool.end(); it != ie; ++it) {
+    if ((*it)->isMyDuty(pFragRef))
+      return (*it);
+  }
+  return NULL;
+}
+
 }  // namespace mcld
diff --git a/lib/Object/ObjectLinker.cpp b/lib/Object/ObjectLinker.cpp
index 4d8ffeb..a9813d9 100644
--- a/lib/Object/ObjectLinker.cpp
+++ b/lib/Object/ObjectLinker.cpp
@@ -986,9 +986,8 @@
         break;
     }
   } else {
-    std::memcpy(target_addr,
-                &pReloc.target(),
-                pReloc.size(*m_LDBackend.getRelocator()) / 8);
+    std::memcpy(target_addr, &pReloc.target(),
+                (pReloc.size(*m_LDBackend.getRelocator()) + 7) / 8);
   }
 }
 
diff --git a/lib/Support/Unix/FileSystem.inc b/lib/Support/Unix/FileSystem.inc
index 67fdd73..a429838 100644
--- a/lib/Support/Unix/FileSystem.inc
+++ b/lib/Support/Unix/FileSystem.inc
@@ -15,6 +15,7 @@
 
 #include <dirent.h>
 #include <fcntl.h>
+#include <limits.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
diff --git a/lib/Target/AArch64/AArch64CA53Erratum835769Stub.cpp b/lib/Target/AArch64/AArch64CA53Erratum835769Stub.cpp
new file mode 100644
index 0000000..7d10a1e
--- /dev/null
+++ b/lib/Target/AArch64/AArch64CA53Erratum835769Stub.cpp
@@ -0,0 +1,93 @@
+//===- AArch64CA53Erratum835769Stub.cpp -----------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AArch64CA53Erratum835769Stub.h"
+#include "AArch64InsnHelpers.h"
+
+#include "mcld/Fragment/FragmentRef.h"
+#include "mcld/Fragment/Relocation.h"
+#include "mcld/IRBuilder.h"
+#include "mcld/LD/BranchIsland.h"
+#include "mcld/LD/LDSymbol.h"
+#include "mcld/LD/ResolveInfo.h"
+
+#include <llvm/ADT/StringExtras.h>
+#include <llvm/Support/ELF.h>
+
+#include <cassert>
+
+namespace mcld {
+
+//===----------------------------------------------------------------------===//
+// AArch64CA53Erratum835769Stub
+//===----------------------------------------------------------------------===//
+AArch64CA53Erratum835769Stub::AArch64CA53Erratum835769Stub() {
+}
+
+/// for doClone
+AArch64CA53Erratum835769Stub::AArch64CA53Erratum835769Stub(
+    const uint32_t* pData,
+    size_t pSize,
+    const char* pName,
+    const_fixup_iterator pBegin,
+    const_fixup_iterator pEnd)
+    : AArch64CA53ErratumStub(pData, pSize, pName, pBegin, pEnd) {
+}
+
+AArch64CA53Erratum835769Stub::~AArch64CA53Erratum835769Stub() {
+}
+
+bool AArch64CA53Erratum835769Stub::isMyDuty(const FragmentRef& pFragRef) const {
+  unsigned rt;
+  unsigned rt2;
+  bool is_pair;
+  bool is_load;
+  ErratumSequence code;
+  pFragRef.memcpy(&code, sizeof(ErratumSequence), 0);
+
+  if (AArch64InsnHelpers::isMLXL(code.insns[1]) &&
+      AArch64InsnHelpers::isMemOp(code.insns[0], rt, rt2, is_pair, is_load)) {
+    // Any SIMD memory op is independent of the subsequent MLA by definition of
+    // the erratum.
+    if (AArch64InsnHelpers::getBit(code.insns[0], 26) != 0) {
+      return true;
+    }
+
+    // If not SIMD, check for integer memory ops and MLA relationship.
+    unsigned ra = AArch64InsnHelpers::getRa(code.insns[1]);
+    unsigned rm = AArch64InsnHelpers::getRm(code.insns[1]);
+    unsigned rn = AArch64InsnHelpers::getRn(code.insns[1]);
+
+    // If this is a load and there's a true(RAW) dependency, we are safe and
+    // this is not an erratum sequence.
+    if (is_load &&
+        ((rt == ra) ||
+         (rt == rm) ||
+         (rt == rn) ||
+         (is_pair && ((rt2 == ra) || (rt2 == rm) || (rt2 == rn))))) {
+      return false;
+    }
+
+    // We conservatively put out stubs for all other cases (including
+    // writebacks).
+    return true;
+  }
+
+  return false;
+}
+
+Stub* AArch64CA53Erratum835769Stub::doClone() {
+  return new AArch64CA53Erratum835769Stub(getData(),
+                                          size(),
+                                          "erratum_835769_veneer",
+                                          fixup_begin(),
+                                          fixup_end());
+}
+
+}  // namespace mcld
diff --git a/lib/Target/AArch64/AArch64CA53Erratum835769Stub.h b/lib/Target/AArch64/AArch64CA53Erratum835769Stub.h
new file mode 100644
index 0000000..2adadb4
--- /dev/null
+++ b/lib/Target/AArch64/AArch64CA53Erratum835769Stub.h
@@ -0,0 +1,63 @@
+//===- AArch64CA53Erratum835769Stub.h -------------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TARGET_AARCH64_AARCH64CA53ERRATUM835769STUB_H_
+#define TARGET_AARCH64_AARCH64CA53ERRATUM835769STUB_H_
+
+#include "AArch64CA53ErratumStub.h"
+
+#include "mcld/Support/Compiler.h"
+#include <llvm/Support/DataTypes.h>
+#include <string>
+#include <vector>
+
+namespace mcld {
+
+class FragmentRef;
+
+class AArch64CA53Erratum835769Stub : public AArch64CA53ErratumStub {
+ public:
+  static constexpr unsigned ErratumInsnOffset = 4;
+
+  struct ErratumSequence {
+    unsigned insns[2];
+  };
+
+ public:
+  AArch64CA53Erratum835769Stub();
+
+  ~AArch64CA53Erratum835769Stub();
+
+  bool isMyDuty(const FragmentRef& pFragRef) const;
+
+  unsigned getErratumInsnOffset() const {
+    return ErratumInsnOffset;
+  }
+
+  unsigned getErratumSequenceSize() const {
+    return sizeof(ErratumSequence);
+  }
+
+ private:
+  /// for doClone
+  AArch64CA53Erratum835769Stub(const uint32_t* pData,
+                               size_t pSize,
+                               const char* pName,
+                               const_fixup_iterator pBegin,
+                               const_fixup_iterator pEnd);
+
+  /// doClone
+  Stub* doClone();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AArch64CA53Erratum835769Stub);
+};
+
+}  // namespace mcld
+
+#endif  // TARGET_AARCH64_AARCH64CA53ERRATUM835769STUB_H_
diff --git a/lib/Target/AArch64/AArch64CA53Erratum843419Stub.cpp b/lib/Target/AArch64/AArch64CA53Erratum843419Stub.cpp
new file mode 100644
index 0000000..978c533
--- /dev/null
+++ b/lib/Target/AArch64/AArch64CA53Erratum843419Stub.cpp
@@ -0,0 +1,93 @@
+//===- AArch64CA53Erratum843419Stub.cpp -----------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AArch64CA53Erratum843419Stub.h"
+#include "AArch64InsnHelpers.h"
+
+#include "mcld/Fragment/FragmentRef.h"
+#include "mcld/Fragment/Relocation.h"
+#include "mcld/IRBuilder.h"
+#include "mcld/LD/BranchIsland.h"
+#include "mcld/LD/LDSection.h"
+#include "mcld/LD/LDSymbol.h"
+#include "mcld/LD/ResolveInfo.h"
+#include "mcld/LD/SectionData.h"
+
+#include <llvm/ADT/StringExtras.h>
+#include <llvm/Support/ELF.h>
+
+#include <cassert>
+
+namespace mcld {
+
+//===----------------------------------------------------------------------===//
+// AArch64CA53Erratum843419Stub
+//===----------------------------------------------------------------------===//
+AArch64CA53Erratum843419Stub::AArch64CA53Erratum843419Stub() {
+}
+
+/// for doClone
+AArch64CA53Erratum843419Stub::AArch64CA53Erratum843419Stub(
+    const uint32_t* pData,
+    size_t pSize,
+    const char* pName,
+    const_fixup_iterator pBegin,
+    const_fixup_iterator pEnd)
+    : AArch64CA53ErratumStub(pData, pSize, pName, pBegin, pEnd) {
+}
+
+AArch64CA53Erratum843419Stub::~AArch64CA53Erratum843419Stub() {
+}
+
+bool AArch64CA53Erratum843419Stub::isErratum843419Sequence(unsigned insn1,
+                                                           unsigned insn2,
+                                                           unsigned insn3) {
+  unsigned rt;
+  unsigned rt2;
+  bool is_pair;
+  bool is_load;
+  return AArch64InsnHelpers::isMemOp(insn2, rt, rt2, is_pair, is_load) &&
+         (!is_pair || (is_pair && !is_load)) &&
+         AArch64InsnHelpers::isLDSTUIMM(insn3) &&
+         (AArch64InsnHelpers::getRn(insn3) == AArch64InsnHelpers::getRd(insn1));
+}
+
+bool AArch64CA53Erratum843419Stub::isMyDuty(const FragmentRef& pFragRef) const {
+  if ((pFragRef.offset() + AArch64InsnHelpers::InsnSize * 3) >
+      pFragRef.frag()->size()) {
+    return false;
+  }
+
+  // The first instruction must be ending at 0xFF8 or 0xFFC.
+  const uint64_t vma = pFragRef.frag()->getParent()->getSection().addr() +
+                       pFragRef.getOutputOffset();
+  const unsigned page_offset = (vma & 0xFFF);
+  if ((page_offset != 0xFF8) && (page_offset != 0xFFC)) {
+    return false;
+  }
+
+  ErratumSequence code;
+  pFragRef.memcpy(&code, AArch64InsnHelpers::InsnSize * 3, 0);
+
+  if (isErratum843419Sequence(code.insns[0], code.insns[1], code.insns[2])) {
+    return true;
+  }
+
+  return false;
+}
+
+Stub* AArch64CA53Erratum843419Stub::doClone() {
+  return new AArch64CA53Erratum843419Stub(getData(),
+                                          size(),
+                                          "erratum_843419_veneer",
+                                          fixup_begin(),
+                                          fixup_end());
+}
+
+}  // namespace mcld
diff --git a/lib/Target/AArch64/AArch64CA53Erratum843419Stub.h b/lib/Target/AArch64/AArch64CA53Erratum843419Stub.h
new file mode 100644
index 0000000..4c3b5b6
--- /dev/null
+++ b/lib/Target/AArch64/AArch64CA53Erratum843419Stub.h
@@ -0,0 +1,68 @@
+//===- AArch64CA53Erratum843419Stub.h -------------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TARGET_AARCH64_AARCH64CA53ERRATUM843419STUB_H_
+#define TARGET_AARCH64_AARCH64CA53ERRATUM843419STUB_H_
+
+#include "AArch64CA53ErratumStub.h"
+
+#include "mcld/Support/Compiler.h"
+#include <llvm/Support/DataTypes.h>
+#include <string>
+#include <vector>
+
+namespace mcld {
+
+class FragmentRef;
+
+class AArch64CA53Erratum843419Stub : public AArch64CA53ErratumStub {
+ public:
+  static constexpr unsigned ErratumInsnOffset = 8;
+
+  struct ErratumSequence {
+    unsigned insns[3];
+  };
+
+ public:
+  static bool isErratum843419Sequence(unsigned insn1,
+                                      unsigned insn2,
+                                      unsigned insn3);
+
+ public:
+  AArch64CA53Erratum843419Stub();
+
+  ~AArch64CA53Erratum843419Stub();
+
+  bool isMyDuty(const FragmentRef& pFragRef) const;
+
+  unsigned getErratumInsnOffset() const {
+    return ErratumInsnOffset;
+  }
+
+  unsigned getErratumSequenceSize() const {
+    return sizeof(ErratumSequence);
+  }
+
+ private:
+  /// for doClone
+  AArch64CA53Erratum843419Stub(const uint32_t* pData,
+                               size_t pSize,
+                               const char* pName,
+                               const_fixup_iterator pBegin,
+                               const_fixup_iterator pEnd);
+
+  /// doClone
+  Stub* doClone();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AArch64CA53Erratum843419Stub);
+};
+
+}  // namespace mcld
+
+#endif  // TARGET_AARCH64_AARCH64CA53ERRATUM843419STUB_H_
diff --git a/lib/Target/AArch64/AArch64CA53Erratum843419Stub2.cpp b/lib/Target/AArch64/AArch64CA53Erratum843419Stub2.cpp
new file mode 100644
index 0000000..f498d65
--- /dev/null
+++ b/lib/Target/AArch64/AArch64CA53Erratum843419Stub2.cpp
@@ -0,0 +1,84 @@
+//===- AArch64CA53Erratum843419Stub2.cpp ----------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AArch64CA53Erratum843419Stub.h"
+#include "AArch64CA53Erratum843419Stub2.h"
+#include "AArch64InsnHelpers.h"
+
+#include "mcld/Fragment/FragmentRef.h"
+#include "mcld/Fragment/Relocation.h"
+#include "mcld/IRBuilder.h"
+#include "mcld/LD/BranchIsland.h"
+#include "mcld/LD/LDSection.h"
+#include "mcld/LD/LDSymbol.h"
+#include "mcld/LD/ResolveInfo.h"
+#include "mcld/LD/SectionData.h"
+
+#include <llvm/ADT/StringExtras.h>
+#include <llvm/Support/ELF.h>
+
+#include <cassert>
+
+namespace mcld {
+
+//===----------------------------------------------------------------------===//
+// AArch64CA53Erratum843419Stub2
+//===----------------------------------------------------------------------===//
+AArch64CA53Erratum843419Stub2::AArch64CA53Erratum843419Stub2() {
+}
+
+/// for doClone
+AArch64CA53Erratum843419Stub2::AArch64CA53Erratum843419Stub2(
+    const uint32_t* pData,
+    size_t pSize,
+    const char* pName,
+    const_fixup_iterator pBegin,
+    const_fixup_iterator pEnd)
+    : AArch64CA53ErratumStub(pData, pSize, pName, pBegin, pEnd) {
+}
+
+AArch64CA53Erratum843419Stub2::~AArch64CA53Erratum843419Stub2() {
+}
+
+bool AArch64CA53Erratum843419Stub2::isMyDuty(
+    const FragmentRef& pFragRef) const {
+  if ((pFragRef.offset() + AArch64InsnHelpers::InsnSize * 4) >
+      pFragRef.frag()->size()) {
+    return false;
+  }
+
+  // The first instruction must be ending at 0xFF8 or 0xFFC.
+  const uint64_t vma = pFragRef.frag()->getParent()->getSection().addr() +
+                       pFragRef.getOutputOffset();
+  const unsigned page_offset = (vma & 0xFFF);
+  if ((page_offset != 0xFF8) && (page_offset != 0xFFC)) {
+    return false;
+  }
+
+  ErratumSequence code;
+  pFragRef.memcpy(&code, AArch64InsnHelpers::InsnSize * 4, 0);
+
+  if (AArch64CA53Erratum843419Stub::isErratum843419Sequence(code.insns[0],
+                                                            code.insns[1],
+                                                            code.insns[3])) {
+    return true;
+  }
+
+  return false;
+}
+
+Stub* AArch64CA53Erratum843419Stub2::doClone() {
+  return new AArch64CA53Erratum843419Stub2(getData(),
+                                           size(),
+                                           "erratum_843419_veneer",
+                                           fixup_begin(),
+                                           fixup_end());
+}
+
+}  // namespace mcld
diff --git a/lib/Target/AArch64/AArch64CA53Erratum843419Stub2.h b/lib/Target/AArch64/AArch64CA53Erratum843419Stub2.h
new file mode 100644
index 0000000..53a282b
--- /dev/null
+++ b/lib/Target/AArch64/AArch64CA53Erratum843419Stub2.h
@@ -0,0 +1,63 @@
+//===- AArch64CA53Erratum843419Stub2.h ------------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TARGET_AARCH64_AARCH64CA53ERRATUM843419STUB2_H_
+#define TARGET_AARCH64_AARCH64CA53ERRATUM843419STUB2_H_
+
+#include "AArch64CA53ErratumStub.h"
+
+#include "mcld/Support/Compiler.h"
+#include <llvm/Support/DataTypes.h>
+#include <string>
+#include <vector>
+
+namespace mcld {
+
+class FragmentRef;
+
+class AArch64CA53Erratum843419Stub2 : public AArch64CA53ErratumStub {
+ public:
+  static constexpr unsigned ErratumInsnOffset = 12;
+
+  struct ErratumSequence {
+    unsigned insns[4];
+  };
+
+ public:
+  AArch64CA53Erratum843419Stub2();
+
+  ~AArch64CA53Erratum843419Stub2();
+
+  bool isMyDuty(const FragmentRef& pFragRef) const;
+
+  unsigned getErratumInsnOffset() const {
+    return ErratumInsnOffset;
+  }
+
+  unsigned getErratumSequenceSize() const {
+    return sizeof(ErratumSequence);
+  }
+
+ private:
+  /// for doClone
+  AArch64CA53Erratum843419Stub2(const uint32_t* pData,
+                                size_t pSize,
+                                const char* pName,
+                                const_fixup_iterator pBegin,
+                                const_fixup_iterator pEnd);
+
+  /// doClone
+  Stub* doClone();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AArch64CA53Erratum843419Stub2);
+};
+
+}  // namespace mcld
+
+#endif  // TARGET_AARCH64_AARCH64CA53ERRATUM843419STUB2_H_
diff --git a/lib/Target/AArch64/AArch64CA53ErratumStub.cpp b/lib/Target/AArch64/AArch64CA53ErratumStub.cpp
new file mode 100644
index 0000000..9fdc496
--- /dev/null
+++ b/lib/Target/AArch64/AArch64CA53ErratumStub.cpp
@@ -0,0 +1,149 @@
+//===- AArch64CA53ErratumStub.cpp -----------------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AArch64CA53ErratumStub.h"
+#include "AArch64InsnHelpers.h"
+#include "AArch64LDBackend.h"
+#include "AArch64RelocationHelpers.h"
+#include "AArch64Relocator.h"
+
+#include "mcld/Fragment/FragmentRef.h"
+#include "mcld/Fragment/Relocation.h"
+#include "mcld/IRBuilder.h"
+#include "mcld/LD/BranchIsland.h"
+#include "mcld/LD/LDSymbol.h"
+#include "mcld/LD/ResolveInfo.h"
+
+#include <llvm/ADT/StringExtras.h>
+#include <llvm/Support/ELF.h>
+
+#include <cassert>
+
+namespace mcld {
+
+//===----------------------------------------------------------------------===//
+// AArch64CA53ErratumStub
+//===----------------------------------------------------------------------===//
+const uint32_t AArch64CA53ErratumStub::TEMPLATE[] = {
+  0x00000000,  // Placeholder for erratum insn
+  0x00000000,  // Palceholder for branch instruction
+};
+
+AArch64CA53ErratumStub::AArch64CA53ErratumStub()
+    : m_pData(NULL),
+      m_Name("erratum__prototype"),
+      m_Size(0x0) {
+  m_pData = TEMPLATE;
+  m_Size = sizeof(TEMPLATE);
+  addFixup(0x0, 0, AArch64Relocator::R_AARCH64_REWRITE_INSN);
+  addFixup(0x4, 0, llvm::ELF::R_AARCH64_JUMP26);
+}
+
+/// for doClone
+AArch64CA53ErratumStub::AArch64CA53ErratumStub(const uint32_t* pData,
+                                               size_t pSize,
+                                               const char* pName,
+                                               const_fixup_iterator pBegin,
+                                               const_fixup_iterator pEnd)
+    : m_pData(pData),
+      m_Name(pName),
+      m_Size(pSize) {
+  for (const_fixup_iterator it = pBegin, ie = pEnd; it != ie; ++it) {
+    addFixup(**it);
+  }
+}
+
+AArch64CA53ErratumStub::~AArch64CA53ErratumStub() {
+}
+
+bool AArch64CA53ErratumStub::isMyDuty(const FragmentRef& pFragRef) const {
+  return false;
+}
+
+void AArch64CA53ErratumStub::applyFixup(FragmentRef& pSrcFragRef,
+                                        IRBuilder& pBuilder,
+                                        BranchIsland& pIsland) {
+  assert(isMyDuty(pSrcFragRef));
+
+  // Build stub symbol name.
+  std::string sym_name("__");
+  sym_name.append(name())
+          .append(llvm::utohexstr(pIsland.numOfStubs()))
+          .append("@")
+          .append(pIsland.name());
+
+  // Create LDSymbol for the stub.
+  LDSymbol* stub_sym =
+      pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>(
+          sym_name,
+          ResolveInfo::NoType,
+          ResolveInfo::Define,
+          ResolveInfo::Local,
+          size(),
+          initSymValue(),
+          FragmentRef::Create(*this, initSymValue()),
+          ResolveInfo::Default);
+  setSymInfo(stub_sym->resolveInfo());
+
+  // Create the target symbol of the stub to the next instruction of erratum
+  // pattarn.
+  FragmentRef* target = FragmentRef::Create(*pSrcFragRef.frag(),
+                                            pSrcFragRef.offset() +
+                                                getErratumInsnOffset() +
+                                                AArch64InsnHelpers::InsnSize);
+  ResolveInfo* target_info = pBuilder.CreateLocalSymbol(*target);
+
+  // Apply the fixups.
+  fixup_iterator it = fixup_begin();
+  // Rewrite the first instruction as the erratum instruction.
+  Relocation* reloc =
+      Relocation::Create((*it)->type(),
+                         *(FragmentRef::Create(*this, (*it)->offset())),
+                         (*it)->addend());
+  reloc->setSymInfo(target_info);
+
+  std::unique_ptr<unsigned[]> code(new unsigned[getErratumSequenceSize() / 4]);
+  pSrcFragRef.memcpy(code.get(), getErratumSequenceSize(), 0);
+  reloc->target() =
+      code[getErratumInsnOffset() / AArch64InsnHelpers::InsnSize];
+  pIsland.addRelocation(*reloc);
+
+  // Construct the second instruction as a branch to target.
+  ++it;
+  reloc = Relocation::Create((*it)->type(),
+                             *(FragmentRef::Create(*this, (*it)->offset())),
+                             (*it)->addend());
+  reloc->setSymInfo(target_info);
+  reloc->target() = AArch64InsnHelpers::buildBranchInsn();
+  pIsland.addRelocation(*reloc);
+
+  assert((++it) == fixup_end());
+}
+
+const std::string& AArch64CA53ErratumStub::name() const {
+  return m_Name;
+}
+
+const uint32_t* AArch64CA53ErratumStub::getData() const {
+  return m_pData;
+}
+
+const uint8_t* AArch64CA53ErratumStub::getContent() const {
+  return reinterpret_cast<const uint8_t*>(m_pData);
+}
+
+size_t AArch64CA53ErratumStub::size() const {
+  return m_Size;
+}
+
+size_t AArch64CA53ErratumStub::alignment() const {
+  return 8;
+}
+
+}  // namespace mcld
diff --git a/lib/Target/AArch64/AArch64CA53ErratumStub.h b/lib/Target/AArch64/AArch64CA53ErratumStub.h
new file mode 100644
index 0000000..9c9998e
--- /dev/null
+++ b/lib/Target/AArch64/AArch64CA53ErratumStub.h
@@ -0,0 +1,71 @@
+//===- AArch64CA53ErratumStub.h -------------------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TARGET_AARCH64_AARCH64CA53ERRATUMSTUB_H_
+#define TARGET_AARCH64_AARCH64CA53ERRATUMSTUB_H_
+
+#include "mcld/Fragment/Stub.h"
+#include "mcld/Support/Compiler.h"
+#include <llvm/Support/DataTypes.h>
+#include <string>
+#include <vector>
+
+namespace mcld {
+
+class BranchIsland;
+class FragmentRef;
+class IRBuilder;
+
+class AArch64CA53ErratumStub : public Stub {
+ public:
+  AArch64CA53ErratumStub();
+
+  AArch64CA53ErratumStub(const uint32_t* pData,
+                         size_t pSize,
+                         const char* pName,
+                         const_fixup_iterator pBegin,
+                         const_fixup_iterator pEnd);
+
+  ~AArch64CA53ErratumStub();
+
+  bool isMyDuty(const FragmentRef& pFragRef) const;
+
+  void applyFixup(FragmentRef& pSrcFragRef,
+                  IRBuilder& pBuilder,
+                  BranchIsland& pIsland);
+
+  const std::string& name() const;
+
+  const uint32_t* getData() const;
+
+  const uint8_t* getContent() const;
+
+  size_t size() const;
+
+  size_t alignment() const;
+
+ public:
+  virtual unsigned getErratumSequenceSize() const = 0;
+
+  virtual unsigned getErratumInsnOffset() const = 0;
+
+ private:
+  static const uint32_t TEMPLATE[];
+
+ private:
+  const uint32_t* m_pData;
+  std::string m_Name;
+  size_t m_Size;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AArch64CA53ErratumStub);
+};
+
+}  // namespace mcld
+
+#endif  // TARGET_AARCH64_AARCH64CA53ERRATUMSTUB_H_
diff --git a/lib/Target/AArch64/AArch64GNUInfo.h b/lib/Target/AArch64/AArch64GNUInfo.h
index 491d189..e2a2857 100644
--- a/lib/Target/AArch64/AArch64GNUInfo.h
+++ b/lib/Target/AArch64/AArch64GNUInfo.h
@@ -20,8 +20,6 @@
 
   uint32_t machine() const { return llvm::ELF::EM_AARCH64; }
 
-  uint64_t abiPageSize() const { return 0x10000; }
-
   uint64_t defaultTextSegmentAddr() const { return 0x400000; }
 
   // There are no processor-specific flags so this field shall contain zero.
diff --git a/lib/Target/AArch64/AArch64InsnHelpers.h b/lib/Target/AArch64/AArch64InsnHelpers.h
new file mode 100644
index 0000000..265464f
--- /dev/null
+++ b/lib/Target/AArch64/AArch64InsnHelpers.h
@@ -0,0 +1,278 @@
+//===- AArch64InsnHelpers.h -----------------------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TARGET_AARCH64_AARCH64INSNHELPERS_H_
+#define TARGET_AARCH64_AARCH64INSNHELPERS_H_
+
+#include "mcld/Support/Compiler.h"
+
+namespace mcld {
+
+class AArch64InsnHelpers {
+ public:
+  typedef uint32_t InsnType;
+
+  static constexpr unsigned InsnSize = 4;
+
+  // Zero register encoding - 31.
+  static constexpr unsigned ZR = 31;
+
+  static unsigned getBits(InsnType insn, int pos, int l) {
+    return (insn >> pos) & ((1 << l) - 1);
+  }
+
+  static unsigned getRt(InsnType insn) {
+    return getBits(insn, 0, 5);
+  }
+
+  static unsigned getRt2(InsnType insn) {
+    return getBits(insn, 10, 5);
+  }
+
+  static unsigned getRa(InsnType insn) {
+    return getBits(insn, 10, 5);
+  }
+
+  static unsigned getRd(InsnType insn) {
+    return getBits(insn, 0, 5);
+  }
+
+  static unsigned getRn(InsnType insn) {
+    return getBits(insn, 5, 5);
+  }
+
+  static unsigned getRm(InsnType insn) {
+    return getBits(insn, 16, 5);
+  }
+
+  static unsigned getBit(InsnType insn, int pos) {
+    return getBits(insn, pos, 1);
+  }
+
+  static unsigned getOp31(InsnType insn) {
+    return getBits(insn, 21, 3);
+  }
+
+  // All ld/st ops. See C4-182 of the ARM ARM. The encoding space for LD_PCREL,
+  // LDST_RO, LDST_UI and LDST_UIMM cover prefetch ops.
+  static bool isLD(InsnType insn) {
+    return (getBit(insn, 22) == 1);
+  }
+
+  static bool isLDST(InsnType insn) {
+    return (((insn) & 0x0a000000) == 0x08000000);
+  }
+
+  static bool isLDSTEX(InsnType insn) {
+    return (((insn) & 0x3f000000) == 0x08000000);
+  }
+
+  static bool isLDSTPCREL(InsnType insn) {
+    return (((insn) & 0x3b000000) == 0x18000000);
+  }
+
+  static bool isLDSTNAP(InsnType insn) {
+    return (((insn) & 0x3b800000) == 0x28000000);
+  }
+
+  static bool isLDSTPPI(InsnType insn) {
+    return (((insn) & 0x3b800000) == 0x28800000);
+  }
+
+  static bool isLDSTPO(InsnType insn) {
+    return (((insn) & 0x3b800000) == 0x29000000);
+  }
+
+  static bool isLDSTPPRE(InsnType insn) {
+    return (((insn) & 0x3b800000) == 0x29800000);
+  }
+
+  static bool isLDSTUI(InsnType insn) {
+    return (((insn) & 0x3b200c00) == 0x38000000);
+  }
+
+  static bool isLDSTPIIMM(InsnType insn) {
+    return (((insn) & 0x3b200c00) == 0x38000400);
+  }
+
+  static bool isLDSTU(InsnType insn) {
+    return (((insn) & 0x3b200c00) == 0x38000800);
+  }
+
+  static bool isLDSTPREIMM(InsnType insn) {
+    return (((insn) & 0x3b200c00) == 0x38000c00);
+  }
+
+  static bool isLDSTRO(InsnType insn) {
+    return (((insn) & 0x3b200c00) == 0x38200800);
+  }
+
+  static bool isLDSTUIMM(InsnType insn) {
+    return (((insn) & 0x3b000000) == 0x39000000);
+  }
+
+  static bool isLDSTSIMDM(InsnType insn) {
+    return (((insn) & 0xbfbf0000) == 0x0c000000);
+  }
+
+  static bool isLDSTSIMDMPI(InsnType insn) {
+    return (((insn) & 0xbfa00000) == 0x0c800000);
+  }
+
+  static bool isLDSTSIMDS(InsnType insn) {
+    return (((insn) & 0xbf9f0000) == 0x0d000000);
+  }
+
+  static bool isLDSTSIMDSPI(InsnType insn) {
+    return (((insn) & 0xbf800000) == 0x0d800000);
+  }
+
+  // Return true if INSN is a mac insn.
+  static bool isMAC(InsnType insn) {
+    return (insn & 0xff000000) == 0x9b000000;
+  }
+
+  // Return true if INSN is multiply-accumulate
+  static bool isMLXL(InsnType insn) {
+    unsigned op31 = getOp31(insn);
+    // Exclude MUL instructions which are encoded as a multiple-accumulate with
+    // RA = XZR
+    if (isMAC(insn) &&
+        ((op31 == 0) || (op31 == 1) || (op31 == 5)) &&
+        getRa(insn) != ZR) {
+      return true;
+    }
+    return false;
+  }
+
+  // Classify an INSN if it is indeed a load/store.
+  //
+  // Return true if INSN is a LD/ST instruction otherwise return false. For
+  // scalar LD/ST instructions is_pair is false, rt is returned and rt2 is set
+  // equal to rt. For LD/ST pair instructions is_pair is true, rt and rt2 are
+  // returned.
+  static bool isMemOp(InsnType insn,
+                      unsigned& rt,
+                      unsigned& rt2,
+                      bool& is_pair,
+                      bool& is_load) {
+    // Bail out quickly if INSN doesn't fall into the the load-store encoding
+    // space.
+    if (!isLDST(insn)) {
+      return false;
+    }
+
+    is_pair = false;
+    is_load = false;
+
+    if (isLDSTEX(insn)) {
+      rt = getRt(insn);
+      rt2 = rt;
+      if (getBit(insn, 21) == 1) {
+        is_pair = true;
+        rt2 = getRt2(insn);
+      }
+      is_load = isLD(insn);
+      return true;
+    } else if (isLDSTNAP(insn) ||
+               isLDSTPPI(insn) ||
+               isLDSTPO(insn) ||
+               isLDSTPPRE(insn)) {
+      rt = getRt(insn);
+      rt2 = getRt2(insn);
+      is_pair = true;
+      is_load = isLD(insn);
+    } else if (isLDSTPCREL(insn) ||
+               isLDSTUI(insn) ||
+               isLDSTPIIMM(insn) ||
+               isLDSTU(insn) ||
+               isLDSTPREIMM(insn) ||
+               isLDSTRO(insn) ||
+               isLDSTUIMM(insn)) {
+      rt = getRt(insn);
+      rt2 = rt;
+      unsigned opc = getBits(insn, 22, 2);
+      unsigned v = getBit(insn, 26);
+      unsigned opc_v = opc | (v << 2);
+      if (isLDSTPCREL(insn) ||
+          ((opc_v == 1) ||
+           (opc_v == 2) ||
+           (opc_v == 3) ||
+           (opc_v == 5) ||
+           (opc_v == 7))) {
+        is_load = true;
+      }
+      return true;
+    } else if (isLDSTSIMDM(insn) || isLDSTSIMDMPI(insn)) {
+      unsigned opcode = (insn >> 12) & 0xf;
+      rt = getRt(insn);
+      is_load = (getBit(insn, 22) != 0);
+      switch (opcode) {
+        case 0:
+        case 2: {
+          rt2 = rt + 3;
+          return true;
+        }
+        case 4:
+        case 6: {
+          rt2 = rt + 2;
+          return true;
+        }
+        case 7: {
+          rt2 = rt;
+          return true;
+        }
+        case 8:
+        case 10: {
+          rt2 = rt + 1;
+          return true;
+        }
+        default: {
+          return false;
+        }
+      }  // switch (opcode)
+    } else if (isLDSTSIMDS(insn) || isLDSTSIMDSPI(insn)) {
+      unsigned r = (insn >> 21) & 1;
+      unsigned opcode = (insn >> 13) & 0x7;
+      rt = getRt(insn);
+      is_load = (getBit(insn, 22) != 0);
+      switch (opcode) {
+        case 0:
+        case 2:
+        case 4:
+        case 6: {
+          rt2 = rt + r;
+          return true;
+        }
+        case 1:
+        case 3:
+        case 5:
+        case 7: {
+          rt2 = rt + ((r == 0) ? 2 : 3);
+          return true;
+        }
+        default: {
+          return false;
+        }
+      }  // switch (opcode)
+    }
+
+    return false;
+  }
+
+  static InsnType buildBranchInsn() {
+    return 0x14000000;
+  }
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(AArch64InsnHelpers);
+};
+
+}  // namespace mcld
+
+#endif  // TARGET_AARCH64_AARCH64INSNHELPERS_H_
diff --git a/lib/Target/AArch64/AArch64LDBackend.cpp b/lib/Target/AArch64/AArch64LDBackend.cpp
index 9224275..b899a5c 100644
--- a/lib/Target/AArch64/AArch64LDBackend.cpp
+++ b/lib/Target/AArch64/AArch64LDBackend.cpp
@@ -7,9 +7,14 @@
 //
 //===----------------------------------------------------------------------===//
 #include "AArch64.h"
+#include "AArch64CA53Erratum835769Stub.h"
+#include "AArch64CA53Erratum843419Stub.h"
+#include "AArch64CA53Erratum843419Stub2.h"
 #include "AArch64ELFDynamic.h"
 #include "AArch64GNUInfo.h"
+#include "AArch64InsnHelpers.h"
 #include "AArch64LDBackend.h"
+#include "AArch64LongBranchStub.h"
 #include "AArch64Relocator.h"
 
 #include "mcld/IRBuilder.h"
@@ -281,7 +286,6 @@
     return result;
   }
 
-  // TODO
   return pRegion.size();
 }
 
@@ -304,36 +308,232 @@
   return SHO_UNDEFINED;
 }
 
+void AArch64GNULDBackend::scanErrata(Module& pModule,
+                                     IRBuilder& pBuilder,
+                                     size_t& num_new_stubs,
+                                     size_t& stubs_strlen) {
+  // TODO: Implement AArch64 ErrataStubFactory to create the specific erratum
+  //       stub and simplify the logics.
+  for (Module::iterator sect = pModule.begin(), sectEnd = pModule.end();
+       sect != sectEnd; ++sect) {
+    if (((*sect)->kind() == LDFileFormat::TEXT) && (*sect)->hasSectionData()) {
+      SectionData* sd = (*sect)->getSectionData();
+      for (SectionData::iterator it = sd->begin(), ie = sd->end(); it != ie;
+           ++it) {
+        Fragment* frag = llvm::dyn_cast<RegionFragment>(it);
+        if (frag != NULL) {
+          FragmentRef* frag_ref = FragmentRef::Create(*frag, 0);
+          for (unsigned offset = 0; offset < frag->size();
+               offset += AArch64InsnHelpers::InsnSize) {
+            Stub* stub = getStubFactory()->create(*frag_ref,
+                                                  pBuilder,
+                                                  *getBRIslandFactory());
+            if (stub != NULL) {
+              // A stub symbol should be local
+              assert(stub->symInfo() != NULL && stub->symInfo()->isLocal());
+              const AArch64CA53ErratumStub* erratum_stub =
+                  reinterpret_cast<const AArch64CA53ErratumStub*>(stub);
+              assert(erratum_stub != NULL);
+              // Rewrite the erratum instruction as a branch to the stub.
+              uint64_t offset = frag_ref->offset() +
+                                erratum_stub->getErratumInsnOffset();
+              Relocation* reloc =
+                  Relocation::Create(llvm::ELF::R_AARCH64_JUMP26,
+                                     *(FragmentRef::Create(*frag, offset)),
+                                     /* pAddend */0);
+              reloc->setSymInfo(stub->symInfo());
+              reloc->target() = AArch64InsnHelpers::buildBranchInsn();
+              addExtraRelocation(reloc);
+
+              ++num_new_stubs;
+              stubs_strlen += stub->symInfo()->nameSize() + 1;
+            }
+
+            frag_ref->assign(*frag, offset + AArch64InsnHelpers::InsnSize);
+          }  // for each INSN
+        }
+      }  // for each FRAGMENT
+    }
+  }  // for each TEXT section
+}
+
 bool AArch64GNULDBackend::doRelax(Module& pModule,
                                   IRBuilder& pBuilder,
                                   bool& pFinished) {
-  // TODO
-  return false;
+  assert(getStubFactory() != NULL && getBRIslandFactory() != NULL);
+
+  // Number of new stubs added
+  size_t num_new_stubs = 0;
+  // String lengh to hold new stub symbols
+  size_t stubs_strlen = 0;
+
+  if (config().targets().fixCA53Erratum835769() ||
+      config().targets().fixCA53Erratum843419()) {
+    scanErrata(pModule, pBuilder, num_new_stubs, stubs_strlen);
+  }
+
+  ELFFileFormat* file_format = getOutputFormat();
+  // check branch relocs and create the related stubs if needed
+  Module::obj_iterator input, inEnd = pModule.obj_end();
+  for (input = pModule.obj_begin(); input != inEnd; ++input) {
+    LDContext::sect_iterator rs, rsEnd = (*input)->context()->relocSectEnd();
+    for (rs = (*input)->context()->relocSectBegin(); rs != rsEnd; ++rs) {
+      if (LDFileFormat::Ignore == (*rs)->kind() || !(*rs)->hasRelocData())
+        continue;
+      RelocData::iterator reloc, rEnd = (*rs)->getRelocData()->end();
+      for (reloc = (*rs)->getRelocData()->begin(); reloc != rEnd; ++reloc) {
+        Relocation* relocation = llvm::cast<Relocation>(reloc);
+
+        switch (relocation->type()) {
+          case llvm::ELF::R_AARCH64_CALL26:
+          case llvm::ELF::R_AARCH64_JUMP26: {
+            // calculate the possible symbol value
+            uint64_t sym_value = 0x0;
+            LDSymbol* symbol = relocation->symInfo()->outSymbol();
+            if (symbol->hasFragRef()) {
+              uint64_t value = symbol->fragRef()->getOutputOffset();
+              uint64_t addr =
+                  symbol->fragRef()->frag()->getParent()->getSection().addr();
+              sym_value = addr + value;
+            }
+            if ((relocation->symInfo()->reserved() &
+                 AArch64Relocator::ReservePLT) != 0x0) {
+              // FIXME: we need to find out the address of the specific plt
+              // entry
+              assert(file_format->hasPLT());
+              sym_value = file_format->getPLT().addr();
+            }
+            Stub* stub = getStubFactory()->create(*relocation,  // relocation
+                                                  sym_value,    // symbol value
+                                                  pBuilder,
+                                                  *getBRIslandFactory());
+            if (stub != NULL) {
+              // a stub symbol should be local
+              assert(stub->symInfo() != NULL && stub->symInfo()->isLocal());
+              // reset the branch target of the reloc to this stub instead
+              relocation->setSymInfo(stub->symInfo());
+
+              ++num_new_stubs;
+              stubs_strlen += stub->symInfo()->nameSize() + 1;
+            }
+            break;
+          }
+          default: {
+            break;
+          }
+        }  // end of switch
+      }  // for all relocations
+    }  // for all relocation section
+  }  // for all inputs
+
+  // Find the first fragment w/ invalid offset due to stub insertion.
+  std::vector<Fragment*> invalid_frags;
+  pFinished = true;
+  for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(),
+                                     island_end = getBRIslandFactory()->end();
+       island != island_end;
+       ++island) {
+    if ((*island).size() > stubGroupSize()) {
+      error(diag::err_no_space_to_place_stubs) << stubGroupSize();
+      return false;
+    }
+
+    if ((*island).numOfStubs() == 0) {
+      continue;
+    }
+
+    Fragment* exit = &*(*island).end();
+    if (exit == (*island).begin()->getParent()->end()) {
+      continue;
+    }
+
+    if (((*island).offset() + (*island).size()) > exit->getOffset()) {
+      if (invalid_frags.empty() ||
+          (invalid_frags.back()->getParent() != (*island).getParent())) {
+        invalid_frags.push_back(exit);
+        pFinished = false;
+      }
+      continue;
+    }
+  }
+
+  // Reset the offset of invalid fragments.
+  for (auto it = invalid_frags.begin(), ie = invalid_frags.end(); it != ie;
+       ++it) {
+    Fragment* invalid = *it;
+    while (invalid != NULL) {
+      invalid->setOffset(invalid->getPrevNode()->getOffset() +
+                         invalid->getPrevNode()->size());
+      invalid = invalid->getNextNode();
+    }
+  }
+
+  // Fix up the size of .symtab, .strtab, and TEXT sections
+  if (num_new_stubs == 0) {
+    return false;
+  } else {
+    switch (config().options().getStripSymbolMode()) {
+      case GeneralOptions::StripSymbolMode::StripAllSymbols:
+      case GeneralOptions::StripSymbolMode::StripLocals:
+        break;
+      default: {
+        LDSection& symtab = file_format->getSymTab();
+        LDSection& strtab = file_format->getStrTab();
+
+        symtab.setSize(symtab.size() +
+                       sizeof(llvm::ELF::Elf64_Sym) * num_new_stubs);
+        symtab.setInfo(symtab.getInfo() + num_new_stubs);
+        strtab.setSize(strtab.size() + stubs_strlen);
+      }
+    }  // switch (config().options().getStripSymbolMode())
+
+    SectionData* prev = NULL;
+    for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(),
+                                       island_end = getBRIslandFactory()->end();
+         island != island_end;
+         ++island) {
+      SectionData* sd = (*island).begin()->getParent();
+      if ((*island).numOfStubs() != 0) {
+        if (sd != prev) {
+          sd->getSection().setSize(sd->back().getOffset() + sd->back().size());
+        }
+      }
+      prev = sd;
+    }
+    return true;
+  }  // if (num_new_stubs == 0)
 }
 
 bool AArch64GNULDBackend::initTargetStubs() {
-  // TODO
-  return true;
+  StubFactory* factory = getStubFactory();
+  if (factory != NULL) {
+    factory->addPrototype(new AArch64LongBranchStub(config().isCodeIndep()));
+    if (config().targets().fixCA53Erratum835769()) {
+      factory->addPrototype(new AArch64CA53Erratum835769Stub());
+    }
+    if (config().targets().fixCA53Erratum843419()) {
+      factory->addPrototype(new AArch64CA53Erratum843419Stub());
+      factory->addPrototype(new AArch64CA53Erratum843419Stub2());
+    }
+    return true;
+  }
+  return false;
 }
 
 void AArch64GNULDBackend::doCreateProgramHdrs(Module& pModule) {
-  // TODO
 }
 
 bool AArch64GNULDBackend::finalizeTargetSymbols() {
-  // TODO
   return true;
 }
 
 bool AArch64GNULDBackend::mergeSection(Module& pModule,
                                        const Input& pInput,
                                        LDSection& pSection) {
-  // TODO
   return true;
 }
 
 bool AArch64GNULDBackend::readSection(Input& pInput, SectionData& pSD) {
-  // TODO
   return true;
 }
 
diff --git a/lib/Target/AArch64/AArch64LDBackend.h b/lib/Target/AArch64/AArch64LDBackend.h
index b94d535..a8cd52a 100644
--- a/lib/Target/AArch64/AArch64LDBackend.h
+++ b/lib/Target/AArch64/AArch64LDBackend.h
@@ -26,8 +26,11 @@
 ///
 class AArch64GNULDBackend : public GNULDBackend {
  public:
-  static const int64_t AARCH64_MAX_FWD_BRANCH_OFFSET = (((1 << 25) - 1) << 2);
-  static const int64_t AARCH64_MAX_BWD_BRANCH_OFFSET = (-((1 << 25) << 2));
+  static constexpr int64_t MAX_FWD_BRANCH_OFFSET = (((1 << 25) - 1) << 2);
+  static constexpr int64_t MAX_BWD_BRANCH_OFFSET = (-((1 << 25) << 2));
+
+  static constexpr int64_t MAX_ADRP_IMM = (1 << 20) - 1;
+  static constexpr int64_t MIN_ADRP_IMM = -(1 << 20);
 
  public:
   AArch64GNULDBackend(const LinkerConfig& pConfig, GNUInfo* pInfo);
@@ -113,8 +116,13 @@
  private:
   void defineGOTSymbol(IRBuilder& pBuilder);
 
-  int64_t maxFwdBranchOffset() { return AARCH64_MAX_FWD_BRANCH_OFFSET; }
-  int64_t maxBwdBranchOffset() { return AARCH64_MAX_BWD_BRANCH_OFFSET; }
+  int64_t maxFwdBranchOffset() const { return MAX_FWD_BRANCH_OFFSET; }
+  int64_t maxBwdBranchOffset() const { return MAX_BWD_BRANCH_OFFSET; }
+
+  void scanErrata(Module& pModule,
+                  IRBuilder& pBuilder,
+                  size_t& num_new_stubs,
+                  size_t& stubs_strlen);
 
   /// mayRelax - Backends should override this function if they need relaxation
   bool mayRelax() { return true; }
diff --git a/lib/Target/AArch64/AArch64LongBranchStub.cpp b/lib/Target/AArch64/AArch64LongBranchStub.cpp
new file mode 100644
index 0000000..479abca
--- /dev/null
+++ b/lib/Target/AArch64/AArch64LongBranchStub.cpp
@@ -0,0 +1,145 @@
+//===- AArch64LongBranchStub.cpp ------------------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AArch64LongBranchStub.h"
+#include "AArch64LDBackend.h"
+#include "AArch64RelocationHelpers.h"
+
+#include "mcld/Fragment/Relocation.h"
+#include "mcld/LD/BranchIsland.h"
+#include "mcld/LD/LDSymbol.h"
+#include "mcld/LD/ResolveInfo.h"
+
+#include <llvm/Support/ELF.h>
+
+#include <cassert>
+
+namespace mcld {
+
+//===----------------------------------------------------------------------===//
+// AArch64LongBranchStub
+//===----------------------------------------------------------------------===//
+const uint32_t AArch64LongBranchStub::PIC_TEMPLATE[] = {
+  0x58000090,  // ldr   ip0, 0x10
+  0x10000011,  // adr   ip1, #0
+  0x8b110210,  // add   ip0, ip0, ip1
+  0xd61f0200,  // br    ip0
+  0x00000000,  // .xword <-- R_AARCH64_PREL64(X+12)
+  0x00000000,
+};
+
+const uint32_t AArch64LongBranchStub::TEMPLATE[] = {
+  0x58000050,  // ldr   ip0, 0x8
+  0xd61f0200,  // br    ip0
+  0x00000000,  // .xword <-- R_AARCH64_PREL64(X)
+  0x00000000,
+};
+
+const uint32_t AArch64LongBranchStub::ADRP_TEMPLATE[] = {
+  0x90000010,  // adrp  ip0, X <-- R_AARCH64_ADR_PREL_PG_HI21(X)
+  0x91000210,  // add   ip0, ip0, :lo12:X <-- R_AARCH64_ADD_ABS_LO12_NC(X)
+  0xd61f0200,  // br    ip0
+};
+
+AArch64LongBranchStub::AArch64LongBranchStub(bool pIsOutputPIC)
+    : m_pData(NULL),
+      m_Name("ljmp_prototype"),
+      m_Size(0x0) {
+  if (pIsOutputPIC) {
+    m_pData = PIC_TEMPLATE;
+    m_Size = sizeof(PIC_TEMPLATE);
+    addFixup(0x10, 12, llvm::ELF::R_AARCH64_PREL64);
+  } else {
+    m_pData = TEMPLATE;
+    m_Size = sizeof(TEMPLATE);
+    addFixup(0x8, 0, llvm::ELF::R_AARCH64_PREL64);
+  }
+}
+
+/// for doClone
+AArch64LongBranchStub::AArch64LongBranchStub(const uint32_t* pData,
+                                             size_t pSize,
+                                             const_fixup_iterator pBegin,
+                                             const_fixup_iterator pEnd)
+    : m_pData(pData),
+      m_Name("ljmp_veneer"),
+      m_Size(pSize) {
+  for (const_fixup_iterator it = pBegin, ie = pEnd; it != ie; ++it) {
+    addFixup(**it);
+  }
+}
+
+AArch64LongBranchStub::~AArch64LongBranchStub() {
+}
+
+bool AArch64LongBranchStub::isMyDuty(const Relocation& pReloc,
+                                     uint64_t pSource,
+                                     uint64_t pTargetSymValue) const {
+  assert((pReloc.type() == llvm::ELF::R_AARCH64_CALL26) ||
+         (pReloc.type() == llvm::ELF::R_AARCH64_JUMP26));
+  int64_t dest = pTargetSymValue + pReloc.addend();
+  int64_t branch_offset = dest - pSource;
+  if ((branch_offset > AArch64GNULDBackend::MAX_FWD_BRANCH_OFFSET) ||
+      (branch_offset < AArch64GNULDBackend::MAX_BWD_BRANCH_OFFSET)) {
+    return true;
+  }
+  return false;
+}
+
+static bool isValidForADRP(uint64_t pSource, uint64_t pDest) {
+  int64_t imm = static_cast<int64_t>((helper_get_page_address(pDest) -
+                                      helper_get_page_address(pSource))) >> 12;
+  return ((imm <= AArch64GNULDBackend::MAX_ADRP_IMM) &&
+          (imm >= AArch64GNULDBackend::MIN_ADRP_IMM));
+}
+
+void AArch64LongBranchStub::applyFixup(Relocation& pSrcReloc,
+                                       IRBuilder& pBuilder,
+                                       BranchIsland& pIsland) {
+  // Try to relax the stub itself.
+  LDSymbol* symbol = pSrcReloc.symInfo()->outSymbol();
+  uint64_t dest = symbol->fragRef()->frag()->getParent()->getSection().addr() +
+                  symbol->fragRef()->getOutputOffset();
+  uint64_t src = pIsland.getParent()->getSection().addr() +
+                 pIsland.offset() +
+                 pIsland.size();
+  if (isValidForADRP(src, dest)) {
+    m_pData = ADRP_TEMPLATE;
+    m_Name = "adrp_veneer";
+    m_Size = sizeof(ADRP_TEMPLATE);
+
+    getFixupList().clear();
+    addFixup(0x0, 0, llvm::ELF::R_AARCH64_ADR_PREL_PG_HI21);
+    addFixup(0x4, 0, llvm::ELF::R_AARCH64_ADD_ABS_LO12_NC);
+  }
+
+  Stub::applyFixup(pSrcReloc, pBuilder, pIsland);
+}
+
+const std::string& AArch64LongBranchStub::name() const {
+  return m_Name;
+}
+
+const uint8_t* AArch64LongBranchStub::getContent() const {
+  return reinterpret_cast<const uint8_t*>(m_pData);
+}
+
+size_t AArch64LongBranchStub::size() const {
+  return m_Size;
+}
+
+size_t AArch64LongBranchStub::alignment() const {
+  return 8;
+}
+
+Stub* AArch64LongBranchStub::doClone() {
+  return new AArch64LongBranchStub(m_pData, m_Size, fixup_begin(), fixup_end());
+}
+
+}  // namespace mcld
diff --git a/lib/Target/AArch64/AArch64LongBranchStub.h b/lib/Target/AArch64/AArch64LongBranchStub.h
new file mode 100644
index 0000000..d996040
--- /dev/null
+++ b/lib/Target/AArch64/AArch64LongBranchStub.h
@@ -0,0 +1,70 @@
+//===- AArch64LongBranchStub.h --------------------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TARGET_AARCH64_AARCH64LONGBRANCHSTUB_H_
+#define TARGET_AARCH64_AARCH64LONGBRANCHSTUB_H_
+
+#include "mcld/Fragment/Stub.h"
+#include "mcld/Support/Compiler.h"
+#include <llvm/Support/DataTypes.h>
+#include <string>
+#include <vector>
+
+namespace mcld {
+
+class BranchIsland;
+class IRBuilder;
+class Relocation;
+
+class AArch64LongBranchStub : public Stub {
+ public:
+  explicit AArch64LongBranchStub(bool pIsOutputPIC);
+
+  ~AArch64LongBranchStub();
+
+  bool isMyDuty(const Relocation& pReloc,
+                uint64_t pSource,
+                uint64_t pTargetSymValue) const;
+
+  void applyFixup(Relocation& pSrcReloc,
+                  IRBuilder& pBuilder,
+                  BranchIsland& pIsland);
+
+  const std::string& name() const;
+
+  const uint8_t* getContent() const;
+
+  size_t size() const;
+
+  size_t alignment() const;
+
+ private:
+  /// for doClone
+  AArch64LongBranchStub(const uint32_t* pData,
+                        size_t pSize,
+                        const_fixup_iterator pBegin,
+                        const_fixup_iterator pEnd);
+
+  /// doClone
+  Stub* doClone();
+
+ private:
+  static const uint32_t PIC_TEMPLATE[];
+  static const uint32_t TEMPLATE[];
+  static const uint32_t ADRP_TEMPLATE[];
+  const uint32_t* m_pData;
+  std::string m_Name;
+  size_t m_Size;
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(AArch64LongBranchStub);
+};
+
+}  // namespace mcld
+
+#endif  // TARGET_AARCH64_AARCH64LONGBRANCHSTUB_H_
diff --git a/lib/Target/AArch64/AArch64RelocationFunctions.h b/lib/Target/AArch64/AArch64RelocationFunctions.h
index 2ed555d..668cc14 100644
--- a/lib/Target/AArch64/AArch64RelocationFunctions.h
+++ b/lib/Target/AArch64/AArch64RelocationFunctions.h
@@ -29,6 +29,7 @@
 
 #define DECL_AARCH64_APPLY_RELOC_FUNC_PTRS(ValueType, MappedType)                              /* NOLINT */\
   ValueType(0x0,   MappedType(&none,             "R_AARCH64_NULL",                        0)), /* NOLINT */\
+  ValueType(0x1,   MappedType(&none,             "R_AARCH64_REWRITE_INSN",               32)), /* NOLINT */\
   ValueType(0x100, MappedType(&none,             "R_AARCH64_NONE",                        0)), /* NOLINT */\
   ValueType(0x101, MappedType(&abs,              "R_AARCH64_ABS64",                      64)), /* NOLINT */\
   ValueType(0x102, MappedType(&abs,              "R_AARCH64_ABS32",                      32)), /* NOLINT */\
diff --git a/lib/Target/AArch64/AArch64Relocator.cpp b/lib/Target/AArch64/AArch64Relocator.cpp
index e59c112..9632f02 100644
--- a/lib/Target/AArch64/AArch64Relocator.cpp
+++ b/lib/Target/AArch64/AArch64Relocator.cpp
@@ -71,8 +71,10 @@
 
 Relocator::Result AArch64Relocator::applyRelocation(Relocation& pRelocation) {
   Relocation::Type type = pRelocation.type();
-  // valid types are 0x0, 0x100-0x239
-  if ((type < 0x100 || type > 0x239) && (type != 0x0)) {
+  // valid types are 0x0, 0x100-1032, and R_AARCH64_REWRITE_INSN
+  if ((type < 0x100 || type > 1032) &&
+      (type != 0x0) &&
+      (type != R_AARCH64_REWRITE_INSN)) {
     return Relocator::Unknown;
   }
   assert(ApplyFunctions.find(type) != ApplyFunctions.end());
@@ -399,6 +401,31 @@
     issueUndefRef(pReloc, pSection, pInput);
 }
 
+bool
+AArch64Relocator::mayHaveFunctionPointerAccess(const Relocation& pReloc) const {
+  switch (pReloc.type()) {
+    case llvm::ELF::R_AARCH64_ADR_PREL_PG_HI21:
+    case llvm::ELF::R_AARCH64_ADR_PREL_PG_HI21_NC:
+    case llvm::ELF::R_AARCH64_ADD_ABS_LO12_NC:
+    case llvm::ELF::R_AARCH64_ADR_GOT_PAGE:
+    case llvm::ELF::R_AARCH64_LD64_GOT_LO12_NC: {
+      return true;
+    }
+    default: {
+      if (pReloc.symInfo()->isLocal()) {
+        // Do not fold any local symbols if building a shared object.
+        return (config().codeGenType() == LinkerConfig::DynObj);
+      } else {
+        // Do not fold any none global defualt symbols if building a shared
+        // object.
+        return ((config().codeGenType() == LinkerConfig::DynObj) &&
+                (pReloc.symInfo()->visibility() != ResolveInfo::Default));
+      }
+    }
+  }
+  return false;
+}
+
 uint32_t AArch64Relocator::getDebugStringOffset(Relocation& pReloc) const {
   if (pReloc.type() != llvm::ELF::R_AARCH64_ABS32)
     error(diag::unsupport_reloc_for_debug_string)
diff --git a/lib/Target/AArch64/AArch64Relocator.h b/lib/Target/AArch64/AArch64Relocator.h
index 3de682c..f9641c0 100644
--- a/lib/Target/AArch64/AArch64Relocator.h
+++ b/lib/Target/AArch64/AArch64Relocator.h
@@ -58,6 +58,11 @@
    */
   enum EntryValue { Default = 0, SymVal = 1 };
 
+  enum {
+    // mcld internal relocation to rewrite an instruction.
+    R_AARCH64_REWRITE_INSN = 1,
+  };
+
  public:
   AArch64Relocator(AArch64GNULDBackend& pParent, const LinkerConfig& pConfig);
   ~AArch64Relocator();
@@ -96,6 +101,10 @@
                       LDSection& pSection,
                       Input& pInput);
 
+  /// mayHaveFunctionPointerAccess - check if the given reloc would possibly
+  /// access a function pointer.
+  virtual bool mayHaveFunctionPointerAccess(const Relocation& pReloc) const;
+
   /// getDebugStringOffset - get the offset from the relocation target. This is
   /// used to get the debug string offset.
   uint32_t getDebugStringOffset(Relocation& pReloc) const;
diff --git a/lib/Target/AArch64/Android.mk b/lib/Target/AArch64/Android.mk
index a143c53..3abc1be 100644
--- a/lib/Target/AArch64/Android.mk
+++ b/lib/Target/AArch64/Android.mk
@@ -1,11 +1,16 @@
 LOCAL_PATH:= $(call my-dir)
 
 mcld_aarch64_target_SRC_FILES := \
+  AArch64CA53Erratum835769Stub.cpp \
+  AArch64CA53Erratum843419Stub2.cpp \
+  AArch64CA53Erratum843419Stub.cpp \
+  AArch64CA53ErratumStub.cpp \
   AArch64Diagnostic.cpp \
   AArch64ELFDynamic.cpp \
   AArch64Emulation.cpp \
   AArch64GOT.cpp \
   AArch64LDBackend.cpp \
+  AArch64LongBranchStub.cpp \
   AArch64PLT.cpp \
   AArch64Relocator.cpp
 
diff --git a/lib/Target/ARM/ARMException.cpp b/lib/Target/ARM/ARMException.cpp
index 3893190..4e354ed 100644
--- a/lib/Target/ARM/ARMException.cpp
+++ b/lib/Target/ARM/ARMException.cpp
@@ -11,7 +11,6 @@
 
 #include "ARMLDBackend.h"
 
-#include "mcld/ADT/ilist_sort.h"
 #include "mcld/Fragment/RegionFragment.h"
 #include "mcld/LD/ELFFileFormat.h"
 #include "mcld/LD/LDContext.h"
@@ -19,17 +18,21 @@
 
 #include <memory>
 
-static const char g_CantUnwindEntry[8] = {
-  // Relocation to text section.
-  0, 0, 0, 0,
-  // EXIDX_CANTUNWIND (little endian.)
-  1, 0, 0, 0,
-};
-
 namespace mcld {
 
+static RegionFragment* findRegionFragment(LDSection& pSection) {
+  SectionData* sectData = pSection.getSectionData();
+  for (SectionData::iterator it = sectData->begin(),
+                             end = sectData->end(); it != end; ++it) {
+    if (it->getKind() == Fragment::Region) {
+      return static_cast<RegionFragment*>(&*it);
+    }
+  }
+  return NULL;
+}
+
 void ARMExData::addInputMap(Input* pInput,
-                            std::unique_ptr<ARMInputExMap>&& pExMap) {
+                            std::unique_ptr<ARMInputExMap> pExMap) {
   assert(m_Inputs.find(pInput) == m_Inputs.end() &&
          "multiple maps for an input");
 
@@ -46,27 +49,17 @@
   }
 }
 
-void ARMGNULDBackend::scanInputExceptionSections(Module& pModule) {
+std::unique_ptr<ARMExData> ARMExData::create(Module& pModule) {
+  std::unique_ptr<ARMExData> exData(new ARMExData());
   for (Module::obj_iterator it = pModule.obj_begin(),
                             end = pModule.obj_end(); it != end; ++it) {
     Input* input = *it;
-    scanInputExceptionSections(pModule, *input);
+    exData->addInputMap(input, ARMInputExMap::create(*input));
   }
+  return exData;
 }
 
-static RegionFragment* findRegionFragment(LDSection& pSection) {
-  SectionData* sectData = pSection.getSectionData();
-  for (SectionData::iterator it = sectData->begin(),
-                             end = sectData->end(); it != end; ++it) {
-    if (it->getKind() == Fragment::Region) {
-      return static_cast<RegionFragment*>(&*it);
-    }
-  }
-  return NULL;
-}
-
-void ARMGNULDBackend::scanInputExceptionSections(Module& pModule,
-                                                 Input& pInput) {
+std::unique_ptr<ARMInputExMap> ARMInputExMap::create(Input& pInput) {
   std::unique_ptr<ARMInputExMap> exMap(new ARMInputExMap());
 
   // Scan the input and collect all related sections.
@@ -74,21 +67,13 @@
   for (LDContext::sect_iterator it = ctx->sectBegin(),
                                 end = ctx->sectEnd(); it != end; ++it) {
     LDSection* sect = *it;
-    llvm::StringRef name(sect->name());
-
-    if (name.startswith(".ARM.exidx")) {
-      ARMExSectionTuple* exTuple = exMap->getOrCreateByExSection(name);
+    if (sect->type() == llvm::ELF::SHT_ARM_EXIDX) {
+      ARMExSectionTuple* exTuple = exMap->getOrCreateByExSection(*sect);
       exTuple->setExIdxSection(sect);
       exTuple->setTextSection(sect->getLink());
-    } else if (name.startswith(".ARM.extab")) {
-      ARMExSectionTuple* exTuple = exMap->getOrCreateByExSection(name);
-      exTuple->setExTabSection(sect);
-    } else if (name.startswith(".rel.ARM.exidx")) {
-      ARMExSectionTuple* exTuple = exMap->getOrCreateByRelExSection(name);
-      exTuple->setRelExIdxSection(sect);
-    } else if (name.startswith(".rel.ARM.extab")) {
-      ARMExSectionTuple* exTuple = exMap->getOrCreateByRelExSection(name);
-      exTuple->setRelExIdxSection(sect);
+      if (sect->getLink() == NULL) {
+        fatal(diag::eh_missing_text_section) << sect->name() << pInput.name();
+      }
     }
   }
 
@@ -100,225 +85,38 @@
     ARMExSectionTuple* exTuple = it->second.get();
     LDSection* const text = exTuple->getTextSection();
     LDSection* const exIdx = exTuple->getExIdxSection();
-    LDSection* const exTab = exTuple->getExTabSection();
-    LDSection* const relExIdx = exTuple->getRelExIdxSection();
-    LDSection* const relExTab = exTuple->getRelExTabSection();
-
-    // Check the .ARM.exidx section.
-    if (!exIdx) {
-      if (exTab) {
-        fatal(diag::eh_missing_exidx_section) << exTab->name() << pInput.name();
-      } else if (relExIdx) {
-        fatal(diag::eh_missing_exidx_section) << relExIdx->name()
-                                              << pInput.name();
-      } else if (relExTab) {
-        fatal(diag::eh_missing_exidx_section) << relExTab->name()
-                                              << pInput.name();
-      } else {
-        llvm_unreachable("unexpected bad exception tuple");
-      }
-    }
-
-    // Check the text section.
-    if (!text) {
-      fatal(diag::eh_missing_text_section) << exIdx->name() << pInput.name();
-    }
 
     // Ignore the exception section if the text section is ignored.
     if ((text->kind() == LDFileFormat::Ignore) ||
         (text->kind() == LDFileFormat::Folded)) {
       // Set the related exception sections as LDFileFormat::Ignore.
       exIdx->setKind(LDFileFormat::Ignore);
-      if (exTab) {
-        exTab->setKind(LDFileFormat::Ignore);
-      }
       // Remove this tuple from the input exception map.
-      exMap->erase(it++);
+      ARMInputExMap::iterator deadIt = it++;
+      exMap->erase(deadIt);
       continue;
     }
 
     // Get RegionFragment from ".text", ".ARM.exidx", and ".ARM.extab" sections.
     RegionFragment* textFrag = findRegionFragment(*text);
     RegionFragment* exIdxFrag = findRegionFragment(*exIdx);
-    RegionFragment* exTabFrag = exTab ? findRegionFragment(*exTab) : NULL;
 
     exTuple->setTextFragment(textFrag);
     exTuple->setExIdxFragment(exIdxFrag);
-    exTuple->setExTabFragment(exTabFrag);
-
-    // Get the RelocData from ".rel.ARM.exidx" and ".rel.ARM.extab" sections.
-    RelocData* exIdxRD = relExIdx ? relExIdx->getRelocData() : NULL;
-    RelocData* exTabRD = relExTab ? relExTab->getRelocData() : NULL;
-
-    exTuple->setExIdxRelocData(exIdxRD);
-    exTuple->setExTabRelocData(exTabRD);
 
     // If there is no region fragment in the .ARM.extab section, then we can
     // skip this tuple.
-    if (!exIdxFrag) {
-      exMap->erase(it++);
+    if (exIdxFrag == NULL) {
+      ARMInputExMap::iterator deadIt = it++;
+      exMap->erase(deadIt);
       continue;
     }
 
-    // TODO: Sort the RelocData w.r.t. the fixup offset.
-
     // Check next tuple
     ++it;
   }
 
-  // Add input map
-  m_ExData.addInputMap(&pInput, std::move(exMap));
-}
-
-class ExIdxFragmentComparator {
- private:
-  const ARMExData& m_ExData;
-
- public:
-  explicit ExIdxFragmentComparator(const ARMExData& pExData)
-      : m_ExData(pExData) {
-  }
-
-  bool operator()(const Fragment& a, const Fragment& b) {
-    ARMExSectionTuple* tupleA = m_ExData.getTupleByExIdx(&a);
-    ARMExSectionTuple* tupleB = m_ExData.getTupleByExIdx(&b);
-
-    Fragment* textFragA = tupleA->getTextFragment();
-    Fragment* textFragB = tupleB->getTextFragment();
-
-    uint64_t addrA = textFragA->getParent()->getSection().addr() +
-                     textFragA->getOffset();
-    uint64_t addrB = textFragB->getParent()->getSection().addr() +
-                     textFragB->getOffset();
-    return (addrA < addrB);
-  }
-};
-
-static mcld::ResolveInfo*
-CreateLocalSymbolToFragmentEnd(mcld::Module& pModule, mcld::Fragment& pFrag) {
-  // Create and add symbol to the name pool.
-  mcld::ResolveInfo* resolveInfo =
-      pModule.getNamePool().createSymbol(/* pName */"",
-                                         /* pIsDyn */false,
-                                         mcld::ResolveInfo::Section,
-                                         mcld::ResolveInfo::Define,
-                                         mcld::ResolveInfo::Local,
-                                         /* pSize */0,
-                                         mcld::ResolveInfo::Hidden);
-  if (resolveInfo == nullptr) {
-    return nullptr;
-  }
-
-  // Create input symbol.
-  mcld::LDSymbol* inputSym = mcld::LDSymbol::Create(*resolveInfo);
-  if (inputSym == nullptr) {
-    return nullptr;
-  }
-
-  inputSym->setFragmentRef(mcld::FragmentRef::Create(pFrag, pFrag.size()));
-  inputSym->setValue(/* pValue */0);
-
-  // The output symbol is simply an alias to the input symbol.
-  resolveInfo->setSymPtr(inputSym);
-
-  return resolveInfo;
-}
-
-void ARMGNULDBackend::rewriteARMExIdxSection(Module& pModule) {
-  if (!m_pEXIDX->hasSectionData()) {
-    // Return if this is empty section.
-    return;
-  }
-
-  SectionData* sectData = m_pEXIDX->getSectionData();
-  SectionData::FragmentListType& list = sectData->getFragmentList();
-
-  // Move the first fragment (align fragment) and last fragment (null fragment)
-  // to temporary list because we would only like to sort the region fragment.
-  SectionData::FragmentListType tmp;
-  {
-    SectionData::iterator first = sectData->begin();
-    SectionData::iterator last = sectData->end();
-    --last;
-
-    assert(first->getKind() == Fragment::Alignment);
-    assert(last->getKind() == Fragment::Null);
-
-    tmp.splice(tmp.end(), list, first);
-    tmp.splice(tmp.end(), list, last);
-  }
-
-  // Sort the region fragments in the .ARM.exidx output section.
-  sort(list, ExIdxFragmentComparator(m_ExData));
-
-  // Fix the coverage of the .ARM.exidx table.
-  llvm::StringRef cantUnwindRegion(g_CantUnwindEntry,
-                                   sizeof(g_CantUnwindEntry));
-
-  SectionData::FragmentListType::iterator it = list.begin();
-  if (it != list.end()) {
-    Fragment* prevTextFrag = m_ExData.getTupleByExIdx(it)->getTextFragment();
-    uint64_t prevTextEnd = prevTextFrag->getParent()->getSection().addr() +
-                           prevTextFrag->getOffset() +
-                           prevTextFrag->size();
-    ++it;
-    while (it != list.end()) {
-      Fragment* currTextFrag = m_ExData.getTupleByExIdx(it)->getTextFragment();
-      uint64_t currTextBegin = currTextFrag->getParent()->getSection().addr() +
-                               currTextFrag->getOffset();
-
-      if (currTextBegin > prevTextEnd) {
-        // Found a gap. Insert a can't unwind entry.
-        RegionFragment* frag = new RegionFragment(cantUnwindRegion, nullptr);
-        frag->setParent(sectData);
-        list.insert(it, frag);
-
-        // Add PREL31 reference to the beginning of the uncovered region.
-        Relocation* reloc =
-            Relocation::Create(static_cast<uint32_t>(llvm::ELF::R_ARM_PREL31),
-                               *FragmentRef::Create(*frag, /* pOffset */0),
-                               /* pAddend */0);
-        reloc->setSymInfo(
-            CreateLocalSymbolToFragmentEnd(pModule, *prevTextFrag));
-        addExtraRelocation(reloc);
-      }
-
-      prevTextEnd = currTextBegin + currTextFrag->size();
-      prevTextFrag = currTextFrag;
-      ++it;
-    }
-
-    // Add a can't unwind entry to terminate .ARM.exidx section.
-    RegionFragment* frag = new RegionFragment(cantUnwindRegion, nullptr);
-    frag->setParent(sectData);
-    list.push_back(frag);
-
-    // Add PREL31 reference to the end of the .text section.
-    Relocation* reloc =
-        Relocation::Create(static_cast<uint32_t>(llvm::ELF::R_ARM_PREL31),
-                           *FragmentRef::Create(*frag, /* pOffset */0),
-                           /* pAddend */0);
-    reloc->setSymInfo(CreateLocalSymbolToFragmentEnd(pModule, *prevTextFrag));
-    addExtraRelocation(reloc);
-  }
-
-  // Add the first and the last fragment back.
-  list.splice(list.begin(), tmp, tmp.begin());
-  list.splice(list.end(), tmp, tmp.begin());
-
-  // Update the fragment offsets.
-  uint64_t offset = 0;
-  for (SectionData::iterator it = sectData->begin(), end = sectData->end();
-       it != end; ++it) {
-    it->setOffset(offset);
-    offset += it->size();
-  }
-
-  // Update the section size.
-  m_pEXIDX->setSize(offset);
-
-  // Rebuild the section header.
-  setOutputSectionAddress(pModule);
+  return exMap;
 }
 
 }  // namespace mcld
diff --git a/lib/Target/ARM/ARMException.h b/lib/Target/ARM/ARMException.h
index 96afbde..157be2e 100644
--- a/lib/Target/ARM/ARMException.h
+++ b/lib/Target/ARM/ARMException.h
@@ -9,8 +9,11 @@
 #ifndef TARGET_ARM_ARMEXCEPTION_H_
 #define TARGET_ARM_ARMEXCEPTION_H_
 
+#include "mcld/LD/LDSection.h"
+
 #include <llvm/ADT/PointerUnion.h>
 #include <llvm/ADT/StringRef.h>
+#include <llvm/Support/ELF.h>
 
 #include <map>
 #include <memory>
@@ -21,6 +24,7 @@
 class Fragment;
 class Input;
 class LDSection;
+class Module;
 class RegionFragment;
 class RelocData;
 
@@ -29,10 +33,7 @@
  public:
   ARMExSectionTuple()
       : m_pTextSection(NULL),
-        m_pExIdxSection(NULL),
-        m_pExTabSection(NULL),
-        m_pRelExIdxSection(NULL),
-        m_pRelExTabSection(NULL) {
+        m_pExIdxSection(NULL) {
   }
 
   LDSection* getTextSection() const {
@@ -43,18 +44,6 @@
     return m_pExIdxSection;
   }
 
-  LDSection* getExTabSection() const {
-    return m_pExTabSection;
-  }
-
-  LDSection* getRelExIdxSection() const {
-    return m_pRelExIdxSection;
-  }
-
-  LDSection* getRelExTabSection() const {
-    return m_pRelExTabSection;
-  }
-
   void setTextSection(LDSection* pSection) {
     m_pTextSection = pSection;
   }
@@ -63,18 +52,6 @@
     m_pExIdxSection = pSection;
   }
 
-  void setExTabSection(LDSection* pSection) {
-    m_pExTabSection = pSection;
-  }
-
-  void setRelExIdxSection(LDSection* pSection) {
-    m_pRelExIdxSection = pSection;
-  }
-
-  void setRelExTabSection(LDSection* pSection) {
-    m_pRelExTabSection = pSection;
-  }
-
   RegionFragment* getTextFragment() const {
     return m_pTextFragment;
   }
@@ -83,18 +60,6 @@
     return m_pExIdxFragment;
   }
 
-  RegionFragment* getExTabFragment() const {
-    return m_pExTabFragment;
-  }
-
-  RelocData* getExIdxRelocData() const {
-    return m_pExIdxRelocData;
-  }
-
-  RelocData* getExTabRelocData() const {
-    return m_pExTabRelocData;
-  }
-
   void setTextFragment(RegionFragment* pFragment) {
     m_pTextFragment = pFragment;
   }
@@ -103,18 +68,6 @@
     m_pExIdxFragment = pFragment;
   }
 
-  void setExTabFragment(RegionFragment* pFragment) {
-    m_pExTabFragment = pFragment;
-  }
-
-  void setExIdxRelocData(RelocData* pRelocData) {
-    m_pExIdxRelocData = pRelocData;
-  }
-
-  void setExTabRelocData(RelocData* pRelocData) {
-    m_pExTabRelocData = pRelocData;
-  }
-
  private:
   // .text section
   union {
@@ -127,102 +80,60 @@
     LDSection*      m_pExIdxSection;
     RegionFragment* m_pExIdxFragment;
   };
-
-  // .ARM.extab section
-  union {
-    LDSection*      m_pExTabSection;
-    RegionFragment* m_pExTabFragment;
-  };
-
-  // .rel.ARM.exidx section
-  union {
-    LDSection*      m_pRelExIdxSection;
-    RelocData*      m_pExIdxRelocData;
-  };
-
-  // .rel.ARM.extab section
-  union {
-    LDSection*      m_pRelExTabSection;
-    RelocData*      m_pExTabRelocData;
-  };
 };
 
-/// ARMInputExMap - ARM exception handling data of an Input
+/// ARMInputExMap - ARM exception handling section mapping of a mcld::Input.
 class ARMInputExMap {
  public:
-  typedef std::map<std::string, std::unique_ptr<ARMExSectionTuple> > NameMap;
-  typedef NameMap::iterator iterator;
-  typedef NameMap::const_iterator const_iterator;
+  typedef std::map<LDSection*, std::unique_ptr<ARMExSectionTuple> > SectMap;
+  typedef SectMap::iterator iterator;
+  typedef SectMap::const_iterator const_iterator;
 
  public:
-  ARMInputExMap() { }
+  // create - Build the exception handling section mapping of a mcld::Input.
+  static std::unique_ptr<ARMInputExMap> create(Input &input);
 
-  /// get - Get the ARMExSectionTuple by the corresponding text section name.
-  /// As an exception, to get the ARMExSectionTuple for .text section, use ""
-  /// as the section name instead.
-  ARMExSectionTuple* get(const char* pName) const {
-    NameMap::const_iterator it = m_NameToExData.find(pName);
-    if (it == m_NameToExData.end()) {
+  /// getByExSection - Get the ARMExSectionTuple by the address of the
+  /// .ARM.exidx section.
+  ARMExSectionTuple* getByExSection(LDSection &pSect) const {
+    assert(pSect.type() == llvm::ELF::SHT_ARM_EXIDX);
+    SectMap::const_iterator it = m_SectToExData.find(&pSect);
+    if (it == m_SectToExData.end()) {
       return NULL;
     }
     return it->second.get();
   }
 
-  ARMExSectionTuple* getByExSection(llvm::StringRef pName) const {
-    assert((pName.startswith(".ARM.exidx") ||
-            pName.startswith(".ARM.extab")) &&
-           "Not a .ARM.exidx section name");
-    return get(pName.data() + sizeof(".ARM.ex***") - 1);
-  }
-
-  ARMExSectionTuple* getByRelExSection(llvm::StringRef pName) const {
-    assert((pName.startswith(".rel.ARM.exidx") ||
-            pName.startswith(".rel.ARM.extab")) &&
-           "Not a .rel.ARM.exidx section name");
-    return get(pName.data() + sizeof(".rel.ARM.ex***") - 1);
-  }
-
   /// getOrCreate - Get an existing or create a new ARMExSectionTuple which is
-  /// associated with the text section name.  As an exception, use "" as the
-  /// section name for .text section.
-  ARMExSectionTuple* getOrCreate(const char* pName) {
-    std::unique_ptr<ARMExSectionTuple>& result = m_NameToExData[pName];
+  /// associated with the address of the .ARM.exidx section.
+  ARMExSectionTuple* getOrCreateByExSection(LDSection &pSect) {
+    assert(pSect.type() == llvm::ELF::SHT_ARM_EXIDX);
+    std::unique_ptr<ARMExSectionTuple>& result = m_SectToExData[&pSect];
     if (!result) {
       result.reset(new ARMExSectionTuple());
     }
     return result.get();
   }
 
-  ARMExSectionTuple* getOrCreateByExSection(llvm::StringRef pName) {
-    assert((pName.startswith(".ARM.exidx") ||
-            pName.startswith(".ARM.extab")) &&
-           "Not a .ARM.exidx section name");
-    return getOrCreate(pName.data() + sizeof(".ARM.ex***") - 1);
-  }
-
-  ARMExSectionTuple* getOrCreateByRelExSection(llvm::StringRef pName) {
-    assert((pName.startswith(".rel.ARM.exidx") ||
-            pName.startswith(".rel.ARM.extab")) &&
-           "Not a .rel.ARM.exidx section name");
-    return getOrCreate(pName.data() + sizeof(".rel.ARM.ex***") - 1);
-  }
-
   /// begin - return the iterator to the begin of the map
-  iterator       begin()       { return m_NameToExData.begin(); }
-  const_iterator begin() const { return m_NameToExData.begin(); }
+  iterator       begin()       { return m_SectToExData.begin(); }
+  const_iterator begin() const { return m_SectToExData.begin(); }
 
   /// end - return the iterator to the end of the map
-  iterator       end()       { return m_NameToExData.end(); }
-  const_iterator end() const { return m_NameToExData.end(); }
+  iterator       end()       { return m_SectToExData.end(); }
+  const_iterator end() const { return m_SectToExData.end(); }
 
   /// erase - remove an entry from the map
-  void erase(iterator it) { m_NameToExData.erase(it); }
+  void erase(iterator it) { m_SectToExData.erase(it); }
 
  private:
-  NameMap m_NameToExData;
+  ARMInputExMap() = default;
+
+ private:
+  SectMap m_SectToExData;
 };
 
-/// ARMExData - ARM exception handling data of a module
+/// ARMExData - ARM exception handling data of a mcld::Module.
 class ARMExData {
  private:
   typedef std::map<Input*, std::unique_ptr<ARMInputExMap> > InputMap;
@@ -230,11 +141,12 @@
   typedef std::map<const Fragment*, ARMExSectionTuple*> ExIdxMap;
 
  public:
-  ARMExData() { }
+  // create - Build the exception handling section mapping of a mcld::Module.
+  static std::unique_ptr<ARMExData> create(Module &module);
 
   // addInputMap - register the ARMInputExMap with associated pInput
   void addInputMap(Input* pInput,
-                   std::unique_ptr<ARMInputExMap>&& pExMap);
+                   std::unique_ptr<ARMInputExMap> pExMap);
 
   // getInputMap - get the ARMInputExMap corresponding to pInput
   ARMInputExMap* getInputMap(Input* pInput) const {
@@ -255,6 +167,9 @@
   }
 
  private:
+  ARMExData() = default;
+
+ private:
   // Map from Input to ARMInputExMap
   InputMap m_Inputs;
 
diff --git a/lib/Target/ARM/ARMLDBackend.cpp b/lib/Target/ARM/ARMLDBackend.cpp
index 0907cac..b2fbfb2 100644
--- a/lib/Target/ARM/ARMLDBackend.cpp
+++ b/lib/Target/ARM/ARMLDBackend.cpp
@@ -10,6 +10,7 @@
 #include "ARMGNUInfo.h"
 #include "ARMELFAttributeData.h"
 #include "ARMELFDynamic.h"
+#include "ARMException.h"
 #include "ARMLDBackend.h"
 #include "ARMRelocator.h"
 #include "ARMToARMStub.h"
@@ -19,16 +20,17 @@
 
 #include "mcld/IRBuilder.h"
 #include "mcld/LinkerConfig.h"
+#include "mcld/ADT/ilist_sort.h"
 #include "mcld/Fragment/AlignFragment.h"
 #include "mcld/Fragment/FillFragment.h"
 #include "mcld/Fragment/NullFragment.h"
 #include "mcld/Fragment/RegionFragment.h"
 #include "mcld/Fragment/Stub.h"
 #include "mcld/LD/BranchIslandFactory.h"
-#include "mcld/LD/LDContext.h"
 #include "mcld/LD/ELFFileFormat.h"
-#include "mcld/LD/ELFSegmentFactory.h"
 #include "mcld/LD/ELFSegment.h"
+#include "mcld/LD/ELFSegmentFactory.h"
+#include "mcld/LD/LDContext.h"
 #include "mcld/LD/StubFactory.h"
 #include "mcld/Object/ObjectBuilder.h"
 #include "mcld/Support/MemoryArea.h"
@@ -45,9 +47,75 @@
 #include <llvm/Support/ELF.h>
 
 #include <cstring>
+#include <vector>
 
 namespace mcld {
 
+/// Fragment data for EXIDX_CANTUNWIND.
+static const char g_CantUnwindEntry[8] = {
+  // Relocation to text section.
+  0, 0, 0, 0,
+  // EXIDX_CANTUNWIND (little endian.)
+  1, 0, 0, 0,
+};
+
+/// Helper function to create a local symbol at the end of the fragment.
+static mcld::ResolveInfo*
+CreateLocalSymbolToFragmentEnd(mcld::Module& pModule, mcld::Fragment& pFrag) {
+  // Create and add symbol to the name pool.
+  mcld::ResolveInfo* resolveInfo =
+      pModule.getNamePool().createSymbol(/* pName */"",
+                                         /* pIsDyn */false,
+                                         mcld::ResolveInfo::Section,
+                                         mcld::ResolveInfo::Define,
+                                         mcld::ResolveInfo::Local,
+                                         /* pSize */0,
+                                         mcld::ResolveInfo::Hidden);
+  if (resolveInfo == nullptr) {
+    return nullptr;
+  }
+
+  // Create input symbol.
+  mcld::LDSymbol* inputSym = mcld::LDSymbol::Create(*resolveInfo);
+  if (inputSym == nullptr) {
+    return nullptr;
+  }
+
+  inputSym->setFragmentRef(mcld::FragmentRef::Create(pFrag, pFrag.size()));
+  inputSym->setValue(/* pValue */0);
+
+  // The output symbol is simply an alias to the input symbol.
+  resolveInfo->setSymPtr(inputSym);
+
+  return resolveInfo;
+}
+
+/// Comparator to sort .ARM.exidx fragments according to the address of the
+/// corresponding .text fragment.
+class ExIdxFragmentComparator {
+ private:
+  const ARMExData& m_pExData;
+
+ public:
+  explicit ExIdxFragmentComparator(const ARMExData& pExData)
+      : m_pExData(pExData) {
+  }
+
+  bool operator()(const Fragment& a, const Fragment& b) {
+    ARMExSectionTuple* tupleA = m_pExData.getTupleByExIdx(&a);
+    ARMExSectionTuple* tupleB = m_pExData.getTupleByExIdx(&b);
+
+    Fragment* textFragA = tupleA->getTextFragment();
+    Fragment* textFragB = tupleB->getTextFragment();
+
+    uint64_t addrA = textFragA->getParent()->getSection().addr() +
+                     textFragA->getOffset();
+    uint64_t addrB = textFragB->getParent()->getSection().addr() +
+                     textFragB->getOffset();
+    return (addrA < addrB);
+  }
+};
+
 //===----------------------------------------------------------------------===//
 // ARMGNULDBackend
 //===----------------------------------------------------------------------===//
@@ -398,7 +466,10 @@
 
 /// preMergeSections - hooks to be executed before merging sections
 void ARMGNULDBackend::preMergeSections(Module& pModule) {
-  scanInputExceptionSections(pModule);
+  // Since the link relationship between .text and .ARM.exidx will be discarded
+  // after merging sections, we have to build the exception handling section
+  // mapping before section merge.
+  m_pExData = ARMExData::create(pModule);
 }
 
 /// postMergeSections - hooks to be executed after merging sections
@@ -634,6 +705,104 @@
   return SHO_UNDEFINED;
 }
 
+void ARMGNULDBackend::rewriteARMExIdxSection(Module& pModule) {
+  if (!m_pEXIDX->hasSectionData()) {
+    // Return if this is empty section.
+    return;
+  }
+
+  SectionData* sectData = m_pEXIDX->getSectionData();
+  SectionData::FragmentListType& list = sectData->getFragmentList();
+
+  // Move the first fragment (align fragment) and last fragment (null fragment)
+  // to temporary list because we would only like to sort the region fragment.
+  SectionData::FragmentListType tmp;
+  {
+    SectionData::iterator first = sectData->begin();
+    SectionData::iterator last = sectData->end();
+    --last;
+
+    assert(first->getKind() == Fragment::Alignment);
+    assert(last->getKind() == Fragment::Null);
+
+    tmp.splice(tmp.end(), list, first);
+    tmp.splice(tmp.end(), list, last);
+  }
+
+  // Sort the region fragments in the .ARM.exidx output section.
+  sort(list, ExIdxFragmentComparator(*m_pExData));
+
+  // Fix the coverage of the .ARM.exidx table.
+  llvm::StringRef cantUnwindRegion(g_CantUnwindEntry,
+                                   sizeof(g_CantUnwindEntry));
+
+  SectionData::FragmentListType::iterator it = list.begin();
+  if (it != list.end()) {
+    Fragment* prevTextFrag = m_pExData->getTupleByExIdx(&*it)->getTextFragment();
+    uint64_t prevTextEnd = prevTextFrag->getParent()->getSection().addr() +
+                           prevTextFrag->getOffset() +
+                           prevTextFrag->size();
+    ++it;
+    while (it != list.end()) {
+      Fragment* currTextFrag =
+          m_pExData->getTupleByExIdx(&*it)->getTextFragment();
+      uint64_t currTextBegin = currTextFrag->getParent()->getSection().addr() +
+                               currTextFrag->getOffset();
+
+      if (currTextBegin > prevTextEnd) {
+        // Found a gap. Insert a can't unwind entry.
+        RegionFragment* frag = new RegionFragment(cantUnwindRegion, nullptr);
+        frag->setParent(sectData);
+        list.insert(it, frag);
+
+        // Add PREL31 reference to the beginning of the uncovered region.
+        Relocation* reloc =
+            Relocation::Create(static_cast<uint32_t>(llvm::ELF::R_ARM_PREL31),
+                               *FragmentRef::Create(*frag, /* pOffset */0),
+                               /* pAddend */0);
+        reloc->setSymInfo(
+            CreateLocalSymbolToFragmentEnd(pModule, *prevTextFrag));
+        addExtraRelocation(reloc);
+      }
+
+      prevTextEnd = currTextBegin + currTextFrag->size();
+      prevTextFrag = currTextFrag;
+      ++it;
+    }
+
+    // Add a can't unwind entry to terminate .ARM.exidx section.
+    RegionFragment* frag = new RegionFragment(cantUnwindRegion, nullptr);
+    frag->setParent(sectData);
+    list.push_back(frag);
+
+    // Add PREL31 reference to the end of the .text section.
+    Relocation* reloc =
+        Relocation::Create(static_cast<uint32_t>(llvm::ELF::R_ARM_PREL31),
+                           *FragmentRef::Create(*frag, /* pOffset */0),
+                           /* pAddend */0);
+    reloc->setSymInfo(CreateLocalSymbolToFragmentEnd(pModule, *prevTextFrag));
+    addExtraRelocation(reloc);
+  }
+
+  // Add the first and the last fragment back.
+  list.splice(list.begin(), tmp, tmp.begin());
+  list.splice(list.end(), tmp, tmp.begin());
+
+  // Update the fragment offsets.
+  uint64_t offset = 0;
+  for (SectionData::iterator it = sectData->begin(), end = sectData->end();
+       it != end; ++it) {
+    it->setOffset(offset);
+    offset += it->size();
+  }
+
+  // Update the section size.
+  m_pEXIDX->setSize(offset);
+
+  // Rebuild the section header.
+  setOutputSectionAddress(pModule);
+}
+
 /// relax - the relaxation pass
 bool ARMGNULDBackend::relax(Module& pModule, IRBuilder& pBuilder) {
   if (!GNULDBackend::relax(pModule, pBuilder)) {
@@ -692,6 +861,10 @@
                                                   pBuilder,
                                                   *getBRIslandFactory());
             if (stub != NULL) {
+              assert(stub->symInfo() != NULL);
+              // reset the branch target of the reloc to this stub instead
+              relocation->setSymInfo(stub->symInfo());
+
               switch (config().options().getStripSymbolMode()) {
                 case GeneralOptions::StripSymbolMode::StripAllSymbols:
                 case GeneralOptions::StripSymbolMode::StripLocals:
@@ -703,12 +876,7 @@
                   LDSection& strtab = file_format->getStrTab();
 
                   // increase the size of .symtab and .strtab if needed
-                  if (config().targets().is32Bits())
-                    symtab.setSize(symtab.size() +
-                                   sizeof(llvm::ELF::Elf32_Sym));
-                  else
-                    symtab.setSize(symtab.size() +
-                                   sizeof(llvm::ELF::Elf64_Sym));
+                  symtab.setSize(symtab.size() + sizeof(llvm::ELF::Elf32_Sym));
                   symtab.setInfo(symtab.getInfo() + 1);
                   strtab.setSize(strtab.size() + stub->symInfo()->nameSize() +
                                  1);
@@ -729,35 +897,62 @@
   }  // for all inputs
 
   // find the first fragment w/ invalid offset due to stub insertion
-  Fragment* invalid = NULL;
+  std::vector<Fragment*> invalid_frags;
   pFinished = true;
   for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(),
                                      island_end = getBRIslandFactory()->end();
        island != island_end;
        ++island) {
-    if ((*island).end() == file_format->getText().getSectionData()->end())
-      break;
+    if ((*island).size() > stubGroupSize()) {
+      error(diag::err_no_space_to_place_stubs) << stubGroupSize();
+      return false;
+    }
 
-    Fragment* exit = (*island).end();
+    if ((*island).numOfStubs() == 0) {
+      continue;
+    }
+
+    Fragment* exit = &*(*island).end();
+    if (exit == (*island).begin()->getParent()->end()) {
+      continue;
+    }
+
     if (((*island).offset() + (*island).size()) > exit->getOffset()) {
-      invalid = exit;
-      pFinished = false;
-      break;
+      if (invalid_frags.empty() ||
+          (invalid_frags.back()->getParent() != (*island).getParent())) {
+        invalid_frags.push_back(exit);
+        pFinished = false;
+      }
+      continue;
     }
   }
 
   // reset the offset of invalid fragments
-  while (invalid != NULL) {
-    invalid->setOffset(invalid->getPrevNode()->getOffset() +
-                       invalid->getPrevNode()->size());
-    invalid = invalid->getNextNode();
+  for (auto it = invalid_frags.begin(), ie = invalid_frags.end(); it != ie;
+       ++it) {
+    Fragment* invalid = *it;
+    while (invalid != NULL) {
+      invalid->setOffset(invalid->getPrevNode()->getOffset() +
+                         invalid->getPrevNode()->size());
+      invalid = invalid->getNextNode();
+    }
   }
 
-  // reset the size of .text
+  // reset the size of section that has stubs inserted.
   if (isRelaxed) {
-    file_format->getText().setSize(
-        file_format->getText().getSectionData()->back().getOffset() +
-        file_format->getText().getSectionData()->back().size());
+    SectionData* prev = NULL;
+    for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(),
+                                       island_end = getBRIslandFactory()->end();
+         island != island_end;
+         ++island) {
+      SectionData* sd = (*island).begin()->getParent();
+      if ((*island).numOfStubs() != 0) {
+        if (sd != prev) {
+          sd->getSection().setSize(sd->back().getOffset() + sd->back().size());
+        }
+      }
+      prev = sd;
+    }
   }
   return isRelaxed;
 }
@@ -777,7 +972,7 @@
 }
 
 /// maxFwdBranchOffset
-int64_t ARMGNULDBackend::maxFwdBranchOffset() {
+int64_t ARMGNULDBackend::maxFwdBranchOffset() const {
   if (m_pAttrData->usingThumb2()) {
     return THM2_MAX_FWD_BRANCH_OFFSET;
   } else {
@@ -786,7 +981,7 @@
 }
 
 /// maxBwdBranchOffset
-int64_t ARMGNULDBackend::maxBwdBranchOffset() {
+int64_t ARMGNULDBackend::maxBwdBranchOffset() const {
   if (m_pAttrData->usingThumb2()) {
     return THM2_MAX_BWD_BRANCH_OFFSET;
   } else {
diff --git a/lib/Target/ARM/ARMLDBackend.h b/lib/Target/ARM/ARMLDBackend.h
index d2eae7c..53f4e9f 100644
--- a/lib/Target/ARM/ARMLDBackend.h
+++ b/lib/Target/ARM/ARMLDBackend.h
@@ -17,6 +17,8 @@
 #include "mcld/Target/GNULDBackend.h"
 #include "mcld/Target/OutputRelocSection.h"
 
+#include <memory>
+
 namespace mcld {
 
 class ARMELFAttributeData;
@@ -139,9 +141,9 @@
   void defineGOTSymbol(IRBuilder& pBuilder);
 
   /// maxFwdBranchOffset
-  int64_t maxFwdBranchOffset();
+  int64_t maxFwdBranchOffset() const;
   /// maxBwdBranchOffset
-  int64_t maxBwdBranchOffset();
+  int64_t maxBwdBranchOffset() const;
 
   /// mayRelax - Backends should override this function if they need relaxation
   bool mayRelax() { return true; }
@@ -171,14 +173,6 @@
   /// target-dependent segments
   virtual void doCreateProgramHdrs(Module& pModule);
 
-  /// scanInputExceptionSections - scan exception-related input sections in
-  /// the Module, build the ARMExData, and reclaim GC'ed sections
-  void scanInputExceptionSections(Module& pModule);
-
-  /// scanInputExceptionSections - scan exception-related input sections in
-  /// the Input, build the ARMInputExMap, and reclaim GC'ed sections
-  void scanInputExceptionSections(Module& pModule, Input& pInput);
-
   /// rewriteExceptionSection - rewrite the output .ARM.exidx section.
   void rewriteARMExIdxSection(Module& pModule);
 
@@ -208,9 +202,10 @@
   //  LDSection* m_pDebugOverlay;    // .ARM.debug_overlay
   //  LDSection* m_pOverlayTable;    // .ARM.overlay_table
 
-  // m_ExData - exception handling section data structures
-  ARMExData m_ExData;
+  // m_pExData - exception handling section data structures
+  std::unique_ptr<ARMExData> m_pExData;
 };
+
 }  // namespace mcld
 
 #endif  // TARGET_ARM_ARMLDBACKEND_H_
diff --git a/lib/Target/ARM/ARMRelocator.cpp b/lib/Target/ARM/ARMRelocator.cpp
index fd845cf..0e0a5ce 100644
--- a/lib/Target/ARM/ARMRelocator.cpp
+++ b/lib/Target/ARM/ARMRelocator.cpp
@@ -946,7 +946,8 @@
 
   Relocator::DWord T = getThumbBit(pReloc);
   Relocator::DWord A =
-      helper_thumb32_cond_branch_offset(upper_inst, lower_inst);
+      helper_thumb32_cond_branch_offset(upper_inst, lower_inst) +
+      pReloc.addend();
   Relocator::Address P = pReloc.place();
   Relocator::Address S;
   // if symbol has plt
@@ -1052,7 +1053,8 @@
   uint16_t lower_inst = *(reinterpret_cast<uint16_t*>(&pReloc.target()) + 1);
 
   Relocator::DWord T = getThumbBit(pReloc);
-  Relocator::DWord A = helper_thumb32_branch_offset(upper_inst, lower_inst);
+  Relocator::DWord A =
+      helper_thumb32_branch_offset(upper_inst, lower_inst) + pReloc.addend();
   Relocator::Address P = pReloc.place();
   Relocator::Address S;
 
diff --git a/lib/Target/GNULDBackend.cpp b/lib/Target/GNULDBackend.cpp
index 79fa25f..ac2cc2b 100644
--- a/lib/Target/GNULDBackend.cpp
+++ b/lib/Target/GNULDBackend.cpp
@@ -59,7 +59,7 @@
 //===----------------------------------------------------------------------===//
 static const std::string simple_c_identifier_allowed_chars =
     "0123456789"
-    "ABCDEFGHIJKLMNOPWRSTUVWXYZ"
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     "abcdefghijklmnopqrstuvwxyz"
     "_";
 
@@ -2175,7 +2175,13 @@
             // Try to align p_vaddr at page boundary if not in script options.
             // To do so will add more padding in file, but can save one page
             // at runtime.
-            alignAddress(vma, (*seg)->align());
+            // Avoid doing this optimization if -z relro is given, because there
+            // seems to be too many padding.
+            if (!config().options().hasRelro()) {
+              alignAddress(vma, (*seg)->align());
+            } else {
+              vma += abiPageSize();
+            }
           }
         }
       } else {
@@ -2876,11 +2882,21 @@
   }
 }
 
+unsigned GNULDBackend::stubGroupSize() const {
+  const unsigned group_size = config().targets().getStubGroupSize();
+  if (group_size == 0) {
+    return m_pInfo->stubGroupSize();
+  } else {
+    return group_size;
+  }
+}
+
 /// initBRIslandFactory - initialize the branch island factory for relaxation
 bool GNULDBackend::initBRIslandFactory() {
   if (m_pBRIslandFactory == NULL) {
-    m_pBRIslandFactory =
-        new BranchIslandFactory(maxFwdBranchOffset(), maxBwdBranchOffset());
+    m_pBRIslandFactory = new BranchIslandFactory(maxFwdBranchOffset(),
+                                                 maxBwdBranchOffset(),
+                                                 stubGroupSize());
   }
   return true;
 }
diff --git a/lib/Target/Hexagon/HexagonLDBackend.cpp b/lib/Target/Hexagon/HexagonLDBackend.cpp
index 5d4d4dc..b46de99 100644
--- a/lib/Target/Hexagon/HexagonLDBackend.cpp
+++ b/lib/Target/Hexagon/HexagonLDBackend.cpp
@@ -34,6 +34,7 @@
 #include <llvm/Support/Casting.h>
 
 #include <cstring>
+#include <vector>
 
 namespace mcld {
 
@@ -577,6 +578,9 @@
                                                   *getBRIslandFactory());
             if (stub != NULL) {
               assert(stub->symInfo() != NULL);
+              // reset the branch target of the reloc to this stub instead
+              relocation->setSymInfo(stub->symInfo());
+
               // increase the size of .symtab and .strtab
               LDSection& symtab = file_format->getSymTab();
               LDSection& strtab = file_format->getStrTab();
@@ -594,35 +598,62 @@
   }
 
   // find the first fragment w/ invalid offset due to stub insertion
-  Fragment* invalid = NULL;
+  std::vector<Fragment*> invalid_frags;
   pFinished = true;
   for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(),
                                      island_end = getBRIslandFactory()->end();
        island != island_end;
        ++island) {
-    if ((*island).end() == file_format->getText().getSectionData()->end())
-      break;
+    if ((*island).size() > stubGroupSize()) {
+      error(diag::err_no_space_to_place_stubs) << stubGroupSize();
+      return false;
+    }
 
-    Fragment* exit = (*island).end();
+    if ((*island).numOfStubs() == 0) {
+      continue;
+    }
+
+    Fragment* exit = &*(*island).end();
+    if (exit == (*island).begin()->getParent()->end()) {
+      continue;
+    }
+
     if (((*island).offset() + (*island).size()) > exit->getOffset()) {
-      invalid = exit;
-      pFinished = false;
-      break;
+      if (invalid_frags.empty() ||
+          (invalid_frags.back()->getParent() != (*island).getParent())) {
+        invalid_frags.push_back(exit);
+        pFinished = false;
+      }
+      continue;
     }
   }
 
   // reset the offset of invalid fragments
-  while (invalid != NULL) {
-    invalid->setOffset(invalid->getPrevNode()->getOffset() +
-                       invalid->getPrevNode()->size());
-    invalid = invalid->getNextNode();
+  for (auto it = invalid_frags.begin(), ie = invalid_frags.end(); it != ie;
+       ++it) {
+    Fragment* invalid = *it;
+    while (invalid != NULL) {
+      invalid->setOffset(invalid->getPrevNode()->getOffset() +
+                         invalid->getPrevNode()->size());
+      invalid = invalid->getNextNode();
+    }
   }
 
-  // reset the size of .text
+  // reset the size of section that has stubs inserted.
   if (isRelaxed) {
-    file_format->getText().setSize(
-        file_format->getText().getSectionData()->back().getOffset() +
-        file_format->getText().getSectionData()->back().size());
+    SectionData* prev = NULL;
+    for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(),
+                                       island_end = getBRIslandFactory()->end();
+         island != island_end;
+         ++island) {
+      SectionData* sd = (*island).begin()->getParent();
+      if ((*island).numOfStubs() != 0) {
+        if (sd != prev) {
+          sd->getSection().setSize(sd->back().getOffset() + sd->back().size());
+        }
+      }
+      prev = sd;
+    }
   }
   return isRelaxed;
 }
@@ -720,7 +751,7 @@
     return true;
   }
 
-  int8_t maxGPSize = config().options().getGPSize();
+  int8_t maxGPSize = config().targets().getGPSize();
 
   SymbolCategory::iterator com_sym, com_end;
 
diff --git a/lib/Target/Hexagon/HexagonLDBackend.h b/lib/Target/Hexagon/HexagonLDBackend.h
index 89aea83..8e32062 100644
--- a/lib/Target/Hexagon/HexagonLDBackend.h
+++ b/lib/Target/Hexagon/HexagonLDBackend.h
@@ -157,7 +157,7 @@
   void doCreateProgramHdrs(Module& pModule);
 
   /// maxFwdBranchOffset
-  int64_t maxFwdBranchOffset() { return ~(~0 << 6); }
+  int64_t maxFwdBranchOffset() const { return ~(~0U << 6); }
 
   virtual void setGOTSectionSize(IRBuilder& pBuilder);
 
diff --git a/lib/Target/Hexagon/HexagonPLT.cpp b/lib/Target/Hexagon/HexagonPLT.cpp
index ca9352a..dcd5c94 100644
--- a/lib/Target/Hexagon/HexagonPLT.cpp
+++ b/lib/Target/Hexagon/HexagonPLT.cpp
@@ -35,10 +35,10 @@
 HexagonPLT::HexagonPLT(LDSection& pSection,
                        HexagonGOTPLT& pGOTPLT,
                        const LinkerConfig& pConfig)
-    : PLT(pSection), m_GOTPLT(pGOTPLT), m_Config(pConfig) {
-  assert(LinkerConfig::DynObj == m_Config.codeGenType() ||
-         LinkerConfig::Exec == m_Config.codeGenType() ||
-         LinkerConfig::Binary == m_Config.codeGenType());
+    : PLT(pSection), m_GOTPLT(pGOTPLT) {
+  assert(LinkerConfig::DynObj == pConfig.codeGenType() ||
+         LinkerConfig::Exec == pConfig.codeGenType() ||
+         LinkerConfig::Binary == pConfig.codeGenType());
 
   m_PLT0 = hexagon_plt0;
   m_PLT0Size = sizeof(hexagon_plt0);
diff --git a/lib/Target/Hexagon/HexagonPLT.h b/lib/Target/Hexagon/HexagonPLT.h
index 22f580e..01f16b1 100644
--- a/lib/Target/Hexagon/HexagonPLT.h
+++ b/lib/Target/Hexagon/HexagonPLT.h
@@ -85,8 +85,6 @@
 
   const uint8_t* m_PLT0;
   unsigned int m_PLT0Size;
-
-  const LinkerConfig& m_Config;
 };
 
 class HexagonPLT1 : public PLT::Entry<sizeof(hexagon_plt1)> {
diff --git a/lib/Target/Mips/Android.mk b/lib/Target/Mips/Android.mk
index 0bb2d40..17e93f5 100644
--- a/lib/Target/Mips/Android.mk
+++ b/lib/Target/Mips/Android.mk
@@ -1,6 +1,7 @@
 LOCAL_PATH:= $(call my-dir)
 
 mcld_mips_target_SRC_FILES := \
+  MipsAbiFlags.cpp \
   MipsDiagnostic.cpp  \
   MipsELFDynamic.cpp  \
   MipsEmulation.cpp \
diff --git a/lib/Target/Mips/MipsAbiFlags.cpp b/lib/Target/Mips/MipsAbiFlags.cpp
new file mode 100644
index 0000000..ff17284
--- /dev/null
+++ b/lib/Target/Mips/MipsAbiFlags.cpp
@@ -0,0 +1,298 @@
+//===- MipsAbiFlags.cpp ---------------------------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "MipsAbiFlags.h"
+
+#include "mcld/Fragment/RegionFragment.h"
+#include "mcld/LD/LDSection.h"
+#include "mcld/LD/SectionData.h"
+#include "mcld/MC/Input.h"
+#include "mcld/Support/MsgHandling.h"
+
+#include <llvm/Support/Casting.h>
+#include <llvm/Support/MipsABIFlags.h>
+
+namespace mcld {
+
+// SHT_MIPS_ABIFLAGS has the same format for both 32/64-bit targets.
+// We do not support linking of big-endian code now so 32-bit LE
+// combination is Okay.
+typedef llvm::object::ELFType<llvm::support::little, false> ELF32LE;
+typedef llvm::object::Elf_Mips_ABIFlags<ELF32LE> ElfMipsAbiFlags;
+
+uint64_t MipsAbiFlags::size() {
+  return sizeof(ElfMipsAbiFlags);
+}
+
+uint64_t MipsAbiFlags::emit(const MipsAbiFlags& pInfo, MemoryRegion& pRegion) {
+  auto* buf = reinterpret_cast<ElfMipsAbiFlags*>(pRegion.begin());
+  buf->version = 0;
+  buf->isa_level = pInfo.m_IsaLevel;
+  buf->isa_rev = pInfo.m_IsaRev;
+  buf->gpr_size = pInfo.m_GprSize;
+  buf->cpr1_size = pInfo.m_Cpr1Size;
+  buf->cpr2_size = pInfo.m_Cpr2Size;
+  buf->fp_abi = pInfo.m_FpAbi;
+  buf->isa_ext = pInfo.m_IsaExt;
+  buf->ases = pInfo.m_Ases;
+  buf->flags1 = pInfo.m_Flags1;
+  buf->flags2 = 0;
+  return size();
+}
+
+bool MipsAbiFlags::fillBySection(const Input& pInput, const LDSection& pSection,
+                                 MipsAbiFlags& mipsAbi) {
+  assert(pSection.type() == llvm::ELF::SHT_MIPS_ABIFLAGS &&
+         "Unexpected section type");
+
+  if (pSection.size() != size()) {
+    error(diag::error_Mips_abiflags_invalid_size) << pInput.name();
+    return false;
+  }
+
+  const SectionData* secData = pSection.getSectionData();
+  if (secData->size() != 2 || !llvm::isa<RegionFragment>(secData->front())) {
+    error(diag::error_Mips_abiflags_invalid_size) << pInput.name();
+    return false;
+  }
+
+  const auto& frag = llvm::cast<RegionFragment>(secData->front());
+  auto* data =
+      reinterpret_cast<const ElfMipsAbiFlags*>(frag.getRegion().data());
+  if (data->version != 0) {
+    error(diag::error_Mips_abiflags_invalid_version) << int(data->version)
+                                                     << pInput.name();
+    return false;
+  }
+
+  mipsAbi.m_IsaLevel = data->isa_level;
+  mipsAbi.m_IsaRev = data->isa_rev;
+  mipsAbi.m_GprSize = data->gpr_size;
+  mipsAbi.m_Cpr1Size = data->cpr1_size;
+  mipsAbi.m_Cpr2Size = data->cpr2_size;
+  mipsAbi.m_FpAbi = data->fp_abi;
+  mipsAbi.m_IsaExt = data->isa_ext;
+  mipsAbi.m_Ases = data->ases;
+  mipsAbi.m_Flags1 = data->flags1;
+  return true;
+}
+
+static unsigned getIsaLevel(uint64_t flags) {
+  switch (flags & llvm::ELF::EF_MIPS_ARCH) {
+    case llvm::ELF::EF_MIPS_ARCH_1:
+      return 1;
+    case llvm::ELF::EF_MIPS_ARCH_2:
+      return 2;
+    case llvm::ELF::EF_MIPS_ARCH_3:
+      return 3;
+    case llvm::ELF::EF_MIPS_ARCH_4:
+      return 4;
+    case llvm::ELF::EF_MIPS_ARCH_5:
+      return 5;
+    case llvm::ELF::EF_MIPS_ARCH_32:
+    case llvm::ELF::EF_MIPS_ARCH_32R2:
+    case llvm::ELF::EF_MIPS_ARCH_32R6:
+      return 32;
+    case llvm::ELF::EF_MIPS_ARCH_64:
+    case llvm::ELF::EF_MIPS_ARCH_64R2:
+    case llvm::ELF::EF_MIPS_ARCH_64R6:
+      return 64;
+    default:
+      // We check ELF flags and show error in case
+      // of unknown value in other place.
+      llvm_unreachable("Unknown MIPS architecture flag");
+  }
+}
+
+static unsigned getIsaRev(uint64_t flags) {
+  switch (flags & llvm::ELF::EF_MIPS_ARCH) {
+    case llvm::ELF::EF_MIPS_ARCH_1:
+    case llvm::ELF::EF_MIPS_ARCH_2:
+    case llvm::ELF::EF_MIPS_ARCH_3:
+    case llvm::ELF::EF_MIPS_ARCH_4:
+    case llvm::ELF::EF_MIPS_ARCH_5:
+      return 0;
+    case llvm::ELF::EF_MIPS_ARCH_32:
+    case llvm::ELF::EF_MIPS_ARCH_64:
+      return 1;
+    case llvm::ELF::EF_MIPS_ARCH_32R2:
+    case llvm::ELF::EF_MIPS_ARCH_64R2:
+      return 2;
+    case llvm::ELF::EF_MIPS_ARCH_32R6:
+    case llvm::ELF::EF_MIPS_ARCH_64R6:
+      return 6;
+    default:
+      // We check ELF flags and show error in case
+      // of unknown value in other place.
+      llvm_unreachable("Unknown MIPS architecture flag");
+  }
+}
+
+static unsigned getIsaExt(uint64_t flags) {
+  switch (flags & llvm::ELF::EF_MIPS_MACH) {
+    case 0:
+      return llvm::Mips::AFL_EXT_NONE;
+    case llvm::ELF::EF_MIPS_MACH_3900: return llvm::Mips::AFL_EXT_3900;
+    case llvm::ELF::EF_MIPS_MACH_4010: return llvm::Mips::AFL_EXT_4010;
+    case llvm::ELF::EF_MIPS_MACH_4100: return llvm::Mips::AFL_EXT_4010;
+    case llvm::ELF::EF_MIPS_MACH_4111: return llvm::Mips::AFL_EXT_4111;
+    case llvm::ELF::EF_MIPS_MACH_4120: return llvm::Mips::AFL_EXT_4120;
+    case llvm::ELF::EF_MIPS_MACH_4650: return llvm::Mips::AFL_EXT_4650;
+    case llvm::ELF::EF_MIPS_MACH_5400: return llvm::Mips::AFL_EXT_5400;
+    case llvm::ELF::EF_MIPS_MACH_5500: return llvm::Mips::AFL_EXT_5500;
+    case llvm::ELF::EF_MIPS_MACH_5900: return llvm::Mips::AFL_EXT_5900;
+    case llvm::ELF::EF_MIPS_MACH_SB1: return llvm::Mips::AFL_EXT_SB1;
+    case llvm::ELF::EF_MIPS_MACH_LS2E: return llvm::Mips::AFL_EXT_LOONGSON_2E;
+    case llvm::ELF::EF_MIPS_MACH_LS2F: return llvm::Mips::AFL_EXT_LOONGSON_2F;
+    case llvm::ELF::EF_MIPS_MACH_LS3A: return llvm::Mips::AFL_EXT_LOONGSON_3A;
+    case llvm::ELF::EF_MIPS_MACH_OCTEON3: return llvm::Mips::AFL_EXT_OCTEON3;
+    case llvm::ELF::EF_MIPS_MACH_OCTEON2: return llvm::Mips::AFL_EXT_OCTEON2;
+    case llvm::ELF::EF_MIPS_MACH_OCTEON: return llvm::Mips::AFL_EXT_OCTEON;
+    case llvm::ELF::EF_MIPS_MACH_XLR: return llvm::Mips::AFL_EXT_XLR;
+    default:
+      // We check ELF flags and show error in case
+      // of unknown value in other place.
+      llvm_unreachable("Unknown MIPS extension flag");
+  }
+}
+
+static bool is32BitElfFlags(uint64_t flags) {
+  if (flags & llvm::ELF::EF_MIPS_32BITMODE)
+    return true;
+
+  uint64_t arch = flags & llvm::ELF::EF_MIPS_ARCH;
+  if (arch == llvm::ELF::EF_MIPS_ARCH_1 ||
+      arch == llvm::ELF::EF_MIPS_ARCH_2 ||
+      arch == llvm::ELF::EF_MIPS_ARCH_32 ||
+      arch == llvm::ELF::EF_MIPS_ARCH_32R2 ||
+      arch == llvm::ELF::EF_MIPS_ARCH_32R6)
+    return true;
+
+  uint64_t abi = flags & llvm::ELF::EF_MIPS_ABI;
+  if (abi == llvm::ELF::EF_MIPS_ABI_O32 || abi == llvm::ELF::EF_MIPS_ABI_EABI32)
+    return true;
+
+  return false;
+}
+
+bool MipsAbiFlags::fillByElfFlags(const Input& pInput, uint64_t elfFlags,
+                                  MipsAbiFlags& mipsAbi) {
+  mipsAbi.m_IsaLevel = getIsaLevel(elfFlags);
+  mipsAbi.m_IsaRev = getIsaRev(elfFlags);
+  mipsAbi.m_IsaExt = getIsaExt(elfFlags);
+
+  mipsAbi.m_GprSize = is32BitElfFlags(elfFlags) ?
+                      llvm::Mips::AFL_REG_32 : llvm::Mips::AFL_REG_64;
+
+  mipsAbi.m_Cpr1Size = llvm::Mips::AFL_REG_NONE;
+  mipsAbi.m_Cpr2Size = llvm::Mips::AFL_REG_NONE;
+  mipsAbi.m_FpAbi = llvm::Mips::Val_GNU_MIPS_ABI_FP_ANY;
+
+  mipsAbi.m_Ases = 0;
+  if (elfFlags & llvm::ELF::EF_MIPS_MICROMIPS)
+    mipsAbi.m_Ases |= llvm::Mips::AFL_ASE_MICROMIPS;
+  if (elfFlags & llvm::ELF::EF_MIPS_ARCH_ASE_M16)
+    mipsAbi.m_Ases |= llvm::Mips::AFL_ASE_MIPS16;
+  if (elfFlags & llvm::ELF::EF_MIPS_ARCH_ASE_MDMX)
+    mipsAbi.m_Ases |= llvm::Mips::AFL_ASE_MDMX;
+
+  mipsAbi.m_Flags1 = 0;
+  return true;
+}
+
+bool MipsAbiFlags::isCompatible(const Input& pInput, const MipsAbiFlags& elf,
+                                const MipsAbiFlags& abi) {
+  unsigned isaRev = abi.m_IsaRev;
+  if (isaRev == 3 || isaRev == 5)
+    isaRev = 2;
+  if (abi.m_IsaLevel != elf.m_IsaLevel || isaRev != elf.m_IsaRev) {
+    warning(diag::warn_Mips_isa_incompatible) << pInput.name();
+    return false;
+  }
+  if (abi.m_IsaExt != elf.m_IsaExt) {
+    warning(diag::warn_Mips_isa_ext_incompatible) << pInput.name();
+    return false;
+  }
+  if ((abi.m_Ases & elf.m_Ases) != elf.m_Ases) {
+    warning(diag::warn_Mips_ases_incompatible) << pInput.name();
+    return false;
+  }
+  return true;
+}
+
+static bool isFpGreater(uint64_t fpA, uint64_t fpB) {
+  if (fpB == llvm::Mips::Val_GNU_MIPS_ABI_FP_ANY)
+    return true;
+  if (fpB == llvm::Mips::Val_GNU_MIPS_ABI_FP_64A &&
+      fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_64)
+    return true;
+  if (fpB != llvm::Mips::Val_GNU_MIPS_ABI_FP_XX)
+    return false;
+  return fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_DOUBLE ||
+         fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_64 ||
+         fpA == llvm::Mips::Val_GNU_MIPS_ABI_FP_64A;
+}
+
+static llvm::StringRef getFpAbiName(uint64_t abi) {
+  switch (abi) {
+    case llvm::Mips::Val_GNU_MIPS_ABI_FP_ANY:
+      return "<any>";
+    case llvm::Mips::Val_GNU_MIPS_ABI_FP_DOUBLE:
+      return "-mdouble-float";
+    case llvm::Mips::Val_GNU_MIPS_ABI_FP_SINGLE:
+      return "-msingle-float";
+    case llvm::Mips::Val_GNU_MIPS_ABI_FP_SOFT:
+      return "-msoft-float";
+    case llvm::Mips::Val_GNU_MIPS_ABI_FP_OLD_64:
+      return "-mips32r2 -mfp64 (old)";
+    case llvm::Mips::Val_GNU_MIPS_ABI_FP_XX:
+      return "-mfpxx";
+    case llvm::Mips::Val_GNU_MIPS_ABI_FP_64:
+      return "-mgp32 -mfp64";
+    case llvm::Mips::Val_GNU_MIPS_ABI_FP_64A:
+      return "-mgp32 -mfp64 -mno-odd-spreg";
+    default:
+      return "<unknown>";
+  }
+}
+
+bool MipsAbiFlags::merge(const Input& pInput, MipsAbiFlags& oldFlags,
+                         const MipsAbiFlags& newFlags) {
+  if (oldFlags.m_IsaLevel == 0) {
+    oldFlags = newFlags;
+    return true;
+  }
+
+  if (newFlags.m_IsaLevel > oldFlags.m_IsaLevel)
+    oldFlags.m_IsaLevel = newFlags.m_IsaLevel;
+
+  oldFlags.m_IsaRev = std::max(oldFlags.m_IsaRev, newFlags.m_IsaRev);
+  oldFlags.m_GprSize = std::max(oldFlags.m_GprSize, newFlags.m_GprSize);
+  oldFlags.m_Cpr1Size = std::max(oldFlags.m_Cpr1Size, newFlags.m_Cpr1Size);
+  oldFlags.m_Cpr2Size = std::max(oldFlags.m_Cpr2Size, newFlags.m_Cpr2Size);
+  oldFlags.m_Ases |= newFlags.m_Ases;
+  oldFlags.m_Flags1 |= newFlags.m_Flags1;
+
+  if (oldFlags.m_FpAbi == newFlags.m_FpAbi)
+    return true;
+
+  if (isFpGreater(newFlags.m_FpAbi, oldFlags.m_FpAbi)) {
+    oldFlags.m_FpAbi = newFlags.m_FpAbi;
+    return true;
+  }
+
+  if (isFpGreater(oldFlags.m_FpAbi, newFlags.m_FpAbi))
+    return true;
+
+  llvm::StringRef oldAbiName = getFpAbiName(oldFlags.m_FpAbi);
+  llvm::StringRef newAbiName = getFpAbiName(newFlags.m_FpAbi);
+  warning(diag::warn_Mips_fp_abi_incompatible) << oldAbiName << newAbiName
+                                               << pInput.name();
+  return false;
+}
+}  // namespace mcld
diff --git a/lib/Target/Mips/MipsAbiFlags.h b/lib/Target/Mips/MipsAbiFlags.h
new file mode 100644
index 0000000..4a55b4d
--- /dev/null
+++ b/lib/Target/Mips/MipsAbiFlags.h
@@ -0,0 +1,63 @@
+//===- MipsAbiFlags.h -----------------------------------------------------===//
+//
+//                     The MCLinker Project
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TARGET_MIPS_MIPSABIFLAGS_H_
+#define TARGET_MIPS_MIPSABIFLAGS_H_
+
+#include "mcld/Support/MemoryRegion.h"
+
+#include <llvm/ADT/Optional.h>
+#include <llvm/Object/ELFTypes.h>
+
+namespace mcld {
+
+class Input;
+class LDSection;
+
+/** \class MipsAbiFlags
+ *  \brief Representation of .MIPS.abiflags section.
+ */
+class MipsAbiFlags {
+ public:
+  /// size of underlaid ELF section structure
+  static uint64_t size();
+
+  /// write ELF section structure to the memory region
+  static uint64_t emit(const MipsAbiFlags& pInfo, MemoryRegion& pRegion);
+
+  /// fill the structure by the data from the input section
+  static bool fillBySection(const Input& pInput, const LDSection& pSection,
+                            MipsAbiFlags& mipsAbi);
+
+  /// fill the structure by the data from ELF header flags
+  static bool fillByElfFlags(const Input& pInput, uint64_t elfFlags,
+                             MipsAbiFlags& mipsAbi);
+
+  /// check compatibility between two structures
+  static bool isCompatible(const Input& pInput, const MipsAbiFlags& elf,
+                           const MipsAbiFlags& abi);
+
+  /// merge new abi settings to the old structure
+  static bool merge(const Input& pInput, MipsAbiFlags& oldFlags,
+                    const MipsAbiFlags& newFlags);
+
+ private:
+  uint32_t m_IsaLevel;
+  uint32_t m_IsaRev;
+  uint32_t m_IsaExt;
+  uint32_t m_GprSize;
+  uint32_t m_Cpr1Size;
+  uint32_t m_Cpr2Size;
+  uint32_t m_FpAbi;
+  uint32_t m_Ases;
+  uint32_t m_Flags1;
+};
+
+}  // namespace mcld
+
+#endif  // TARGET_MIPS_MIPSABIFLAGS_H_
diff --git a/lib/Target/Mips/MipsGNUInfo.cpp b/lib/Target/Mips/MipsGNUInfo.cpp
index 132e9c3..9a1804f 100644
--- a/lib/Target/Mips/MipsGNUInfo.cpp
+++ b/lib/Target/Mips/MipsGNUInfo.cpp
@@ -14,15 +14,15 @@
 // MipsGNUInfo
 //===----------------------------------------------------------------------===//
 MipsGNUInfo::MipsGNUInfo(const llvm::Triple& pTriple)
-    : GNUInfo(pTriple), m_ABIVersion(0), m_PICFlags(0) {
+    : GNUInfo(pTriple), m_ABIVersion(0), m_ElfFlags(0) {
 }
 
 void MipsGNUInfo::setABIVersion(uint8_t ver) {
   m_ABIVersion = ver;
 }
 
-void MipsGNUInfo::setPICFlags(uint64_t flags) {
-  m_PICFlags = flags;
+void MipsGNUInfo::setElfFlags(uint64_t flags) {
+  m_ElfFlags = flags;
 }
 
 uint32_t MipsGNUInfo::machine() const {
@@ -41,14 +41,7 @@
 }
 
 uint64_t MipsGNUInfo::flags() const {
-  uint64_t val = llvm::ELF::EF_MIPS_NOREORDER | m_PICFlags;
-
-  if (m_Triple.isArch32Bit())
-    val |= llvm::ELF::EF_MIPS_ARCH_32R2 | llvm::ELF::EF_MIPS_ABI_O32;
-  else
-    val |= llvm::ELF::EF_MIPS_ARCH_64R2;
-
-  return val;
+  return m_ElfFlags;
 }
 
 const char* MipsGNUInfo::entry() const {
diff --git a/lib/Target/Mips/MipsGNUInfo.h b/lib/Target/Mips/MipsGNUInfo.h
index 673c39a..9ab907f 100644
--- a/lib/Target/Mips/MipsGNUInfo.h
+++ b/lib/Target/Mips/MipsGNUInfo.h
@@ -18,7 +18,7 @@
   explicit MipsGNUInfo(const llvm::Triple& pTriple);
 
   void setABIVersion(uint8_t ver);
-  void setPICFlags(uint64_t flags);
+  void setElfFlags(uint64_t flags);
 
   // GNUInfo
   uint32_t machine() const;
@@ -31,7 +31,7 @@
 
  private:
   uint8_t m_ABIVersion;
-  uint64_t m_PICFlags;
+  uint64_t m_ElfFlags;
 };
 
 }  // namespace mcld
diff --git a/lib/Target/Mips/MipsGOT.cpp b/lib/Target/Mips/MipsGOT.cpp
index 85d89c7..4234e87 100644
--- a/lib/Target/Mips/MipsGOT.cpp
+++ b/lib/Target/Mips/MipsGOT.cpp
@@ -33,14 +33,19 @@
 MipsGOT::GOTMultipart::GOTMultipart(size_t local, size_t global)
     : m_LocalNum(local),
       m_GlobalNum(global),
+      m_TLSNum(0),
+      m_TLSDynNum(0),
       m_ConsumedLocal(0),
       m_ConsumedGlobal(0),
+      m_ConsumedTLS(0),
       m_pLastLocal(NULL),
-      m_pLastGlobal(NULL) {
+      m_pLastGlobal(NULL),
+      m_pLastTLS(NULL) {
 }
 
 bool MipsGOT::GOTMultipart::isConsumed() const {
-  return m_LocalNum == m_ConsumedLocal && m_GlobalNum == m_ConsumedGlobal;
+  return m_LocalNum == m_ConsumedLocal && m_GlobalNum == m_ConsumedGlobal &&
+         m_TLSNum == m_ConsumedTLS;
 }
 
 void MipsGOT::GOTMultipart::consumeLocal() {
@@ -56,6 +61,13 @@
   m_pLastGlobal = m_pLastGlobal->getNextNode();
 }
 
+void MipsGOT::GOTMultipart::consumeTLS(Relocation::Type pType) {
+  assert(m_ConsumedTLS < m_TLSNum &&
+         "Consumed too many TLS GOT entries");
+  m_ConsumedTLS += pType == llvm::ELF::R_MIPS_TLS_GOTTPREL ? 1 : 2;
+  m_pLastTLS = m_pLastTLS->getNextNode();
+}
+
 //===----------------------------------------------------------------------===//
 // MipsGOT::LocalEntry
 //===----------------------------------------------------------------------===//
@@ -79,7 +91,11 @@
 // MipsGOT
 //===----------------------------------------------------------------------===//
 MipsGOT::MipsGOT(LDSection& pSection)
-    : GOT(pSection), m_pInput(NULL), m_CurrentGOTPart(0) {
+    : GOT(pSection),
+      m_pInput(NULL),
+      m_HasTLSLdmSymbol(false),
+      m_CurrentGOTPart(0),
+      m_GotTLSLdmEntry(nullptr) {
 }
 
 uint64_t MipsGOT::getGPDispAddress() const {
@@ -108,6 +124,8 @@
     reserve(it->m_LocalNum);
     it->m_pLastGlobal = &m_SectionData->back();
     reserve(it->m_GlobalNum);
+    it->m_pLastTLS = &m_SectionData->back();
+    reserve(it->m_TLSNum);
 
     if (it == m_MultipartList.begin()) {
       // Reserve entries in the second part of the primary GOT.
@@ -122,6 +140,9 @@
       for (size_t i = 0; i < count; ++i)
         pRelDyn.reserveEntry();
     }
+
+    for (size_t i = 0; i < it->m_TLSDynNum; ++i)
+      pRelDyn.reserveEntry();
   }
 }
 
@@ -147,6 +168,8 @@
   m_InputGlobalSymbols.clear();
   m_MergedLocalSymbols.clear();
   m_InputLocalSymbols.clear();
+  m_InputTLSGdSymbols.clear();
+  m_HasTLSLdmSymbol = false;
 }
 
 void MipsGOT::changeInput() {
@@ -262,6 +285,39 @@
   return true;
 }
 
+bool MipsGOT::reserveTLSGdEntry(ResolveInfo& pInfo) {
+  if (m_InputTLSGdSymbols.count(&pInfo))
+    return false;
+
+  m_InputTLSGdSymbols.insert(&pInfo);
+  m_MultipartList.back().m_TLSNum += 2;
+  m_MultipartList.back().m_TLSDynNum += 2;
+
+  return true;
+}
+
+bool MipsGOT::reserveTLSLdmEntry() {
+  if (m_HasTLSLdmSymbol)
+    return false;
+
+  m_HasTLSLdmSymbol = true;
+  m_MultipartList.back().m_TLSNum += 2;
+  m_MultipartList.back().m_TLSDynNum += 1;
+
+  return true;
+}
+
+bool MipsGOT::reserveTLSGotEntry(ResolveInfo& pInfo) {
+  if (m_InputTLSGotSymbols.count(&pInfo))
+    return false;
+
+  m_InputTLSGotSymbols.insert(&pInfo);
+  m_MultipartList.back().m_TLSNum += 1;
+  m_MultipartList.back().m_TLSDynNum += 1;
+
+  return true;
+}
+
 bool MipsGOT::isPrimaryGOTConsumed() {
   return m_CurrentGOTPart > 0;
 }
@@ -290,6 +346,18 @@
   return m_MultipartList[m_CurrentGOTPart].m_pLastGlobal;
 }
 
+Fragment* MipsGOT::consumeTLS(Relocation::Type pType) {
+  assert(m_CurrentGOTPart < m_MultipartList.size() &&
+         "GOT number is out of range!");
+
+  if (m_MultipartList[m_CurrentGOTPart].isConsumed())
+    ++m_CurrentGOTPart;
+
+  m_MultipartList[m_CurrentGOTPart].consumeTLS(pType);
+
+  return m_MultipartList[m_CurrentGOTPart].m_pLastTLS;
+}
+
 uint64_t MipsGOT::getGPAddr(const Input& pInput) const {
   uint64_t gotSize = 0;
   for (MultipartListType::const_iterator it = m_MultipartList.begin(),
@@ -333,6 +401,50 @@
   return it->second;
 }
 
+void MipsGOT::recordTLSEntry(const ResolveInfo* pInfo, Fragment* pEntry,
+                             Relocation::Type pType) {
+  if (pType == llvm::ELF::R_MIPS_TLS_LDM) {
+    m_GotTLSLdmEntry = pEntry;
+  } else if (pType == llvm::ELF::R_MIPS_TLS_GD) {
+    GotEntryKey key;
+    key.m_GOTPage = m_CurrentGOTPart;
+    key.m_pInfo = pInfo;
+    key.m_Addend = 0;
+    m_GotTLSGdEntriesMap[key] = pEntry;
+  } else if (pType == llvm::ELF::R_MIPS_TLS_GOTTPREL) {
+    GotEntryKey key;
+    key.m_GOTPage = m_CurrentGOTPart;
+    key.m_pInfo = pInfo;
+    key.m_Addend = 0;
+    m_GotTLSGotEntriesMap[key] = pEntry;
+  } else {
+    llvm_unreachable("Unexpected relocation");
+  }
+}
+
+Fragment* MipsGOT::lookupTLSEntry(const ResolveInfo* pInfo,
+                                  Relocation::Type pType) {
+  if (pType == llvm::ELF::R_MIPS_TLS_LDM)
+    return m_GotTLSLdmEntry;
+  if (pType == llvm::ELF::R_MIPS_TLS_GD) {
+    GotEntryKey key;
+    key.m_GOTPage = m_CurrentGOTPart;
+    key.m_pInfo = pInfo;
+    key.m_Addend = 0;
+    GotEntryMapType::iterator it = m_GotTLSGdEntriesMap.find(key);
+    return it == m_GotTLSGdEntriesMap.end() ? nullptr : it->second;
+  }
+  if (pType == llvm::ELF::R_MIPS_TLS_GOTTPREL) {
+    GotEntryKey key;
+    key.m_GOTPage = m_CurrentGOTPart;
+    key.m_pInfo = pInfo;
+    key.m_Addend = 0;
+    GotEntryMapType::iterator it = m_GotTLSGotEntriesMap.find(key);
+    return it == m_GotTLSGotEntriesMap.end() ? nullptr : it->second;
+  }
+  llvm_unreachable("Unexpected relocation");
+}
+
 void MipsGOT::recordLocalEntry(const ResolveInfo* pInfo,
                                Relocation::DWord pAddend,
                                Fragment* pEntry) {
diff --git a/lib/Target/Mips/MipsGOT.h b/lib/Target/Mips/MipsGOT.h
index 409347b..fc0e540 100644
--- a/lib/Target/Mips/MipsGOT.h
+++ b/lib/Target/Mips/MipsGOT.h
@@ -50,6 +50,9 @@
                          int reloc,
                          Relocation::DWord pAddend);
   bool reserveGlobalEntry(ResolveInfo& pInfo);
+  bool reserveTLSGdEntry(ResolveInfo& pInfo);
+  bool reserveTLSGotEntry(ResolveInfo& pInfo);
+  bool reserveTLSLdmEntry();
 
   size_t getLocalNum() const;   ///< number of local symbols in primary GOT
   size_t getGlobalNum() const;  ///< total number of global symbols
@@ -58,6 +61,7 @@
 
   Fragment* consumeLocal();
   Fragment* consumeGlobal();
+  Fragment* consumeTLS(Relocation::Type pType);
 
   uint64_t getGPAddr(const Input& pInput) const;
   uint64_t getGPRelOffset(const Input& pInput, const Fragment& pEntry) const;
@@ -65,6 +69,10 @@
   void recordGlobalEntry(const ResolveInfo* pInfo, Fragment* pEntry);
   Fragment* lookupGlobalEntry(const ResolveInfo* pInfo);
 
+  void recordTLSEntry(const ResolveInfo* pInfo, Fragment* pEntry,
+                      Relocation::Type pType);
+  Fragment* lookupTLSEntry(const ResolveInfo* pInfo, Relocation::Type pType);
+
   void recordLocalEntry(const ResolveInfo* pInfo,
                         Relocation::DWord pAddend,
                         Fragment* pEntry);
@@ -103,12 +111,16 @@
 
     size_t m_LocalNum;   ///< number of reserved local entries
     size_t m_GlobalNum;  ///< number of reserved global entries
+    size_t m_TLSNum;     ///< number of reserved TLS entries
+    size_t m_TLSDynNum;  ///< number of reserved TLS related dynamic relocations
 
     size_t m_ConsumedLocal;   ///< consumed local entries
     size_t m_ConsumedGlobal;  ///< consumed global entries
+    size_t m_ConsumedTLS;     ///< consumed TLS entries
 
     Fragment* m_pLastLocal;   ///< the last consumed local entry
     Fragment* m_pLastGlobal;  ///< the last consumed global entry
+    Fragment* m_pLastTLS;     ///< the last consumed TLS entry
 
     InputSetType m_Inputs;
 
@@ -116,6 +128,7 @@
 
     void consumeLocal();
     void consumeGlobal();
+    void consumeTLS(Relocation::Type pType);
   };
 
   /** \class LocalEntry
@@ -153,6 +166,12 @@
   SymbolSetType m_MergedGlobalSymbols;
   // Global symbols from the current input.
   SymbolUniqueMapType m_InputGlobalSymbols;
+  // Set of symbols referenced by TLS GD relocations.
+  SymbolSetType m_InputTLSGdSymbols;
+  // Set of symbols referenced by TLS GOTTPREL relocation.
+  SymbolSetType m_InputTLSGotSymbols;
+  // There is a symbol referenced by TLS LDM relocations.
+  bool m_HasTLSLdmSymbol;
   // Local symbols merged to the current GOT
   // except symbols from the current input.
   LocalSymbolSetType m_MergedLocalSymbols;
@@ -191,6 +210,9 @@
   typedef std::map<GotEntryKey, Fragment*> GotEntryMapType;
   GotEntryMapType m_GotLocalEntriesMap;
   GotEntryMapType m_GotGlobalEntriesMap;
+  GotEntryMapType m_GotTLSGdEntriesMap;
+  GotEntryMapType m_GotTLSGotEntriesMap;
+  Fragment* m_GotTLSLdmEntry;
 };
 
 /** \class Mips32GOT
diff --git a/lib/Target/Mips/MipsGOTPLT.cpp b/lib/Target/Mips/MipsGOTPLT.cpp
index 745b258..dab4be5 100644
--- a/lib/Target/Mips/MipsGOTPLT.cpp
+++ b/lib/Target/Mips/MipsGOTPLT.cpp
@@ -25,12 +25,6 @@
   // Create header's entries.
   new GOTPLTEntry(0, m_SectionData);
   new GOTPLTEntry(0, m_SectionData);
-  m_Last = ++m_SectionData->begin();
-}
-
-void MipsGOTPLT::reserve(size_t pNum) {
-  for (size_t i = 0; i < pNum; ++i)
-    new GOTPLTEntry(0, m_SectionData);
 }
 
 uint64_t MipsGOTPLT::emit(MemoryRegion& pRegion) {
@@ -45,11 +39,8 @@
   return result;
 }
 
-Fragment* MipsGOTPLT::consume() {
-  ++m_Last;
-  assert(m_Last != m_SectionData->end() &&
-         "There is no reserved GOTPLT entries");
-  return &(*m_Last);
+Fragment* MipsGOTPLT::create() {
+  return new GOTPLTEntry(0, m_SectionData);
 }
 
 bool MipsGOTPLT::hasGOT1() const {
diff --git a/lib/Target/Mips/MipsGOTPLT.h b/lib/Target/Mips/MipsGOTPLT.h
index f5cd909..b585707 100644
--- a/lib/Target/Mips/MipsGOTPLT.h
+++ b/lib/Target/Mips/MipsGOTPLT.h
@@ -31,17 +31,9 @@
 
   uint64_t emit(MemoryRegion& pRegion);
 
-  Fragment* consume();
+  Fragment* create();
 
   void applyAllGOTPLT(uint64_t pltAddr);
-
- public:
-  // GOT
-  void reserve(size_t pNum = 1);
-
- private:
-  // the last consumed entry.
-  SectionData::iterator m_Last;
 };
 
 }  // namespace mcld
diff --git a/lib/Target/Mips/MipsLA25Stub.cpp b/lib/Target/Mips/MipsLA25Stub.cpp
index 3127b37..9bcf85f 100644
--- a/lib/Target/Mips/MipsLA25Stub.cpp
+++ b/lib/Target/Mips/MipsLA25Stub.cpp
@@ -19,13 +19,6 @@
     0x00000000   // nop
 };
 
-enum {
-  // Fake relocations for patching LA25 stubs.
-  R_MIPS_LA25_LUI = 200,
-  R_MIPS_LA25_J = 201,
-  R_MIPS_LA25_ADD = 202
-};
-
 }  // anonymous namespace
 
 namespace mcld {
@@ -39,9 +32,9 @@
       m_Name("MipsLA25_Prototype"),
       m_pData(STUB),
       m_Size(sizeof(STUB)) {
-  addFixup(0, 0x0, R_MIPS_LA25_LUI);
-  addFixup(4, 0x0, R_MIPS_LA25_J);
-  addFixup(8, 0x0, R_MIPS_LA25_ADD);
+  addFixup(0, 0x0, llvm::ELF::R_MIPS_HI16);
+  addFixup(4, 0x0, llvm::ELF::R_MIPS_26);
+  addFixup(8, 0x0, llvm::ELF::R_MIPS_LO16);
 }
 
 MipsLA25Stub::MipsLA25Stub(const MipsGNULDBackend& pTarget,
diff --git a/lib/Target/Mips/MipsLDBackend.cpp b/lib/Target/Mips/MipsLDBackend.cpp
index c629edd..d4e8145 100644
--- a/lib/Target/Mips/MipsLDBackend.cpp
+++ b/lib/Target/Mips/MipsLDBackend.cpp
@@ -16,11 +16,14 @@
 #include "mcld/IRBuilder.h"
 #include "mcld/LinkerConfig.h"
 #include "mcld/Module.h"
+#include "mcld/Fragment/AlignFragment.h"
 #include "mcld/Fragment/FillFragment.h"
 #include "mcld/LD/BranchIslandFactory.h"
 #include "mcld/LD/LDContext.h"
 #include "mcld/LD/StubFactory.h"
 #include "mcld/LD/ELFFileFormat.h"
+#include "mcld/LD/ELFSegment.h"
+#include "mcld/LD/ELFSegmentFactory.h"
 #include "mcld/MC/Attribute.h"
 #include "mcld/Object/ObjectBuilder.h"
 #include "mcld/Support/MemoryRegion.h"
@@ -30,9 +33,13 @@
 #include "mcld/Target/OutputRelocSection.h"
 
 #include <llvm/ADT/Triple.h>
+#include <llvm/Object/ELFTypes.h>
 #include <llvm/Support/Casting.h>
 #include <llvm/Support/ELF.h>
 #include <llvm/Support/Host.h>
+#include <llvm/Support/MipsABIFlags.h>
+
+#include <vector>
 
 namespace mcld {
 
@@ -50,6 +57,7 @@
       m_pRelPlt(NULL),
       m_pRelDyn(NULL),
       m_pDynamic(NULL),
+      m_pAbiFlags(NULL),
       m_pGOTSymbol(NULL),
       m_pPLTSymbol(NULL),
       m_pGpDispSymbol(NULL) {
@@ -99,6 +107,17 @@
   // initialize .rel.dyn
   LDSection& reldyn = file_format->getRelDyn();
   m_pRelDyn = new OutputRelocSection(pModule, reldyn);
+
+  // initialize .sdata
+  m_psdata = pBuilder.CreateSection(
+      ".sdata", LDFileFormat::Target, llvm::ELF::SHT_PROGBITS,
+      llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE | llvm::ELF::SHF_MIPS_GPREL,
+      4);
+
+  // initialize .MIPS.abiflags
+  m_pAbiFlags = pBuilder.CreateSection(".MIPS.abiflags", LDFileFormat::Target,
+                                       llvm::ELF::SHT_MIPS_ABIFLAGS,
+                                       llvm::ELF::SHF_ALLOC, 4);
 }
 
 void MipsGNULDBackend::initTargetSymbols(IRBuilder& pBuilder, Module& pModule) {
@@ -163,6 +182,9 @@
   if (!config().isCodeStatic() && m_pDynamic == NULL)
     m_pDynamic = new MipsELFDynamic(*this, config());
 
+  if (m_pAbiInfo.hasValue())
+    m_pAbiFlags->setSize(m_pAbiInfo->size());
+
   // set .got size
   // when building shared object, the .got section is must.
   if (LinkerConfig::Object != config().codeGenType()) {
@@ -219,18 +241,6 @@
   }
 
   m_pInfo.setABIVersion(m_pPLT && m_pPLT->hasPLT1() ? 1 : 0);
-
-  // FIXME: (simon) We need to iterate all input sections
-  // check that flags are consistent and merge them properly.
-  uint64_t picFlags = llvm::ELF::EF_MIPS_CPIC;
-  if (config().targets().triple().isArch64Bit()) {
-    picFlags |= llvm::ELF::EF_MIPS_PIC;
-  } else {
-    if (LinkerConfig::DynObj == config().codeGenType())
-      picFlags |= llvm::ELF::EF_MIPS_PIC;
-  }
-
-  m_pInfo.setPICFlags(picFlags);
 }
 
 /// dynamic - the dynamic section of the target machine.
@@ -265,6 +275,60 @@
     return m_pGOTPLT->emit(pRegion);
   }
 
+  if (&pSection == m_pAbiFlags && m_pAbiInfo.hasValue())
+    return MipsAbiFlags::emit(*m_pAbiInfo, pRegion);
+
+  if (&pSection == m_psdata && m_psdata->hasSectionData()) {
+    const SectionData* sect_data = pSection.getSectionData();
+    SectionData::const_iterator frag_iter, frag_end = sect_data->end();
+    uint8_t* out_offset = pRegion.begin();
+    for (frag_iter = sect_data->begin(); frag_iter != frag_end; ++frag_iter) {
+      size_t size = frag_iter->size();
+      switch (frag_iter->getKind()) {
+        case Fragment::Fillment: {
+          const FillFragment& fill_frag = llvm::cast<FillFragment>(*frag_iter);
+          if (fill_frag.getValueSize() == 0) {
+            // virtual fillment, ignore it.
+            break;
+          }
+          memset(out_offset, fill_frag.getValue(), fill_frag.size());
+          break;
+        }
+        case Fragment::Region: {
+          const RegionFragment& region_frag =
+              llvm::cast<RegionFragment>(*frag_iter);
+          const char* start = region_frag.getRegion().begin();
+          memcpy(out_offset, start, size);
+          break;
+        }
+        case Fragment::Alignment: {
+          const AlignFragment& align_frag =
+              llvm::cast<AlignFragment>(*frag_iter);
+          uint64_t count = size / align_frag.getValueSize();
+          switch (align_frag.getValueSize()) {
+            case 1u:
+              std::memset(out_offset, align_frag.getValue(), count);
+              break;
+            default:
+              llvm::report_fatal_error(
+                  "unsupported value size for align fragment emission yet.\n");
+              break;
+          }  // end switch
+          break;
+        }
+        case Fragment::Null: {
+          assert(0x0 == size);
+          break;
+        }
+        default:
+          llvm::report_fatal_error("unsupported fragment type.\n");
+          break;
+      }  // end switch
+      out_offset += size;
+    }
+    return pRegion.size();
+  }
+
   fatal(diag::unrecognized_output_sectoin) << pSection.name()
                                            << "mclinker@googlegroups.com";
   return 0;
@@ -335,10 +399,55 @@
 
 namespace mcld {
 
-bool MipsGNULDBackend::readSection(Input& pInput, SectionData& pSD) {
-  llvm::StringRef name(pSD.getSection().name());
+static const char* ArchName(uint64_t flagBits) {
+  switch (flagBits) {
+    case llvm::ELF::EF_MIPS_ARCH_1:
+      return "mips1";
+    case llvm::ELF::EF_MIPS_ARCH_2:
+      return "mips2";
+    case llvm::ELF::EF_MIPS_ARCH_3:
+      return "mips3";
+    case llvm::ELF::EF_MIPS_ARCH_4:
+      return "mips4";
+    case llvm::ELF::EF_MIPS_ARCH_5:
+      return "mips5";
+    case llvm::ELF::EF_MIPS_ARCH_32:
+      return "mips32";
+    case llvm::ELF::EF_MIPS_ARCH_64:
+      return "mips64";
+    case llvm::ELF::EF_MIPS_ARCH_32R2:
+      return "mips32r2";
+    case llvm::ELF::EF_MIPS_ARCH_64R2:
+      return "mips64r2";
+    case llvm::ELF::EF_MIPS_ARCH_32R6:
+      return "mips32r6";
+    case llvm::ELF::EF_MIPS_ARCH_64R6:
+      return "mips64r6";
+    default:
+      return "Unknown Arch";
+  }
+}
 
-  if (name.startswith(".sdata")) {
+void MipsGNULDBackend::mergeFlags(Input& pInput, const char* ELF_hdr) {
+  bool isTarget64Bit = config().targets().triple().isArch64Bit();
+  bool isInput64Bit = ELF_hdr[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS64;
+
+  if (isTarget64Bit != isInput64Bit) {
+    fatal(diag::error_Mips_incompatible_class)
+        << (isTarget64Bit ? "ELFCLASS64" : "ELFCLASS32")
+        << (isInput64Bit ? "ELFCLASS64" : "ELFCLASS32") << pInput.name();
+    return;
+  }
+
+  m_ElfFlagsMap[&pInput] =
+      isInput64Bit ?
+          reinterpret_cast<const llvm::ELF::Elf64_Ehdr*>(ELF_hdr)->e_flags :
+          reinterpret_cast<const llvm::ELF::Elf32_Ehdr*>(ELF_hdr)->e_flags;
+}
+
+bool MipsGNULDBackend::readSection(Input& pInput, SectionData& pSD) {
+  if ((pSD.getSection().flag() & llvm::ELF::SHF_MIPS_GPREL) ||
+      (pSD.getSection().type() == llvm::ELF::SHT_MIPS_ABIFLAGS)) {
     uint64_t offset = pInput.fileOffset() + pSD.getSection().offset();
     uint64_t size = pSD.getSection().size();
 
@@ -449,6 +558,12 @@
   if (file_format->hasPLT() && (&pSectHdr == &file_format->getPLT()))
     return SHO_PLT;
 
+  if (&pSectHdr == m_psdata)
+    return SHO_SMALL_DATA;
+
+  if (&pSectHdr == m_pAbiFlags)
+    return SHO_RO_NOTE;
+
   return SHO_UNDEFINED;
 }
 
@@ -558,6 +673,14 @@
   return true;
 }
 
+uint64_t MipsGNULDBackend::getTPOffset(const Input& pInput) const {
+  return m_TpOffsetMap.lookup(&pInput);
+}
+
+uint64_t MipsGNULDBackend::getDTPOffset(const Input& pInput) const {
+  return m_DtpOffsetMap.lookup(&pInput);
+}
+
 uint64_t MipsGNULDBackend::getGP0(const Input& pInput) const {
   return m_GP0Map.lookup(&pInput);
 }
@@ -620,7 +743,24 @@
 /// doCreateProgramHdrs - backend can implement this function to create the
 /// target-dependent segments
 void MipsGNULDBackend::doCreateProgramHdrs(Module& pModule) {
-  // TODO
+  if (!m_pAbiFlags || m_pAbiFlags->size() == 0)
+    return;
+
+  // create PT_MIPS_ABIFLAGS segment
+  ELFSegmentFactory::iterator sit =
+      elfSegmentTable().find(llvm::ELF::PT_INTERP, 0x0, 0x0);
+  if (sit == elfSegmentTable().end())
+    sit = elfSegmentTable().find(llvm::ELF::PT_PHDR, 0x0, 0x0);
+  if (sit == elfSegmentTable().end())
+    sit = elfSegmentTable().begin();
+  else
+    ++sit;
+
+  ELFSegment* abiSeg = elfSegmentTable().insert(sit,
+                                                llvm::ELF::PT_MIPS_ABIFLAGS,
+                                                llvm::ELF::PF_R);
+  abiSeg->setAlign(8);
+  abiSeg->append(m_pAbiFlags);
 }
 
 bool MipsGNULDBackend::relaxRelocation(IRBuilder& pBuilder, Relocation& pRel) {
@@ -640,6 +780,9 @@
     return false;
 
   assert(stub->symInfo() != NULL);
+  // reset the branch target of the reloc to this stub instead
+  pRel.setSymInfo(stub->symInfo());
+
   // increase the size of .symtab and .strtab
   LDSection& symtab = getOutputFormat()->getSymTab();
   LDSection& strtab = getOutputFormat()->getStrTab();
@@ -681,38 +824,65 @@
     }
   }
 
-  SectionData* textData = getOutputFormat()->getText().getSectionData();
-
   // find the first fragment w/ invalid offset due to stub insertion
-  Fragment* invalid = NULL;
+  std::vector<Fragment*> invalid_frags;
   pFinished = true;
   for (BranchIslandFactory::iterator ii = getBRIslandFactory()->begin(),
                                      ie = getBRIslandFactory()->end();
        ii != ie;
        ++ii) {
     BranchIsland& island = *ii;
-    if (island.end() == textData->end())
-      break;
+    if (island.size() > stubGroupSize()) {
+      error(diag::err_no_space_to_place_stubs) << stubGroupSize();
+      return false;
+    }
 
-    Fragment* exit = island.end();
+    if (island.numOfStubs() == 0) {
+      continue;
+    }
+
+    Fragment* exit = &*island.end();
+    if (exit == island.begin()->getParent()->end()) {
+      continue;
+    }
+
     if ((island.offset() + island.size()) > exit->getOffset()) {
-      invalid = exit;
-      pFinished = false;
-      break;
+      if (invalid_frags.empty() ||
+          (invalid_frags.back()->getParent() != island.getParent())) {
+        invalid_frags.push_back(exit);
+        pFinished = false;
+      }
+      continue;
     }
   }
 
   // reset the offset of invalid fragments
-  while (invalid != NULL) {
-    invalid->setOffset(invalid->getPrevNode()->getOffset() +
-                       invalid->getPrevNode()->size());
-    invalid = invalid->getNextNode();
+  for (auto it = invalid_frags.begin(), ie = invalid_frags.end(); it != ie;
+       ++it) {
+    Fragment* invalid = *it;
+    while (invalid != NULL) {
+      invalid->setOffset(invalid->getPrevNode()->getOffset() +
+                         invalid->getPrevNode()->size());
+      invalid = invalid->getNextNode();
+    }
   }
 
-  // reset the size of .text
-  if (isRelaxed)
-    getOutputFormat()->getText().setSize(textData->back().getOffset() +
-                                         textData->back().size());
+  // reset the size of section that has stubs inserted.
+  if (isRelaxed) {
+    SectionData* prev = NULL;
+    for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(),
+                                       island_end = getBRIslandFactory()->end();
+         island != island_end;
+         ++island) {
+      SectionData* sd = (*island).begin()->getParent();
+      if ((*island).numOfStubs() != 0) {
+        if (sd != prev) {
+          sd->getSection().setSize(sd->back().getOffset() + sd->back().size());
+        }
+      }
+      prev = sd;
+    }
+  }
 
   return isRelaxed;
 }
@@ -824,6 +994,253 @@
   pRel.r_addend = pAddend;
 }
 
+namespace {
+struct ISATreeEdge {
+  unsigned child;
+  unsigned parent;
+};
+}
+
+static ISATreeEdge isaTree[] = {
+    // MIPS32R6 and MIPS64R6 are not compatible with other extensions
+
+    // MIPS64 extensions.
+    {llvm::ELF::EF_MIPS_ARCH_64R2, llvm::ELF::EF_MIPS_ARCH_64},
+    // MIPS V extensions.
+    {llvm::ELF::EF_MIPS_ARCH_64, llvm::ELF::EF_MIPS_ARCH_5},
+    // MIPS IV extensions.
+    {llvm::ELF::EF_MIPS_ARCH_5, llvm::ELF::EF_MIPS_ARCH_4},
+    // MIPS III extensions.
+    {llvm::ELF::EF_MIPS_ARCH_4, llvm::ELF::EF_MIPS_ARCH_3},
+    // MIPS32 extensions.
+    {llvm::ELF::EF_MIPS_ARCH_32R2, llvm::ELF::EF_MIPS_ARCH_32},
+    // MIPS II extensions.
+    {llvm::ELF::EF_MIPS_ARCH_3, llvm::ELF::EF_MIPS_ARCH_2},
+    {llvm::ELF::EF_MIPS_ARCH_32, llvm::ELF::EF_MIPS_ARCH_2},
+    // MIPS I extensions.
+    {llvm::ELF::EF_MIPS_ARCH_2, llvm::ELF::EF_MIPS_ARCH_1},
+};
+
+static bool isIsaMatched(uint32_t base, uint32_t ext) {
+  if (base == ext)
+    return true;
+  if (base == llvm::ELF::EF_MIPS_ARCH_32 &&
+      isIsaMatched(llvm::ELF::EF_MIPS_ARCH_64, ext))
+    return true;
+  if (base == llvm::ELF::EF_MIPS_ARCH_32R2 &&
+      isIsaMatched(llvm::ELF::EF_MIPS_ARCH_64R2, ext))
+    return true;
+  for (const auto &edge : isaTree) {
+    if (ext == edge.child) {
+      ext = edge.parent;
+      if (ext == base)
+        return true;
+    }
+  }
+  return false;
+}
+
+static bool getAbiFlags(const Input& pInput, uint64_t elfFlags, bool& hasFlags,
+                        MipsAbiFlags& pFlags) {
+  MipsAbiFlags pElfFlags = {};
+  if (!MipsAbiFlags::fillByElfFlags(pInput, elfFlags, pElfFlags))
+    return false;
+
+  const LDContext* ctx = pInput.context();
+  for (auto it = ctx->sectBegin(), ie = ctx->sectEnd(); it != ie; ++it)
+    if ((*it)->type() == llvm::ELF::SHT_MIPS_ABIFLAGS) {
+      if (!MipsAbiFlags::fillBySection(pInput, **it, pFlags))
+        return false;
+      if (!MipsAbiFlags::isCompatible(pInput, pElfFlags, pFlags))
+        return false;
+      hasFlags = true;
+      return true;
+    }
+
+  pFlags = pElfFlags;
+  return true;
+}
+
+static const char* getNanName(uint64_t flags) {
+  return flags & llvm::ELF::EF_MIPS_NAN2008 ? "2008" : "legacy";
+}
+
+static bool mergeElfFlags(const Input& pInput, uint64_t& oldElfFlags,
+                          uint64_t newElfFlags) {
+  // PIC code is inherently CPIC and may not set CPIC flag explicitly.
+  // Ensure that this flag will exist in the linked file.
+  if (newElfFlags & llvm::ELF::EF_MIPS_PIC)
+    newElfFlags |= llvm::ELF::EF_MIPS_CPIC;
+
+  if (newElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE_M16) {
+    error(diag::error_Mips_m16_unsupported) << pInput.name();
+    return false;
+  }
+
+  if (!oldElfFlags) {
+    oldElfFlags = newElfFlags;
+    return true;
+  }
+
+  uint64_t newPic =
+      newElfFlags & (llvm::ELF::EF_MIPS_PIC | llvm::ELF::EF_MIPS_CPIC);
+  uint64_t oldPic =
+      oldElfFlags & (llvm::ELF::EF_MIPS_PIC | llvm::ELF::EF_MIPS_CPIC);
+
+  // Check PIC / CPIC flags compatibility.
+  if ((newPic != 0) != (oldPic != 0))
+    warning(diag::warn_Mips_abicalls_linking) << pInput.name();
+
+  if (!(newPic & llvm::ELF::EF_MIPS_PIC))
+    oldElfFlags &= ~llvm::ELF::EF_MIPS_PIC;
+  if (newPic)
+    oldElfFlags |= llvm::ELF::EF_MIPS_CPIC;
+
+  // Check ISA compatibility.
+  uint64_t newArch = newElfFlags & llvm::ELF::EF_MIPS_ARCH;
+  uint64_t oldArch = oldElfFlags & llvm::ELF::EF_MIPS_ARCH;
+  if (!isIsaMatched(newArch, oldArch)) {
+    if (!isIsaMatched(oldArch, newArch)) {
+      error(diag::error_Mips_inconsistent_arch)
+          << ArchName(oldArch) << ArchName(newArch) << pInput.name();
+      return false;
+    }
+    oldElfFlags &= ~llvm::ELF::EF_MIPS_ARCH;
+    oldElfFlags |= newArch;
+  }
+
+  // Check ABI compatibility.
+  uint32_t newAbi = newElfFlags & llvm::ELF::EF_MIPS_ABI;
+  uint32_t oldAbi = oldElfFlags & llvm::ELF::EF_MIPS_ABI;
+  if (newAbi != oldAbi && newAbi && oldAbi) {
+    error(diag::error_Mips_inconsistent_abi) << pInput.name();
+    return false;
+  }
+
+  // Check -mnan flags compatibility.
+  if ((newElfFlags & llvm::ELF::EF_MIPS_NAN2008) !=
+      (oldElfFlags & llvm::ELF::EF_MIPS_NAN2008)) {
+    // Linking -mnan=2008 and -mnan=legacy modules
+    error(diag::error_Mips_inconsistent_mnan)
+        << getNanName(oldElfFlags) << getNanName(newElfFlags) << pInput.name();
+    return false;
+  }
+
+  // Check ASE compatibility.
+  uint64_t newAse = newElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE;
+  uint64_t oldAse = oldElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE;
+  if (newAse != oldAse)
+    oldElfFlags |= newAse;
+
+  // Check FP64 compatibility.
+  if ((newElfFlags & llvm::ELF::EF_MIPS_FP64) !=
+      (oldElfFlags & llvm::ELF::EF_MIPS_FP64)) {
+    // Linking -mnan=2008 and -mnan=legacy modules
+    error(diag::error_Mips_inconsistent_fp64) << pInput.name();
+    return false;
+  }
+
+  oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_NOREORDER;
+  oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_MICROMIPS;
+  oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_NAN2008;
+  oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_32BITMODE;
+
+  return true;
+}
+
+void MipsGNULDBackend::saveTPOffset(const Input& pInput) {
+  const LDContext* ctx = pInput.context();
+  for (auto it = ctx->sectBegin(), ie = ctx->sectEnd(); it != ie; ++it) {
+    LDSection* sect = *it;
+    if (sect->flag() & llvm::ELF::SHF_TLS) {
+      m_TpOffsetMap[&pInput] = sect->addr() + 0x7000;
+      m_DtpOffsetMap[&pInput] = sect->addr() + 0x8000;
+      break;
+    }
+  }
+}
+
+void MipsGNULDBackend::preMergeSections(Module& pModule) {
+  uint64_t elfFlags = 0;
+  bool hasAbiFlags = false;
+  MipsAbiFlags abiFlags = {};
+  for (const Input *input : pModule.getObjectList()) {
+    if (input->type() != Input::Object)
+      continue;
+
+    uint64_t newElfFlags = m_ElfFlagsMap[input];
+
+    MipsAbiFlags newAbiFlags = {};
+    if (!getAbiFlags(*input, newElfFlags, hasAbiFlags, newAbiFlags))
+      continue;
+
+    if (!mergeElfFlags(*input, elfFlags, newElfFlags))
+      continue;
+
+    if (!MipsAbiFlags::merge(*input, abiFlags, newAbiFlags))
+      continue;
+
+    saveTPOffset(*input);
+  }
+
+  m_pInfo.setElfFlags(elfFlags);
+  if (hasAbiFlags)
+    m_pAbiInfo = abiFlags;
+}
+
+bool MipsGNULDBackend::mergeSection(Module& pModule, const Input& pInput,
+                                    LDSection& pSection) {
+  if (pSection.flag() & llvm::ELF::SHF_MIPS_GPREL) {
+    SectionData* sd = NULL;
+    if (!m_psdata->hasSectionData()) {
+      sd = IRBuilder::CreateSectionData(*m_psdata);
+      m_psdata->setSectionData(sd);
+    }
+    sd = m_psdata->getSectionData();
+    moveSectionData(*pSection.getSectionData(), *sd);
+  } else if (pSection.type() == llvm::ELF::SHT_MIPS_ABIFLAGS) {
+    // Nothing to do because we handle all .MIPS.abiflags sections
+    // in the preMergeSections method.
+  } else {
+    ObjectBuilder builder(pModule);
+    builder.MergeSection(pInput, pSection);
+  }
+  return true;
+}
+
+void MipsGNULDBackend::moveSectionData(SectionData& pFrom, SectionData& pTo) {
+  assert(&pFrom != &pTo && "Cannot move section data to itself!");
+
+  uint64_t offset = pTo.getSection().size();
+  AlignFragment* align = NULL;
+  if (pFrom.getSection().align() > 1) {
+    // if the align constraint is larger than 1, append an alignment
+    unsigned int alignment = pFrom.getSection().align();
+    align = new AlignFragment(/*alignment*/ alignment,
+                              /*the filled value*/ 0x0,
+                              /*the size of filled value*/ 1u,
+                              /*max bytes to emit*/ alignment - 1);
+    align->setOffset(offset);
+    align->setParent(&pTo);
+    pTo.getFragmentList().push_back(align);
+    offset += align->size();
+  }
+
+  // move fragments from pFrom to pTO
+  SectionData::FragmentListType& from_list = pFrom.getFragmentList();
+  SectionData::FragmentListType& to_list = pTo.getFragmentList();
+  SectionData::FragmentListType::iterator frag, fragEnd = from_list.end();
+  for (frag = from_list.begin(); frag != fragEnd; ++frag) {
+    frag->setParent(&pTo);
+    frag->setOffset(offset);
+    offset += frag->size();
+  }
+  to_list.splice(to_list.end(), from_list);
+
+  // set up pTo's header
+  pTo.getSection().setSize(offset);
+}
+
 //===----------------------------------------------------------------------===//
 // Mips32GNULDBackend
 //===----------------------------------------------------------------------===//
diff --git a/lib/Target/Mips/MipsLDBackend.h b/lib/Target/Mips/MipsLDBackend.h
index 2dbf8d1..92e9926 100644
--- a/lib/Target/Mips/MipsLDBackend.h
+++ b/lib/Target/Mips/MipsLDBackend.h
@@ -8,7 +8,9 @@
 //===----------------------------------------------------------------------===//
 #ifndef TARGET_MIPS_MIPSLDBACKEND_H_
 #define TARGET_MIPS_MIPSLDBACKEND_H_
+#include <llvm/Support/ELF.h>
 #include "mcld/Target/GNULDBackend.h"
+#include "MipsAbiFlags.h"
 #include "MipsELFDynamic.h"
 #include "MipsGOT.h"
 #include "MipsGOTPLT.h"
@@ -123,6 +125,14 @@
   /// sections.
   bool allocateCommonSymbols(Module& pModule);
 
+  /// getTPOffset - return TP_OFFSET against the SHF_TLS
+  /// section in the specified input.
+  uint64_t getTPOffset(const Input& pInput) const;
+
+  /// getDTPOffset - return DTP_OFFSET against the SHF_TLS
+  /// section in the specified input.
+  uint64_t getDTPOffset(const Input& pInput) const;
+
   /// getGP0 - the gp value used to create the relocatable objects
   /// in the specified input.
   uint64_t getGP0(const Input& pInput) const;
@@ -208,9 +218,19 @@
                       uint64_t pOffset,
                       int64_t pAddend) const;
 
+  /// preMergeSections - hooks to be executed before merging sections
+  void preMergeSections(Module& pModule);
+
+  /// mergeSection - merge target dependent sections
+  bool mergeSection(Module& pModule, const Input& pInput, LDSection& pSection);
+
+ protected:
+  virtual void mergeFlags(Input& pInput, const char* ELF_hdr);
+
  private:
   typedef llvm::DenseSet<const ResolveInfo*> ResolveInfoSetType;
-  typedef llvm::DenseMap<const Input*, llvm::ELF::Elf64_Addr> GP0MapType;
+  typedef llvm::DenseMap<const Input*, llvm::ELF::Elf64_Addr> InputNumMapType;
+  typedef llvm::DenseMap<const Input*, uint64_t> ElfFlagsMapType;
 
  protected:
   Relocator* m_pRelocator;
@@ -220,18 +240,27 @@
 
  private:
   MipsGNUInfo& m_pInfo;
+  llvm::Optional<MipsAbiFlags> m_pAbiInfo;
 
   OutputRelocSection* m_pRelPlt;  // .rel.plt
   OutputRelocSection* m_pRelDyn;  // .rel.dyn
 
   MipsELFDynamic* m_pDynamic;
+  LDSection* m_psdata;
+  LDSection* m_pAbiFlags;
   LDSymbol* m_pGOTSymbol;
   LDSymbol* m_pPLTSymbol;
   LDSymbol* m_pGpDispSymbol;
 
   SymbolListType m_GlobalGOTSyms;
   ResolveInfoSetType m_HasNonPICBranchSyms;
-  GP0MapType m_GP0Map;
+  InputNumMapType m_GP0Map;
+  InputNumMapType m_TpOffsetMap;
+  InputNumMapType m_DtpOffsetMap;
+  ElfFlagsMapType m_ElfFlagsMap;
+
+  void moveSectionData(SectionData& pFrom, SectionData& pTo);
+  void saveTPOffset(const Input& pInput);
 };
 
 /** \class Mips32GNULDBackend
diff --git a/lib/Target/Mips/MipsPLT.cpp b/lib/Target/Mips/MipsPLT.cpp
index aef25f9..c0a02f6 100644
--- a/lib/Target/Mips/MipsPLT.cpp
+++ b/lib/Target/Mips/MipsPLT.cpp
@@ -57,7 +57,6 @@
 //===----------------------------------------------------------------------===//
 MipsPLT::MipsPLT(LDSection& pSection) : PLT(pSection) {
   new MipsPLT0(*m_pSectionData);
-  m_Last = m_pSectionData->begin();
 }
 
 void MipsPLT::finalizeSectionSize() {
@@ -94,20 +93,8 @@
   return result;
 }
 
-void MipsPLT::reserveEntry(size_t pNum) {
-  for (size_t i = 0; i < pNum; ++i) {
-    Fragment* entry = new (std::nothrow) MipsPLTA(*m_pSectionData);
-
-    if (entry == NULL)
-      fatal(diag::fail_allocate_memory_plt);
-  }
-}
-
-Fragment* MipsPLT::consume() {
-  ++m_Last;
-  assert(m_Last != m_pSectionData->end() &&
-         "The number of PLT Entries and ResolveInfo doesn't match");
-  return &(*m_Last);
+PLTEntryBase* MipsPLT::create() {
+  return new MipsPLTA(*m_pSectionData);
 }
 
 void MipsPLT::applyAllPLT(MipsGOTPLT& pGOTPLT) {
diff --git a/lib/Target/Mips/MipsPLT.h b/lib/Target/Mips/MipsPLT.h
index 7c26b8e..a509e0b 100644
--- a/lib/Target/Mips/MipsPLT.h
+++ b/lib/Target/Mips/MipsPLT.h
@@ -26,23 +26,16 @@
  public:
   explicit MipsPLT(LDSection& pSection);
 
+  void finalizeSectionSize();
+
   // hasPLT1 - return if this PLT has any PLTA/PLTB entries
   bool hasPLT1() const;
 
   uint64_t emit(MemoryRegion& pRegion);
 
-  Fragment* consume();
+  PLTEntryBase* create();
 
   void applyAllPLT(MipsGOTPLT& pGOTPLT);
-
- public:
-  // PLT
-  void reserveEntry(size_t pNum = 1);
-  void finalizeSectionSize();
-
- private:
-  // the last consumed entry.
-  SectionData::iterator m_Last;
 };
 
 }  // namespace mcld
diff --git a/lib/Target/Mips/MipsRelocationFunctions.h b/lib/Target/Mips/MipsRelocationFunctions.h
index 06c9a0e..c11e67f 100644
--- a/lib/Target/Mips/MipsRelocationFunctions.h
+++ b/lib/Target/Mips/MipsRelocationFunctions.h
@@ -21,6 +21,7 @@
   DECL_MIPS_APPLY_RELOC_FUNC(lo16)    \
   DECL_MIPS_APPLY_RELOC_FUNC(gprel16) \
   DECL_MIPS_APPLY_RELOC_FUNC(got16)   \
+  DECL_MIPS_APPLY_RELOC_FUNC(pc16)    \
   DECL_MIPS_APPLY_RELOC_FUNC(call16)  \
   DECL_MIPS_APPLY_RELOC_FUNC(gprel32) \
   DECL_MIPS_APPLY_RELOC_FUNC(abs64)   \
@@ -30,10 +31,16 @@
   DECL_MIPS_APPLY_RELOC_FUNC(gotlo16) \
   DECL_MIPS_APPLY_RELOC_FUNC(sub)     \
   DECL_MIPS_APPLY_RELOC_FUNC(jalr)    \
-  DECL_MIPS_APPLY_RELOC_FUNC(la25lui) \
-  DECL_MIPS_APPLY_RELOC_FUNC(la25j)   \
-  DECL_MIPS_APPLY_RELOC_FUNC(la25add) \
   DECL_MIPS_APPLY_RELOC_FUNC(pc32)    \
+  DECL_MIPS_APPLY_RELOC_FUNC(pc18_s3) \
+  DECL_MIPS_APPLY_RELOC_FUNC(pc21_s2) \
+  DECL_MIPS_APPLY_RELOC_FUNC(pc19_s2) \
+  DECL_MIPS_APPLY_RELOC_FUNC(pc26_s2) \
+  DECL_MIPS_APPLY_RELOC_FUNC(pchi16) \
+  DECL_MIPS_APPLY_RELOC_FUNC(pclo16) \
+  DECL_MIPS_APPLY_RELOC_FUNC(tlshi16) \
+  DECL_MIPS_APPLY_RELOC_FUNC(tlslo16) \
+  DECL_MIPS_APPLY_RELOC_FUNC(tlsgot)  \
   DECL_MIPS_APPLY_RELOC_FUNC(unsupported)
 
 #define DECL_MIPS_APPLY_RELOC_FUNC_PTRS \
@@ -47,7 +54,7 @@
   { &gprel16,       7, "R_MIPS_GPREL16",              16}, \
   { &unsupported,   8, "R_MIPS_LITERAL",              16}, \
   { &got16,         9, "R_MIPS_GOT16",                16}, \
-  { &unsupported,  10, "R_MIPS_PC16",                 16}, \
+  { &pc16,         10, "R_MIPS_PC16",                 16}, \
   { &call16,       11, "R_MIPS_CALL16",               16}, \
   { &gprel32,      12, "R_MIPS_GPREL32",              32}, \
   { &none,         13, "R_MIPS_UNUSED1",               0}, \
@@ -79,15 +86,15 @@
   { &unsupported,  39, "R_MIPS_TLS_DTPREL32",         32}, \
   { &unsupported,  40, "R_MIPS_TLS_DTPMOD64",          0}, \
   { &unsupported,  41, "R_MIPS_TLS_DTPREL64",          0}, \
-  { &unsupported,  42, "R_MIPS_TLS_GD",               16}, \
-  { &unsupported,  43, "R_MIPS_TLS_LDM",              16}, \
-  { &unsupported,  44, "R_MIPS_TLS_DTPREL_HI16",      16}, \
-  { &unsupported,  45, "R_MIPS_TLS_DTPREL_LO16",      16}, \
-  { &unsupported,  46, "R_MIPS_TLS_GOTTPREL",         16}, \
+  { &tlsgot,       42, "R_MIPS_TLS_GD",               16}, \
+  { &tlsgot,       43, "R_MIPS_TLS_LDM",              16}, \
+  { &tlshi16,      44, "R_MIPS_TLS_DTPREL_HI16",      16}, \
+  { &tlslo16,      45, "R_MIPS_TLS_DTPREL_LO16",      16}, \
+  { &tlsgot,       46, "R_MIPS_TLS_GOTTPREL",         16}, \
   { &unsupported,  47, "R_MIPS_TLS_TPREL32",          32}, \
   { &unsupported,  48, "R_MIPS_TLS_TPREL64",           0}, \
-  { &unsupported,  49, "R_MIPS_TLS_TPREL_HI16",       16}, \
-  { &unsupported,  50, "R_MIPS_TLS_TPREL_LO16",       16}, \
+  { &tlshi16,      49, "R_MIPS_TLS_TPREL_HI16",       16}, \
+  { &tlslo16,      50, "R_MIPS_TLS_TPREL_LO16",       16}, \
   { &unsupported,  51, "R_MIPS_GLOB_DAT",              0}, \
   { &unsupported,  52, "",                             0}, \
   { &unsupported,  53, "",                             0}, \
@@ -97,12 +104,12 @@
   { &unsupported,  57, "",                             0}, \
   { &unsupported,  58, "",                             0}, \
   { &unsupported,  59, "",                             0}, \
-  { &unsupported,  60, "",                             0}, \
-  { &unsupported,  61, "",                             0}, \
-  { &unsupported,  62, "",                             0}, \
-  { &unsupported,  63, "",                             0}, \
-  { &unsupported,  64, "",                             0}, \
-  { &unsupported,  65, "",                             0}, \
+  { &pc21_s2,      60, "R_MIPS_PC21_S2",              21}, \
+  { &pc26_s2,      61, "R_MIPS_PC26_S2",              26}, \
+  { &pc18_s3,      62, "R_MIPS_PC18_S3",              18}, \
+  { &pc19_s2,      63, "R_MIPS_PC19_S2",              19}, \
+  { &pchi16,       64, "R_MIPS_PCHI16",               16}, \
+  { &pclo16,       65, "R_MIPS_PCLO16",               16}, \
   { &unsupported,  66, "",                             0}, \
   { &unsupported,  67, "",                             0}, \
   { &unsupported,  68, "",                             0}, \
@@ -237,9 +244,9 @@
   { &unsupported, 197, "",                             0}, \
   { &unsupported, 198, "",                             0}, \
   { &unsupported, 199, "",                             0}, \
-  { &la25lui,     200, "R_MIPS_LA25_LUI",             16}, \
-  { &la25j,       201, "R_MIPS_LA25_J",               26}, \
-  { &la25add,     202, "R_MIPS_LA25_ADD",             16}, \
+  { &unsupported, 200, "",                             0}, \
+  { &unsupported, 201, "",                             0}, \
+  { &unsupported, 202, "",                             0}, \
   { &unsupported, 203, "",                             0}, \
   { &unsupported, 204, "",                             0}, \
   { &unsupported, 205, "",                             0}, \
@@ -285,7 +292,7 @@
   { &unsupported, 245, "",                             0}, \
   { &unsupported, 246, "",                             0}, \
   { &unsupported, 247, "",                             0}, \
-  { &pc32,        248, "R_MIPS_PC32",                  0}, \
+  { &pc32,        248, "R_MIPS_PC32",                 32}, \
   { &unsupported, 249, "",                             0}, \
   { &unsupported, 250, "R_MIPS_GNU_REL16_S2",          0}, \
   { &unsupported, 251, "",                             0}, \
diff --git a/lib/Target/Mips/MipsRelocator.cpp b/lib/Target/Mips/MipsRelocator.cpp
index 701a876..0f39a11 100644
--- a/lib/Target/Mips/MipsRelocator.cpp
+++ b/lib/Target/Mips/MipsRelocator.cpp
@@ -19,19 +19,6 @@
 #include <llvm/ADT/Twine.h>
 #include <llvm/Support/ELF.h>
 
-namespace llvm {
-namespace ELF {
-
-// FIXME: Consider upstream these relocation types to LLVM.
-enum {
-  R_MIPS_LA25_LUI = 200,
-  R_MIPS_LA25_J = 201,
-  R_MIPS_LA25_ADD = 202,
-};
-
-}  // namespace ELF
-}  // namespace llvm
-
 namespace mcld {
 
 //===----------------------------------------------------------------------===//
@@ -56,22 +43,16 @@
   MipsRelocationInfo(Relocation& pParent, bool pIsRel)
       : m_Parent(&pParent),
         m_Type(pParent.type()),
-        m_Addend(0),
+        m_Addend(pIsRel ? pParent.target() : pParent.addend()),
         m_Symbol(pParent.symValue()),
-        m_Result(pParent.target()) {
-    if (pIsRel && (type() < llvm::ELF::R_MIPS_LA25_LUI ||
-                   type() > llvm::ELF::R_MIPS_LA25_ADD))
-      m_Addend = pParent.target();
-    else
-      m_Addend = pParent.addend();
-  }
+        m_Result(pParent.target()) {}
 
   bool isNone() const { return llvm::ELF::R_MIPS_NONE == type(); }
-
+  bool isFirst() const { return type() == (parent().type() & 0xff); }
   bool isLast() const { return llvm::ELF::R_MIPS_NONE == (m_Type >> 8); }
 
   MipsRelocationInfo next() const {
-    return MipsRelocationInfo(*m_Parent, m_Type >> 8, result(), result(), 0);
+    return MipsRelocationInfo(*m_Parent, m_Type >> 8, result(), result());
   }
 
   const Relocation& parent() const { return *m_Parent; }
@@ -97,20 +78,42 @@
   Relocation::DWord m_Symbol;
   Relocation::DWord m_Result;
 
-  MipsRelocationInfo(Relocation& pParent,
-                     Relocation::Type pType,
-                     Relocation::DWord pResult,
-                     Relocation::DWord pAddend,
-                     Relocation::DWord pSymbol)
+  MipsRelocationInfo(Relocation& pParent, Relocation::Type pType,
+                     Relocation::DWord pResult, Relocation::DWord pAddend)
       : m_Parent(&pParent),
         m_Type(pType),
         m_Addend(pAddend),
-        m_Symbol(pSymbol),
+        m_Symbol(0),
         m_Result(pResult) {}
-
-  bool isFirst() const { return m_Type == parent().type(); }
 };
 
+static void helper_PLT_init(MipsRelocationInfo& pReloc,
+                            MipsRelocator& pParent) {
+  ResolveInfo* rsym = pReloc.parent().symInfo();
+  assert(pParent.getSymPLTMap().lookUp(*rsym) == NULL && "PLT entry exists");
+
+  MipsGNULDBackend& backend = pParent.getTarget();
+  PLTEntryBase* pltEntry = backend.getPLT().create();
+  pParent.getSymPLTMap().record(*rsym, *pltEntry);
+
+  assert(pParent.getSymGOTPLTMap().lookUp(*rsym) == NULL &&
+         "PLT entry not exist, but DynRel entry exist!");
+  Fragment* gotpltEntry = backend.getGOTPLT().create();
+  pParent.getSymGOTPLTMap().record(*rsym, *gotpltEntry);
+
+  Relocation* relEntry = backend.getRelPLT().create();
+  relEntry->setType(llvm::ELF::R_MIPS_JUMP_SLOT);
+  relEntry->targetRef().assign(*gotpltEntry);
+  relEntry->setSymInfo(rsym);
+}
+
+static Relocator::Address helper_get_PLT_address(ResolveInfo& pSym,
+                                                 MipsRelocator& pParent) {
+  PLTEntryBase* plt_entry = pParent.getSymPLTMap().lookUp(pSym);
+  assert(plt_entry != NULL);
+  return pParent.getTarget().getPLT().addr() + plt_entry->getOffset();
+}
+
 //===----------------------------------------------------------------------===//
 // Relocation Functions and Tables
 //===----------------------------------------------------------------------===//
@@ -177,10 +180,6 @@
   return ApplyFunctions[pType & 0xff].name;
 }
 
-Relocator::Size MipsRelocator::getSize(Relocation::Type pType) const {
-  return ApplyFunctions[pType & 0xff].size;
-}
-
 void MipsRelocator::scanRelocation(Relocation& pReloc,
                                    IRBuilder& pBuilder,
                                    Module& pModule,
@@ -253,7 +252,7 @@
       break;
     case llvm::ELF::R_MIPS_32:
     case llvm::ELF::R_MIPS_64:
-      if (LinkerConfig::DynObj == config().codeGenType()) {
+      if (pReloc.isFirst() && LinkerConfig::DynObj == config().codeGenType()) {
         // TODO: (simon) The gold linker does not create an entry in .rel.dyn
         // section if the symbol section flags contains SHF_EXECINSTR.
         // 1. Find the reason of this condition.
@@ -267,7 +266,6 @@
     case llvm::ELF::R_MIPS_26:
     case llvm::ELF::R_MIPS_HI16:
     case llvm::ELF::R_MIPS_LO16:
-    case llvm::ELF::R_MIPS_PC16:
     case llvm::ELF::R_MIPS_SHIFT5:
     case llvm::ELF::R_MIPS_SHIFT6:
     case llvm::ELF::R_MIPS_SUB:
@@ -306,21 +304,37 @@
     case llvm::ELF::R_MIPS_GPREL16:
     case llvm::ELF::R_MIPS_LITERAL:
       break;
+    case llvm::ELF::R_MIPS_TLS_GD:
+      getTarget().getGOT().reserveTLSGdEntry(*rsym);
+      getTarget().checkAndSetHasTextRel(*pSection.getLink());
+      break;
+    case llvm::ELF::R_MIPS_TLS_LDM:
+      getTarget().getGOT().reserveTLSLdmEntry();
+      getTarget().checkAndSetHasTextRel(*pSection.getLink());
+      break;
+    case llvm::ELF::R_MIPS_TLS_GOTTPREL:
+      getTarget().getGOT().reserveTLSGotEntry(*rsym);
+      getTarget().checkAndSetHasTextRel(*pSection.getLink());
+      break;
     case llvm::ELF::R_MIPS_TLS_DTPMOD32:
     case llvm::ELF::R_MIPS_TLS_DTPREL32:
     case llvm::ELF::R_MIPS_TLS_DTPMOD64:
     case llvm::ELF::R_MIPS_TLS_DTPREL64:
-    case llvm::ELF::R_MIPS_TLS_GD:
-    case llvm::ELF::R_MIPS_TLS_LDM:
     case llvm::ELF::R_MIPS_TLS_DTPREL_HI16:
     case llvm::ELF::R_MIPS_TLS_DTPREL_LO16:
-    case llvm::ELF::R_MIPS_TLS_GOTTPREL:
     case llvm::ELF::R_MIPS_TLS_TPREL32:
     case llvm::ELF::R_MIPS_TLS_TPREL64:
     case llvm::ELF::R_MIPS_TLS_TPREL_HI16:
     case llvm::ELF::R_MIPS_TLS_TPREL_LO16:
       break;
+    case llvm::ELF::R_MIPS_PC16:
     case llvm::ELF::R_MIPS_PC32:
+    case llvm::ELF::R_MIPS_PC18_S3:
+    case llvm::ELF::R_MIPS_PC19_S2:
+    case llvm::ELF::R_MIPS_PC21_S2:
+    case llvm::ELF::R_MIPS_PC26_S2:
+    case llvm::ELF::R_MIPS_PCHI16:
+    case llvm::ELF::R_MIPS_PCLO16:
       break;
     default:
       fatal(diag::unknown_relocation) << static_cast<int>(pReloc.type())
@@ -332,6 +346,7 @@
                                     IRBuilder& pBuilder,
                                     const LDSection& pSection) {
   ResolveInfo* rsym = pReloc.parent().symInfo();
+  bool hasPLT = rsym->reserved() & ReservePLT;
 
   switch (pReloc.type()) {
     case llvm::ELF::R_MIPS_NONE:
@@ -348,18 +363,22 @@
       break;
     case llvm::ELF::R_MIPS_32:
     case llvm::ELF::R_MIPS_64:
+      if (pReloc.isFirst() &&
+          getTarget().symbolNeedsDynRel(*rsym, hasPLT, true)) {
+        getTarget().getRelDyn().reserveEntry();
+        rsym->setReserved(rsym->reserved() | ReserveRel);
+        getTarget().checkAndSetHasTextRel(*pSection.getLink());
+        if (!getTarget().symbolFinalValueIsKnown(*rsym))
+          getTarget().getGOT().reserveGlobalEntry(*rsym);
+      }
+      break;
     case llvm::ELF::R_MIPS_HI16:
     case llvm::ELF::R_MIPS_LO16:
-      if (getTarget().symbolNeedsDynRel(*rsym, false, true)) {
+      if (getTarget().symbolNeedsDynRel(*rsym, hasPLT, true) ||
+          getTarget().symbolNeedsCopyReloc(pReloc.parent(), *rsym)) {
         getTarget().getRelDyn().reserveEntry();
-        if (getTarget().symbolNeedsCopyReloc(pReloc.parent(), *rsym)) {
-          LDSymbol& cpySym = defineSymbolforCopyReloc(pBuilder, *rsym);
-          addCopyReloc(*cpySym.resolveInfo());
-        } else {
-          // set Rel bit
-          rsym->setReserved(rsym->reserved() | ReserveRel);
-          getTarget().checkAndSetHasTextRel(*pSection.getLink());
-        }
+        LDSymbol& cpySym = defineSymbolforCopyReloc(pBuilder, *rsym);
+        addCopyReloc(*cpySym.resolveInfo());
       }
       break;
     case llvm::ELF::R_MIPS_GOT16:
@@ -385,16 +404,11 @@
       break;
     case llvm::ELF::R_MIPS_26:
       // Create a PLT entry if the symbol requires it and does not have it.
-      if (getTarget().symbolNeedsPLT(*rsym) &&
-          !(rsym->reserved() & ReservePLT)) {
-        getTarget().getPLT().reserveEntry();
-        getTarget().getGOTPLT().reserve();
-        getTarget().getRelPLT().reserveEntry();
+      if (getTarget().symbolNeedsPLT(*rsym) && !hasPLT) {
+        helper_PLT_init(pReloc, *this);
         rsym->setReserved(rsym->reserved() | ReservePLT);
       }
       break;
-    case llvm::ELF::R_MIPS_PC16:
-      break;
     case llvm::ELF::R_MIPS_16:
     case llvm::ELF::R_MIPS_SHIFT5:
     case llvm::ELF::R_MIPS_SHIFT6:
@@ -403,19 +417,35 @@
     case llvm::ELF::R_MIPS_HIGHEST:
     case llvm::ELF::R_MIPS_SCN_DISP:
       break;
-    case llvm::ELF::R_MIPS_TLS_DTPREL32:
     case llvm::ELF::R_MIPS_TLS_GD:
+      getTarget().getGOT().reserveTLSGdEntry(*rsym);
+      getTarget().checkAndSetHasTextRel(*pSection.getLink());
+      break;
     case llvm::ELF::R_MIPS_TLS_LDM:
+      getTarget().getGOT().reserveTLSLdmEntry();
+      getTarget().checkAndSetHasTextRel(*pSection.getLink());
+      break;
+    case llvm::ELF::R_MIPS_TLS_GOTTPREL:
+      getTarget().getGOT().reserveTLSGotEntry(*rsym);
+      getTarget().checkAndSetHasTextRel(*pSection.getLink());
+      break;
+    case llvm::ELF::R_MIPS_TLS_DTPREL32:
     case llvm::ELF::R_MIPS_TLS_DTPREL_HI16:
     case llvm::ELF::R_MIPS_TLS_DTPREL_LO16:
-    case llvm::ELF::R_MIPS_TLS_GOTTPREL:
     case llvm::ELF::R_MIPS_TLS_TPREL32:
     case llvm::ELF::R_MIPS_TLS_TPREL_HI16:
     case llvm::ELF::R_MIPS_TLS_TPREL_LO16:
       break;
     case llvm::ELF::R_MIPS_REL32:
     case llvm::ELF::R_MIPS_JALR:
+    case llvm::ELF::R_MIPS_PC16:
     case llvm::ELF::R_MIPS_PC32:
+    case llvm::ELF::R_MIPS_PC18_S3:
+    case llvm::ELF::R_MIPS_PC19_S2:
+    case llvm::ELF::R_MIPS_PC21_S2:
+    case llvm::ELF::R_MIPS_PC26_S2:
+    case llvm::ELF::R_MIPS_PCHI16:
+    case llvm::ELF::R_MIPS_PCLO16:
       break;
     case llvm::ELF::R_MIPS_COPY:
     case llvm::ELF::R_MIPS_GLOB_DAT:
@@ -429,7 +459,11 @@
 }
 
 bool MipsRelocator::isPostponed(const Relocation& pReloc) const {
-  if (MipsRelocationInfo::HasSubType(pReloc, llvm::ELF::R_MIPS_HI16))
+  if (isN64ABI())
+    return false;
+
+  if (MipsRelocationInfo::HasSubType(pReloc, llvm::ELF::R_MIPS_HI16) ||
+      MipsRelocationInfo::HasSubType(pReloc, llvm::ELF::R_MIPS_PCHI16))
     return true;
 
   if (MipsRelocationInfo::HasSubType(pReloc, llvm::ELF::R_MIPS_GOT16) &&
@@ -549,6 +583,14 @@
   return getTarget().getGOT().getGPAddr(getApplyingInput());
 }
 
+Relocator::Address MipsRelocator::getTPOffset() {
+  return getTarget().getTPOffset(getApplyingInput());
+}
+
+Relocator::Address MipsRelocator::getDTPOffset() {
+  return getTarget().getDTPOffset(getApplyingInput());
+}
+
 Relocator::Address MipsRelocator::getGP0() {
   return getTarget().getGP0(getApplyingInput());
 }
@@ -572,7 +614,7 @@
   got_entry = got.consumeLocal();
 
   if (got.isPrimaryGOTConsumed())
-    setupRelDynEntry(*FragmentRef::Create(*got_entry, 0), NULL);
+    setupRel32DynEntry(*FragmentRef::Create(*got_entry, 0), NULL);
   else
     got.setEntryValue(got_entry, entryValue);
 
@@ -599,7 +641,7 @@
   got_entry = got.consumeGlobal();
 
   if (got.isPrimaryGOTConsumed())
-    setupRelDynEntry(*FragmentRef::Create(*got_entry, 0), rsym);
+    setupRel32DynEntry(*FragmentRef::Create(*got_entry, 0), rsym);
   else
     got.setEntryValue(got_entry, pReloc.parent().symValue());
 
@@ -608,6 +650,25 @@
   return *got_entry;
 }
 
+Fragment& MipsRelocator::getTLSGOTEntry(MipsRelocationInfo& pReloc) {
+  // rsym - The relocation target symbol
+  ResolveInfo* rsym = pReloc.parent().symInfo();
+  MipsGOT& got = getTarget().getGOT();
+
+  Fragment* modEntry = got.lookupTLSEntry(rsym, pReloc.type());
+
+  // Found a mapping, then return the mapped entry immediately.
+  if (modEntry != NULL)
+    return *modEntry;
+
+  // Not found.
+  modEntry = got.consumeTLS(pReloc.type());
+  setupTLSDynEntry(*modEntry, rsym, pReloc.type());
+  got.recordTLSEntry(rsym, modEntry, pReloc.type());
+
+  return *modEntry;
+}
+
 Relocator::Address MipsRelocator::getGOTOffset(MipsRelocationInfo& pReloc) {
   ResolveInfo* rsym = pReloc.parent().symInfo();
   MipsGOT& got = getTarget().getGOT();
@@ -625,23 +686,34 @@
   }
 }
 
+Relocator::Address MipsRelocator::getTLSGOTOffset(MipsRelocationInfo& pReloc) {
+  MipsGOT& got = getTarget().getGOT();
+  return got.getGPRelOffset(getApplyingInput(), getTLSGOTEntry(pReloc));
+}
+
 void MipsRelocator::createDynRel(MipsRelocationInfo& pReloc) {
   Relocator::DWord A = pReloc.A();
   Relocator::DWord S = pReloc.S();
 
   ResolveInfo* rsym = pReloc.parent().symInfo();
 
-  if (isLocalReloc(*rsym)) {
-    setupRelDynEntry(pReloc.parent().targetRef(), NULL);
-    pReloc.result() = A + S;
-  } else {
-    setupRelDynEntry(pReloc.parent().targetRef(), rsym);
+  if (getTarget().isDynamicSymbol(*rsym)) {
+    setupRel32DynEntry(pReloc.parent().targetRef(), rsym);
     // Don't add symbol value that will be resolved by the dynamic linker.
     pReloc.result() = A;
+  } else {
+    setupRel32DynEntry(pReloc.parent().targetRef(), NULL);
+    pReloc.result() = A + S;
   }
+
+  if (!isLocalReloc(*rsym) && !getTarget().symbolFinalValueIsKnown(*rsym))
+    getGlobalGOTEntry(pReloc);
 }
 
 uint64_t MipsRelocator::calcAHL(const MipsRelocationInfo& pHiReloc) {
+  if (isN64ABI())
+    return pHiReloc.A();
+
   assert(m_CurrentLo16Reloc != NULL &&
          "There is no saved R_MIPS_LO16 relocation");
 
@@ -656,32 +728,6 @@
   return config().targets().is64Bits();
 }
 
-uint64_t MipsRelocator::getPLTAddress(ResolveInfo& rsym) {
-  assert((rsym.reserved() & MipsRelocator::ReservePLT) &&
-         "Symbol does not require a PLT entry");
-
-  SymPLTMap::const_iterator it = m_SymPLTMap.find(&rsym);
-
-  Fragment* plt;
-
-  if (it != m_SymPLTMap.end()) {
-    plt = it->second.first;
-  } else {
-    plt = getTarget().getPLT().consume();
-
-    Fragment* got = getTarget().getGOTPLT().consume();
-    Relocation* rel = getTarget().getRelPLT().consumeEntry();
-
-    rel->setType(llvm::ELF::R_MIPS_JUMP_SLOT);
-    rel->targetRef().assign(*got);
-    rel->setSymInfo(&rsym);
-
-    m_SymPLTMap[&rsym] = PLTDescriptor(plt, got);
-  }
-
-  return getTarget().getPLT().addr() + plt->getOffset();
-}
-
 uint32_t MipsRelocator::getDebugStringOffset(Relocation& pReloc) const {
   if (pReloc.type() != llvm::ELF::R_MIPS_32)
     error(diag::unsupport_reloc_for_debug_string)
@@ -698,6 +744,13 @@
   pReloc.target() = pOffset;
 }
 
+void MipsRelocator::setupRelDynEntry(FragmentRef& pFragRef, ResolveInfo* pSym,
+                                     Relocation::Type pType) {
+  Relocation& relEntry = *getTarget().getRelDyn().consumeEntry();
+  relEntry.setType(pType);
+  relEntry.targetRef() = pFragRef;
+  relEntry.setSymInfo(pSym);
+}
 
 //===----------------------------------------------------------------------===//
 // Mips32Relocator
@@ -707,12 +760,32 @@
     : MipsRelocator(pParent, pConfig) {
 }
 
-void Mips32Relocator::setupRelDynEntry(FragmentRef& pFragRef,
-                                       ResolveInfo* pSym) {
-  Relocation& relEntry = *getTarget().getRelDyn().consumeEntry();
-  relEntry.setType(llvm::ELF::R_MIPS_REL32);
-  relEntry.targetRef() = pFragRef;
-  relEntry.setSymInfo(pSym);
+void Mips32Relocator::setupRel32DynEntry(FragmentRef& pFragRef,
+                                         ResolveInfo* pSym) {
+  setupRelDynEntry(pFragRef, pSym, llvm::ELF::R_MIPS_REL32);
+}
+
+void Mips32Relocator::setupTLSDynEntry(Fragment& pFrag, ResolveInfo* pSym,
+                                       Relocation::Type pType) {
+  pSym = pSym->isLocal() ? nullptr : pSym;
+  if (pType == llvm::ELF::R_MIPS_TLS_GD) {
+    FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0);
+    setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPMOD32);
+    FragmentRef& relFrag = *FragmentRef::Create(*pFrag.getNextNode(), 0);
+    setupRelDynEntry(relFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPREL32);
+  } else if (pType == llvm::ELF::R_MIPS_TLS_LDM) {
+    FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0);
+    setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPMOD32);
+  } else if (pType == llvm::ELF::R_MIPS_TLS_GOTTPREL) {
+    FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0);
+    setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_TPREL32);
+  } else {
+    llvm_unreachable("Unexpected relocation");
+  }
+}
+
+Relocator::Size Mips32Relocator::getSize(Relocation::Type pType) const {
+  return ApplyFunctions[pType & 0xff].size;
 }
 
 //===----------------------------------------------------------------------===//
@@ -723,16 +796,37 @@
     : MipsRelocator(pParent, pConfig) {
 }
 
-void Mips64Relocator::setupRelDynEntry(FragmentRef& pFragRef,
-                                       ResolveInfo* pSym) {
+void Mips64Relocator::setupRel32DynEntry(FragmentRef& pFragRef,
+                                         ResolveInfo* pSym) {
   Relocation::Type type = llvm::ELF::R_MIPS_REL32 | llvm::ELF::R_MIPS_64 << 8;
-  // FIXME (simon): Fix dynamic relocations.
-  type = llvm::ELF::R_MIPS_NONE;
+  setupRelDynEntry(pFragRef, pSym, type);
+}
 
-  Relocation& relEntry = *getTarget().getRelDyn().consumeEntry();
-  relEntry.setType(type);
-  relEntry.targetRef() = pFragRef;
-  relEntry.setSymInfo(pSym);
+void Mips64Relocator::setupTLSDynEntry(Fragment& pFrag, ResolveInfo* pSym,
+                                       Relocation::Type pType) {
+  pSym = pSym->isLocal() ? nullptr : pSym;
+  if (pType == llvm::ELF::R_MIPS_TLS_GD) {
+    FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0);
+    setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPMOD64);
+    FragmentRef& relFrag = *FragmentRef::Create(*pFrag.getNextNode(), 0);
+    setupRelDynEntry(relFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPREL64);
+  } else if (pType == llvm::ELF::R_MIPS_TLS_LDM) {
+    FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0);
+    setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_DTPMOD64);
+  } else if (pType == llvm::ELF::R_MIPS_TLS_GOTTPREL) {
+    FragmentRef& modFrag = *FragmentRef::Create(pFrag, 0);
+    setupRelDynEntry(modFrag, pSym, llvm::ELF::R_MIPS_TLS_TPREL64);
+  } else {
+    llvm_unreachable("Unexpected relocation");
+  }
+}
+
+Relocator::Size Mips64Relocator::getSize(Relocation::Type pType) const {
+  if (((pType >> 16) & 0xff) != llvm::ELF::R_MIPS_NONE)
+    return ApplyFunctions[(pType >> 16) & 0xff].size;
+  if (((pType >> 8) & 0xff) != llvm::ELF::R_MIPS_NONE)
+    return ApplyFunctions[(pType >> 8) & 0xff].size;
+  return ApplyFunctions[pType & 0xff].size;
 }
 
 //=========================================//
@@ -784,7 +878,7 @@
   int32_t A = pParent.isN64ABI() ? pReloc.A() : (pReloc.A() & 0x03FFFFFF) << 2;
   int32_t P = pReloc.P();
   int32_t S = rsym->reserved() & MipsRelocator::ReservePLT
-                  ? pParent.getPLTAddress(*rsym)
+                  ? helper_get_PLT_address(*rsym, pParent)
                   : pReloc.S();
 
   if (rsym->isLocal())
@@ -992,37 +1086,133 @@
   return Relocator::OK;
 }
 
-// R_MIPS_LA25_LUI
-static MipsRelocator::Result la25lui(MipsRelocationInfo& pReloc,
-                                     MipsRelocator& pParent) {
-  int32_t S = pReloc.S();
-
-  pReloc.result() = (S + 0x8000) >> 16;
-
+// R_MIPS_PC16
+static MipsRelocator::Result pc16(MipsRelocationInfo& pReloc,
+                                  MipsRelocator& pParent) {
+  int64_t A = signExtend<18>(pReloc.A() << 2);
+  int64_t S = pReloc.S();
+  int64_t P = pReloc.P();
+  pReloc.result() = (A + S - P) >> 2;
   return Relocator::OK;
 }
 
-// R_MIPS_LA25_J
-static MipsRelocator::Result la25j(MipsRelocationInfo& pReloc,
-                                   MipsRelocator& pParent) {
-  int32_t S = pReloc.S();
-
-  pReloc.result() = S >> 2;
-
-  return Relocator::OK;
-}
-
-// R_MIPS_LA25_ADD
-static MipsRelocator::Result la25add(MipsRelocationInfo& pReloc,
-                                     MipsRelocator& pParent) {
-  pReloc.result() = pReloc.S();
-
-  return Relocator::OK;
-}
-
-// R_MIPS_PC32:
+// R_MIPS_PC32
 static MipsRelocator::Result pc32(MipsRelocationInfo& pReloc,
                                   MipsRelocator& pParent) {
+  int64_t A = pReloc.A();
+  int64_t S = pReloc.S();
+  int64_t P = pReloc.P();
+  pReloc.result() = A + S - P;
+  return Relocator::OK;
+}
+
+// R_MIPS_PC18_S3
+static MipsRelocator::Result pc18_s3(MipsRelocationInfo& pReloc,
+                                     MipsRelocator& pParent) {
+  int64_t A = signExtend<21>(pReloc.A() << 3);
+  int64_t S = pReloc.S();
+  int64_t P = pReloc.P();
+  pReloc.result() = (S + A - ((P | 7) ^ 7)) >> 3;
+  return Relocator::OK;
+}
+
+// R_MIPS_PC19_S2
+static MipsRelocator::Result pc19_s2(MipsRelocationInfo& pReloc,
+                                     MipsRelocator& pParent) {
+  int64_t A = signExtend<21>(pReloc.A() << 2);
+  int64_t S = pReloc.S();
+  int64_t P = pReloc.P();
+  pReloc.result() = (A + S - P) >> 2;
+  return Relocator::OK;
+}
+
+// R_MIPS_PC21_S2
+static MipsRelocator::Result pc21_s2(MipsRelocationInfo& pReloc,
+                                     MipsRelocator& pParent) {
+  int32_t A = signExtend<23>(pReloc.A() << 2);
+  int32_t S = pReloc.S();
+  int32_t P = pReloc.P();
+  pReloc.result() = (A + S - P) >> 2;
+  return Relocator::OK;
+}
+
+// R_MIPS_PC26_S2
+static MipsRelocator::Result pc26_s2(MipsRelocationInfo& pReloc,
+                                     MipsRelocator& pParent) {
+  int64_t A = signExtend<28>(pReloc.A() << 2);
+  int64_t S = pReloc.S();
+  int64_t P = pReloc.P();
+  pReloc.result() = (A + S - P) >> 2;
+  return Relocator::OK;
+}
+
+// R_MIPS_PCHI16
+static MipsRelocator::Result pchi16(MipsRelocationInfo& pReloc,
+                                    MipsRelocator& pParent) {
+  uint64_t AHL = pParent.calcAHL(pReloc);
+  int64_t S = pReloc.S();
+  int64_t P = pReloc.P();
+  pReloc.result() = (S + AHL - P + 0x8000) >> 16;
+  return Relocator::OK;
+}
+
+// R_MIPS_PCLO16
+static MipsRelocator::Result pclo16(MipsRelocationInfo& pReloc,
+                                    MipsRelocator& pParent) {
+  int32_t AHL = pReloc.A() & 0xFFFF;
+  int64_t S = pReloc.S();
+  int64_t P = pReloc.P();
+  pReloc.result() = S + AHL - P;
+  pParent.applyPostponedRelocations(pReloc);
+  return Relocator::OK;
+}
+
+// R_MIPS_TLS_TPREL_HI16, R_MIPS_TLS_DTPREL_HI16
+//   local/external: (A + S - TP Offset) >> 16
+//   _gp_disp      : (A + GP - P - TP Offset) >> 16
+static MipsRelocator::Result tlshi16(MipsRelocationInfo& pReloc,
+                                     MipsRelocator& pParent) {
+  uint64_t A = pReloc.A() & 0xFFFF;
+  if (pReloc.type() == llvm::ELF::R_MIPS_TLS_TPREL_HI16)
+    A -= pParent.getTPOffset();
+  else if (pReloc.type() == llvm::ELF::R_MIPS_TLS_DTPREL_HI16)
+    A -= pParent.getDTPOffset();
+  else
+    llvm_unreachable("Unexpected relocation");
+
+  if (pParent.isGpDisp(pReloc.parent()))
+    pReloc.result() = (A + pReloc.S() - pReloc.P() + 0x8000) >> 16;
+  else
+    pReloc.result() = (A + pReloc.S() + 0x8000) >> 16;
+
+  return Relocator::OK;
+}
+
+// R_MIPS_TLS_TPREL_LO16, R_MIPS_TLS_DTPREL_LO16
+//   local/external: A + S - TP Offset
+//   _gp_disp      : A + GP - P + 4 - TP Offset
+static MipsRelocator::Result tlslo16(MipsRelocationInfo& pReloc,
+                                     MipsRelocator& pParent) {
+  uint64_t A = pReloc.A() & 0xFFFF;
+  if (pReloc.type() == llvm::ELF::R_MIPS_TLS_TPREL_LO16)
+    A -= pParent.getTPOffset();
+  else if (pReloc.type() == llvm::ELF::R_MIPS_TLS_DTPREL_LO16)
+    A -= pParent.getDTPOffset();
+  else
+    llvm_unreachable("Unexpected relocation");
+
+  if (pParent.isGpDisp(pReloc.parent()))
+    pReloc.result() = A + pReloc.S() - pReloc.P() + 4;
+  else
+    pReloc.result() = A + pReloc.S();
+
+  return Relocator::OK;
+}
+
+// R_MIPS_TLS_GD, R_MIPS_TLS_LDM
+static MipsRelocator::Result tlsgot(MipsRelocationInfo& pReloc,
+                                    MipsRelocator& pParent) {
+  pReloc.result() = pParent.getTLSGOTOffset(pReloc);
   return Relocator::OK;
 }
 
diff --git a/lib/Target/Mips/MipsRelocator.h b/lib/Target/Mips/MipsRelocator.h
index 27d5896..093718f 100644
--- a/lib/Target/Mips/MipsRelocator.h
+++ b/lib/Target/Mips/MipsRelocator.h
@@ -11,6 +11,7 @@
 
 #include "mcld/LD/Relocator.h"
 #include "mcld/Support/GCFactory.h"
+#include "mcld/Target/KeyEntryMap.h"
 #include "MipsLDBackend.h"
 
 #include <llvm/ADT/DenseMapInfo.h>
@@ -31,6 +32,9 @@
     ReservePLT = 4   // reserve a PLT entry
   };
 
+  typedef KeyEntryMap<ResolveInfo, PLTEntryBase> SymPLTMap;
+  typedef KeyEntryMap<ResolveInfo, Fragment> SymGOTPLTMap;
+
  public:
   MipsRelocator(MipsGNULDBackend& pParent, const LinkerConfig& pConfig);
 
@@ -89,6 +93,14 @@
   /// getGPAddress - return address of _gp symbol.
   Address getGPAddress();
 
+  /// getTPOffset - return TP_OFFSET against the SHF_TLS
+  /// section in the processing input.
+  Address getTPOffset();
+
+  /// getDTPOffset - return DTP_OFFSET against the SHF_TLS
+  /// section in the processing input.
+  Address getDTPOffset();
+
   /// getGP0 - the gp value used to create the relocatable objects
   /// in the processing input.
   Address getGP0();
@@ -102,16 +114,19 @@
   /// for this relocation.
   Fragment& getGlobalGOTEntry(MipsRelocationInfo& pReloc);
 
+  /// getTLSGOTEntry - initialize and return a TLS GOT entry
+  /// for this relocation.
+  Fragment& getTLSGOTEntry(MipsRelocationInfo& pReloc);
+
   /// getGOTOffset - return offset of corresponded GOT entry.
   Address getGOTOffset(MipsRelocationInfo& pReloc);
 
+  /// getTLSGOTOffset - return offset of corresponded TLS GOT entry.
+  Address getTLSGOTOffset(MipsRelocationInfo& pReloc);
+
   /// createDynRel - initialize dynamic relocation for the relocation.
   void createDynRel(MipsRelocationInfo& pReloc);
 
-  /// getPLTOffset - initialize PLT-related entries for the symbol
-  /// @return - return address of PLT entry
-  uint64_t getPLTAddress(ResolveInfo& rsym);
-
   /// calcAHL - calculate combined addend used
   /// by R_MIPS_HI16 and R_MIPS_GOT16 relocations.
   uint64_t calcAHL(const MipsRelocationInfo& pHiReloc);
@@ -121,24 +136,34 @@
 
   const char* getName(Relocation::Type pType) const;
 
-  Size getSize(Relocation::Type pType) const;
+  const SymPLTMap& getSymPLTMap() const { return m_SymPLTMap; }
+  SymPLTMap& getSymPLTMap() { return m_SymPLTMap; }
+
+  const SymGOTPLTMap& getSymGOTPLTMap() const { return m_SymGOTPLTMap; }
+  SymGOTPLTMap& getSymGOTPLTMap() { return m_SymGOTPLTMap; }
 
  protected:
   /// setupRelDynEntry - create dynamic relocation entry.
-  virtual void setupRelDynEntry(FragmentRef& pFragRef, ResolveInfo* pSym) = 0;
+  virtual void setupRel32DynEntry(FragmentRef& pFragRef, ResolveInfo* pSym) = 0;
+  /// setupTLSDynEntry - create DTPMOD / DTPREL relocation entries
+  virtual void setupTLSDynEntry(Fragment& pFrag, ResolveInfo* pSym,
+                                Relocation::Type pType) = 0;
 
   /// isLocalReloc - handle relocation as a local symbol
   bool isLocalReloc(ResolveInfo& pSym) const;
 
+  /// setupRelDynEntry - create dynamic relocation entry with specified type.
+  void setupRelDynEntry(FragmentRef& pFragRef, ResolveInfo* pSym,
+                        Relocation::Type pType);
+
  private:
-  typedef std::pair<Fragment*, Fragment*> PLTDescriptor;
-  typedef llvm::DenseMap<const ResolveInfo*, PLTDescriptor> SymPLTMap;
   typedef llvm::DenseSet<Relocation*> RelocationSet;
   typedef llvm::DenseMap<const ResolveInfo*, RelocationSet> SymRelocSetMap;
 
  private:
   MipsGNULDBackend& m_Target;
   SymPLTMap m_SymPLTMap;
+  SymGOTPLTMap m_SymGOTPLTMap;
   Input* m_pApplyingInput;
   SymRelocSetMap m_PostponedRelocs;
   MipsRelocationInfo* m_CurrentLo16Reloc;
@@ -178,7 +203,10 @@
 
  private:
   // MipsRelocator
-  void setupRelDynEntry(FragmentRef& pFragRef, ResolveInfo* pSym);
+  void setupRel32DynEntry(FragmentRef& pFragRef, ResolveInfo* pSym);
+  void setupTLSDynEntry(Fragment& pFrag, ResolveInfo* pSym,
+                        Relocation::Type pType);
+  Size getSize(Relocation::Type pType) const;
 };
 
 /** \class Mips64Relocator
@@ -190,7 +218,10 @@
 
  private:
   // MipsRelocator
-  void setupRelDynEntry(FragmentRef& pFragRef, ResolveInfo* pSym);
+  void setupRel32DynEntry(FragmentRef& pFragRef, ResolveInfo* pSym);
+  void setupTLSDynEntry(Fragment& pFrag, ResolveInfo* pSym,
+                        Relocation::Type pType);
+  Size getSize(Relocation::Type pType) const;
 };
 
 }  // namespace mcld
diff --git a/tools/mcld/Main.cpp b/tools/mcld/Main.cpp
index 7231d3c..b24d1a9 100644
--- a/tools/mcld/Main.cpp
+++ b/tools/mcld/Main.cpp
@@ -85,9 +85,8 @@
   };
 
  private:
-  Driver(const char* prog_name, std::unique_ptr<llvm::opt::InputArgList> args)
+  explicit Driver(const char* prog_name)
       : prog_name_(prog_name),
-        args_(std::move(args)),
         module_(script_),
         ir_builder_(module_, config_) {
     return;
@@ -99,13 +98,11 @@
   bool Run();
 
  private:
-  bool TranslateArguments();
+  bool TranslateArguments(llvm::opt::InputArgList& args);
 
  private:
   const char* prog_name_;
 
-  std::unique_ptr<llvm::opt::InputArgList> args_;
-
   mcld::LinkerScript script_;
 
   mcld::LinkerConfig config_;
@@ -131,12 +128,12 @@
     { PREFIX, NAME, HELPTEXT, METAVAR, kOpt_ ## ID, \
       llvm::opt::Option::KIND ## Class, PARAM, FLAGS, kOpt_ ## GROUP, \
       kOpt_ ## ALIAS, ALIASARGS },
-#include "Options.inc"
+#include "Options.inc"  // NOLINT
 #undef OPTION
 };
 
 Driver::OptTable::OptTable()
-    : llvm::opt::OptTable(InfoTable, llvm::array_lengthof(InfoTable)) { }
+    : llvm::opt::OptTable(InfoTable) { }
 
 inline bool ShouldColorize() {
   const char* term = getenv("TERM");
@@ -258,13 +255,13 @@
   return true;
 }
 
-bool Driver::TranslateArguments() {
+bool Driver::TranslateArguments(llvm::opt::InputArgList& args) {
   //===--------------------------------------------------------------------===//
   // Preference
   //===--------------------------------------------------------------------===//
 
   // --color=mode
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Color)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Color)) {
     bool res = llvm::StringSwitch<bool>(arg->getValue())
                    .Case("never", false)
                    .Case("always", true)
@@ -278,10 +275,10 @@
   }
 
   // --trace
-  config_.options().setTrace(args_->hasArg(kOpt_Trace));
+  config_.options().setTrace(args.hasArg(kOpt_Trace));
 
   // --verbose=level
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Verbose)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Verbose)) {
     llvm::StringRef value = arg->getValue();
     int level;
     if (value.getAsInteger(0, level)) {
@@ -293,7 +290,7 @@
   }
 
   // --error-limit NUMBER
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_ErrorLimit)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_ErrorLimit)) {
     llvm::StringRef value = arg->getValue();
     int num;
     if (value.getAsInteger(0, num) || (num < 0)) {
@@ -305,7 +302,7 @@
   }
 
   // --warning-limit NUMBER
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_WarningLimit)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_WarningLimit)) {
     llvm::StringRef value = arg->getValue();
     int num;
     if (value.getAsInteger(0, num) || (num < 0)) {
@@ -317,13 +314,13 @@
   }
 
   // --warn-shared-textrel
-  config_.options().setWarnSharedTextrel(args_->hasArg(kOpt_WarnSharedTextrel));
+  config_.options().setWarnSharedTextrel(args.hasArg(kOpt_WarnSharedTextrel));
 
   //===--------------------------------------------------------------------===//
   // Target
   //===--------------------------------------------------------------------===//
   llvm::Triple triple;
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Triple)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Triple)) {
     // 1. Use the triple from command.
     // -mtriple=value
     triple.setTriple(arg->getValue());
@@ -339,22 +336,22 @@
   }
 
   // If a specific emulation was requested, apply it now.
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Emulation)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Emulation)) {
     // -m emulation
     ParseEmulation(triple, arg->getValue());
-  } else if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Arch)) {
+  } else if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Arch)) {
     // -march=value
     config_.targets().setArch(arg->getValue());
   }
 
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_CPU)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_CPU)) {
     config_.targets().setTargetCPU(arg->getValue());
   }
 
   config_.targets().setTriple(triple);
 
   // --gpsize=value
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_GPSize)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_GPSize)) {
     llvm::StringRef value = arg->getValue();
     int size;
     if (value.getAsInteger(0, size) || (size< 0)) {
@@ -362,41 +359,61 @@
                    << ": " << arg->getValue() << "\n";
       return false;
     }
-    config_.options().setGPSize(size);
+    config_.targets().setGPSize(size);
   }
 
+  // --stub-group-size=value
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_StubGroupSize)) {
+    llvm::StringRef value = arg->getValue();
+    int size;
+    if (value.getAsInteger(0, size) || (size< 0)) {
+      mcld::errs() << "Invalid value for" << arg->getOption().getPrefixedName()
+                   << ": " << arg->getValue() << "\n";
+      return false;
+    }
+    config_.targets().setStubGroupSize(size);
+  }
+
+  // --fix-cortex-a53-835769
+  config_.targets().setFixCA53Erratum835769(
+      args.hasArg(kOpt_FixCA53Erratum835769));
+
+  // --fix-cortex-a53-843419
+  config_.targets().setFixCA53Erratum843419(
+      args.hasArg(kOpt_FixCA53Erratum843419));
+
   //===--------------------------------------------------------------------===//
   // Dynamic
   //===--------------------------------------------------------------------===//
 
   // --entry=entry
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Entry)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Entry)) {
     script_.setEntry(arg->getValue());
   }
 
   // -Bsymbolic
-  config_.options().setBsymbolic(args_->hasArg(kOpt_Bsymbolic));
+  config_.options().setBsymbolic(args.hasArg(kOpt_Bsymbolic));
 
   // -Bgroup
-  config_.options().setBgroup(args_->hasArg(kOpt_Bgroup));
+  config_.options().setBgroup(args.hasArg(kOpt_Bgroup));
 
   // -soname=name
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_SOName)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_SOName)) {
     config_.options().setSOName(arg->getValue());
   }
 
   // --no-undefined
-  if (args_->hasArg(kOpt_NoUndef)) {
+  if (args.hasArg(kOpt_NoUndef)) {
     config_.options().setNoUndefined(true);
   }
 
   // --allow-multiple-definition
-  if (args_->hasArg(kOpt_AllowMulDefs)) {
+  if (args.hasArg(kOpt_AllowMulDefs)) {
     config_.options().setMulDefs(true);
   }
 
   // -z options
-  for (llvm::opt::Arg* arg : args_->filtered(kOpt_Z)) {
+  for (llvm::opt::Arg* arg : args.filtered(kOpt_Z)) {
     llvm::StringRef value = arg->getValue();
     mcld::ZOption z_opt =
         llvm::StringSwitch<mcld::ZOption>(value)
@@ -440,15 +457,15 @@
   }
 
   // --dynamic-linker=file
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Dyld)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Dyld)) {
     config_.options().setDyld(arg->getValue());
   }
 
   // --enable-new-dtags
-  config_.options().setNewDTags(args_->hasArg(kOpt_EnableNewDTags));
+  config_.options().setNewDTags(args.hasArg(kOpt_EnableNewDTags));
 
   // --spare-dyanmic-tags COUNT
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_SpareDTags)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_SpareDTags)) {
     llvm::StringRef value = arg->getValue();
     int num;
     if (value.getAsInteger(0, num) || (num < 0)) {
@@ -464,13 +481,13 @@
   //===--------------------------------------------------------------------===//
 
   // Setup the codegen type.
-  if (args_->hasArg(kOpt_Shared) || args_->hasArg(kOpt_PIE)) {
+  if (args.hasArg(kOpt_Shared) || args.hasArg(kOpt_PIE)) {
     // -shared, -pie
     config_.setCodeGenType(mcld::LinkerConfig::DynObj);
-  } else if (args_->hasArg(kOpt_Relocatable)) {
+  } else if (args.hasArg(kOpt_Relocatable)) {
     // -r
     config_.setCodeGenType(mcld::LinkerConfig::Object);
-  } else if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_OutputFormat)) {
+  } else if (llvm::opt::Arg* arg = args.getLastArg(kOpt_OutputFormat)) {
     // --oformat=value
     llvm::StringRef value = arg->getValue();
     if (value.equals("binary")) {
@@ -482,20 +499,20 @@
 
   // Setup the output filename.
   llvm::StringRef output_name;
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Output)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Output)) {
     output_name = arg->getValue();
   }
   if (!ConfigureOutputName(output_name, module_, config_)) {
     mcld::unreachable(mcld::diag::unrecognized_output_file) << module_.name();
     return false;
   } else {
-    if (!args_->hasArg(kOpt_SOName)) {
+    if (!args.hasArg(kOpt_SOName)) {
       config_.options().setSOName(module_.name());
     }
   }
 
   // --format=value
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_InputFormat)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_InputFormat)) {
     llvm::StringRef value = arg->getValue();
     if (value.equals("binary")) {
       config_.options().setBinaryInput();
@@ -503,17 +520,17 @@
   }
 
   // Setup debug info stripping.
-  config_.options().setStripDebug(args_->hasArg(kOpt_StripDebug) ||
-                                  args_->hasArg(kOpt_StripAll));
+  config_.options().setStripDebug(args.hasArg(kOpt_StripDebug) ||
+                                  args.hasArg(kOpt_StripAll));
 
   // Setup symbol stripping mode.
-  if (args_->hasArg(kOpt_StripAll)) {
+  if (args.hasArg(kOpt_StripAll)) {
     config_.options().setStripSymbols(
         mcld::GeneralOptions::StripSymbolMode::StripAllSymbols);
-  } else if (args_->hasArg(kOpt_DiscardAll)) {
+  } else if (args.hasArg(kOpt_DiscardAll)) {
     config_.options().setStripSymbols(
         mcld::GeneralOptions::StripSymbolMode::StripLocals);
-  } else if (args_->hasArg(kOpt_DiscardLocals)) {
+  } else if (args.hasArg(kOpt_DiscardLocals)) {
     config_.options().setStripSymbols(
         mcld::GeneralOptions::StripSymbolMode::StripTemporaries);
   } else {
@@ -522,19 +539,19 @@
   }
 
   // --eh-frame-hdr
-  config_.options().setEhFrameHdr(args_->hasArg(kOpt_EHFrameHdr));
+  config_.options().setEhFrameHdr(args.hasArg(kOpt_EHFrameHdr));
 
   // -pie
-  config_.options().setPIE(args_->hasArg(kOpt_PIE));
+  config_.options().setPIE(args.hasArg(kOpt_PIE));
 
   // --nmagic
-  config_.options().setNMagic(args_->hasArg(kOpt_NMagic));
+  config_.options().setNMagic(args.hasArg(kOpt_NMagic));
 
   // --omagic
-  config_.options().setOMagic(args_->hasArg(kOpt_OMagic));
+  config_.options().setOMagic(args.hasArg(kOpt_OMagic));
 
   // --hash-style=style
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_HashStyle)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_HashStyle)) {
     mcld::GeneralOptions::HashStyle style =
         llvm::StringSwitch<mcld::GeneralOptions::HashStyle>(arg->getValue())
             .Case("sysv", mcld::GeneralOptions::HashStyle::SystemV)
@@ -547,7 +564,7 @@
   }
 
   // --[no]-export-dynamic
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_ExportDynamic,
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_ExportDynamic,
                                               kOpt_NoExportDynamic)) {
     if (arg->getOption().matches(kOpt_ExportDynamic)) {
       config_.options().setExportDynamic(true);
@@ -557,10 +574,10 @@
   }
 
   // --no-warn-mismatch
-  config_.options().setWarnMismatch(!args_->hasArg(kOpt_NoWarnMismatch));
+  config_.options().setWarnMismatch(!args.hasArg(kOpt_NoWarnMismatch));
 
   // --exclude-libs
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_ExcludeLibs)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_ExcludeLibs)) {
     llvm::StringRef value = arg->getValue();
     do {
       std::pair<llvm::StringRef, llvm::StringRef> res = value.split(',');
@@ -574,7 +591,7 @@
   //===--------------------------------------------------------------------===//
 
   // --sysroot
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Sysroot)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Sysroot)) {
     mcld::sys::fs::Path path(arg->getValue());
     if (mcld::sys::fs::exists(path) && mcld::sys::fs::is_directory(path)) {
       script_.setSysroot(path);
@@ -582,19 +599,16 @@
   }
 
   // -L searchdir
-  for (llvm::opt::Arg* arg : args_->filtered(kOpt_LibraryPath)) {
-    if (!script_.directories().insert(arg->getValue())) {
-      // FIXME: need a warning function
-      mcld::errs() << "WARNING: can not open search directory `-L"
-                   << arg->getValue() << "'.\n";
-    }
+  for (llvm::opt::Arg* arg : args.filtered(kOpt_LibraryPath)) {
+    if (!script_.directories().insert(arg->getValue()))
+      mcld::warning(mcld::diag::warn_cannot_open_search_dir) << arg->getValue();
   }
 
   // -nostdlib
-  config_.options().setNoStdlib(args_->hasArg(kOpt_NoStdlib));
+  config_.options().setNoStdlib(args.hasArg(kOpt_NoStdlib));
 
   // -rpath=path
-  for (llvm::opt::Arg* arg : args_->filtered(kOpt_RPath)) {
+  for (llvm::opt::Arg* arg : args.filtered(kOpt_RPath)) {
     config_.options().getRpathList().push_back(arg->getValue());
   }
 
@@ -603,10 +617,10 @@
   //===--------------------------------------------------------------------===//
 
   // -d/-dc/-dp
-  config_.options().setDefineCommon(args_->hasArg(kOpt_DefineCommon));
+  config_.options().setDefineCommon(args.hasArg(kOpt_DefineCommon));
 
   // -u symbol
-  for (llvm::opt::Arg* arg : args_->filtered(kOpt_Undefined)) {
+  for (llvm::opt::Arg* arg : args.filtered(kOpt_Undefined)) {
     config_.options().getUndefSymList().push_back(arg->getValue());
   }
 
@@ -615,7 +629,7 @@
   //===--------------------------------------------------------------------===//
 
   // --wrap=symbol
-  for (llvm::opt::Arg* arg : args_->filtered(kOpt_Wrap)) {
+  for (llvm::opt::Arg* arg : args.filtered(kOpt_Wrap)) {
     bool exist = false;
     const char* symbol = arg->getValue();
     // symbol -> __wrap_symbol
@@ -643,7 +657,7 @@
   }
 
   // --portalbe=symbol
-  for (llvm::opt::Arg* arg : args_->filtered(kOpt_Wrap)) {
+  for (llvm::opt::Arg* arg : args.filtered(kOpt_Portable)) {
     bool exist = false;
     const char* symbol = arg->getValue();
     // symbol -> symbol_portable
@@ -671,7 +685,7 @@
   }
 
   // --section-start=section=addr
-  for (llvm::opt::Arg* arg : args_->filtered(kOpt_SectionStart)) {
+  for (llvm::opt::Arg* arg : args.filtered(kOpt_SectionStart)) {
     llvm::StringRef value = arg->getValue();
     const size_t pos = value.find('=');
     uint64_t addr = 0;
@@ -683,7 +697,7 @@
   }
 
   // -Tbss=value
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Tbss)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Tbss)) {
     llvm::StringRef value = arg->getValue();
     uint64_t addr = 0;
     if (value.getAsInteger(0, addr)) {
@@ -698,7 +712,7 @@
   }
 
   // -Tdata=value
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Tdata)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Tdata)) {
     llvm::StringRef value = arg->getValue();
     uint64_t addr = 0;
     if (value.getAsInteger(0, addr)) {
@@ -713,7 +727,7 @@
   }
 
   // -Ttext=value
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_Ttext)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_Ttext)) {
     llvm::StringRef value = arg->getValue();
     uint64_t addr = 0;
     if (value.getAsInteger(0, addr)) {
@@ -732,7 +746,7 @@
   //===--------------------------------------------------------------------===//
 
   // --[no-]gc-sections
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_GCSections,
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_GCSections,
                                               kOpt_NoGCSections)) {
     if (arg->getOption().matches(kOpt_GCSections)) {
       config_.options().setGCSections(true);
@@ -742,7 +756,7 @@
   }
 
   // --[no-]print-gc-sections
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_PrintGCSections,
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_PrintGCSections,
                                               kOpt_NoPrintGCSections)) {
     if (arg->getOption().matches(kOpt_PrintGCSections)) {
       config_.options().setPrintGCSections(true);
@@ -752,7 +766,7 @@
   }
 
   // --[no-]ld-generated-unwind-info
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_LDGeneratedUnwindInfo,
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_LDGeneratedUnwindInfo,
                                               kOpt_NoLDGeneratedUnwindInfo)) {
     if (arg->getOption().matches(kOpt_LDGeneratedUnwindInfo)) {
       config_.options().setGenUnwindInfo(true);
@@ -762,7 +776,7 @@
   }
 
   // --icf=mode
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_ICF)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_ICF)) {
     mcld::GeneralOptions::ICF mode =
         llvm::StringSwitch<mcld::GeneralOptions::ICF>(arg->getValue())
             .Case("none", mcld::GeneralOptions::ICF::None)
@@ -778,7 +792,7 @@
   }
 
   // --icf-iterations
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_ICFIters)) {
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_ICFIters)) {
     llvm::StringRef value = arg->getValue();
     int num;
     if (value.getAsInteger(0, num) || (num < 0)) {
@@ -790,7 +804,7 @@
   }
 
   // --[no-]print-icf-sections
-  if (llvm::opt::Arg* arg = args_->getLastArg(kOpt_PrintICFSections,
+  if (llvm::opt::Arg* arg = args.getLastArg(kOpt_PrintICFSections,
                                               kOpt_NoPrintICFSections)) {
     if (arg->getOption().matches(kOpt_PrintICFSections)) {
       config_.options().setPrintICFSections(true);
@@ -811,7 +825,7 @@
   Action action;
   actions.reserve(32);
 
-  for (llvm::opt::Arg* arg : *args_) {
+  for (llvm::opt::Arg* arg : args) {
     const unsigned index = arg->getIndex();
 
     switch (arg->getOption().getID()) {
@@ -975,53 +989,47 @@
     return false;
   }
 
+
+  //===--------------------------------------------------------------------===//
+  // Unknown
+  //===--------------------------------------------------------------------===//
+  std::vector<std::string> unknown_args = args.getAllArgValues(kOpt_UNKNOWN);
+  for (std::string arg : unknown_args)
+    mcld::warning(mcld::diag::warn_unsupported_option) << arg;
+
   return true;
 }
 
-std::unique_ptr<llvm::opt::InputArgList>
-ParseArgs(const llvm::opt::OptTable& opt_table,
-          llvm::ArrayRef<const char*> argv) {
-  unsigned missing_arg_idx;
-  unsigned missing_arg_count;
-
-  std::unique_ptr<llvm::opt::InputArgList> args(
-      opt_table.ParseArgs(argv.begin(), argv.end(), missing_arg_idx,
-                          missing_arg_count));
-  if (missing_arg_count > 0) {
-    mcld::errs() << "Argument to '" << args->getArgString(missing_arg_idx)
-                 << "' is missing (expected " << missing_arg_count
-                 << ((missing_arg_count > 1) ? " values" : " value") << ")\n";
-    return nullptr;
-  }
-
-  return args;
-}
-
 std::unique_ptr<Driver> Driver::Create(llvm::ArrayRef<const char*> argv) {
   // Parse command line options.
   OptTable opt_table;
-  std::unique_ptr<llvm::opt::InputArgList> args = ParseArgs(opt_table,
-                                                            argv.slice(1));
-  if (args == nullptr) {
+  unsigned missing_arg_idx;
+  unsigned missing_arg_count;
+  llvm::opt::InputArgList args =
+      opt_table.ParseArgs(argv.slice(1), missing_arg_idx, missing_arg_count);
+  if (missing_arg_count > 0) {
+    mcld::errs() << "Argument to '" << args.getArgString(missing_arg_idx)
+                 << "' is missing (expected " << missing_arg_count
+                 << ((missing_arg_count > 1) ? " values" : " value") << ")\n";
     return nullptr;
   }
 
-  std::unique_ptr<Driver> result(new Driver(argv[0], std::move(args)));
+  std::unique_ptr<Driver> result(new Driver(argv[0]));
 
   // Return quickly if -help is specified.
-  if (result->args_->hasArg(kOpt_Help)) {
+  if (args.hasArg(kOpt_Help)) {
     opt_table.PrintHelp(mcld::outs(), argv[0], "MCLinker",
                         /* FlagsToInclude */0, /* FlagsToExclude */0);
     return nullptr;
   }
 
   // Print version information if requested.
-  if (result->args_->hasArg(kOpt_Version)) {
+  if (args.hasArg(kOpt_Version)) {
     mcld::outs() << result->config_.options().getVersionString() << "\n";
   }
 
   // Setup instance from arguments.
-  if (!result->TranslateArguments()) {
+  if (!result->TranslateArguments(args)) {
     return nullptr;
   }
 
diff --git a/tools/mcld/Options.td b/tools/mcld/Options.td
index 80655a7..d8bc050 100644
--- a/tools/mcld/Options.td
+++ b/tools/mcld/Options.td
@@ -247,10 +247,6 @@
                   Group<OutputGroup>,
                   HelpText<"Allow linking together mismatched input files">;
 
-def BuildID : Flag<["--"], "build-id">,
-              Group<OutputGroup>,
-              HelpText<"Request creation of .note.gnu.build-id ELF note section">;
-
 //===----------------------------------------------------------------------===//
 // Positional
 //===----------------------------------------------------------------------===//
@@ -324,7 +320,7 @@
 def PreferenceGroup : OptionGroup<"preference">,
                       HelpText<"PREFERENCE OPTIONS">;
 
-def Color : Joined<["--"], "colormc=">,
+def Color : Joined<["--"], "color=">,
             Group<PreferenceGroup>,
             HelpText<"Surround the result strings with the marker">;
 
@@ -435,10 +431,6 @@
             Group<SearchpathGroup>,
             HelpText<"Add a directory to the runtime library search path">;
 
-def RPathLink : Joined<["-"], "rpath-link=">,
-                Group<SearchpathGroup>,
-                HelpText<"Add a directory to the link time library search path">;
-
 //===----------------------------------------------------------------------===//
 // Symbol
 //===----------------------------------------------------------------------===//
@@ -490,3 +482,15 @@
 def Emulation : Separate<["-"], "m">,
                 Group<TargetGroup>,
                 HelpText<"Set GNU linker emulation">;
+
+def StubGroupSize : Joined<["--"], "stub-group-size=">,
+                    Group<TargetGroup>,
+                    HelpText<"Set the group size to place stubs between sections">;
+
+def FixCA53Erratum835769 : Flag<["--"], "fix-cortex-a53-835769">,
+                           Group<TargetGroup>,
+                           HelpText<"Enable fix for cortex a53 erratum 835769">;
+
+def FixCA53Erratum843419 : Flag<["--"], "fix-cortex-a53-843419">,
+                           Group<TargetGroup>,
+                           HelpText<"Enable fix for cortex a53 erratum 843419">;