[ELF2][mips] Support both big and little endian MIPS 32-bit targets

- Make the `MipsTargetInfo` template class with `ELFType` argument. Use
  the argument to select an appropriate relocation type and read/write
  routines.
- Add template function `add32` to add-and-write relocation value in
  both big and little endian cases. Keep the `add32le` to reduce code
  changes.

Differential Revision: http://reviews.llvm.org/D13723

llvm-svn: 250297
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index cd9ecb2..4ae54b4 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -39,7 +39,14 @@
   case EM_AARCH64:
     return new AArch64TargetInfo();
   case EM_MIPS:
-    return new MipsTargetInfo();
+    switch (Config->EKind) {
+    case ELF32LEKind:
+      return new MipsTargetInfo<ELF32LE>();
+    case ELF32BEKind:
+      return new MipsTargetInfo<ELF32BE>();
+    default:
+      error("Unsupported MIPS target");
+    }
   case EM_PPC:
     return new PPCTargetInfo();
   case EM_PPC64:
@@ -84,8 +91,13 @@
 }
 
 static void add32le(uint8_t *L, int32_t V) { write32le(L, read32le(L) + V); }
+static void add32be(uint8_t *L, int32_t V) { write32be(L, read32be(L) + V); }
 static void or32le(uint8_t *L, int32_t V) { write32le(L, read32le(L) | V); }
 
+template <bool IsLE> static void add32(uint8_t *L, int32_t V);
+template <> void add32<true>(uint8_t *L, int32_t V) { add32le(L, V); }
+template <> void add32<false>(uint8_t *L, int32_t V) { add32be(L, V); }
+
 void X86TargetInfo::relocateOne(uint8_t *Buf, uint8_t *BufEnd, const void *RelP,
                                 uint32_t Type, uint64_t BaseAddr,
                                 uint64_t SymVA) const {
@@ -542,32 +554,40 @@
   }
 }
 
-MipsTargetInfo::MipsTargetInfo() {
+template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
   // PCRelReloc = FIXME
   // GotReloc = FIXME
   PageSize = 65536;
 }
 
-void MipsTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
-                                   uint64_t PltEntryAddr) const {}
+template <class ELFT>
+void MipsTargetInfo<ELFT>::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr,
+                                         uint64_t PltEntryAddr) const {}
 
-bool MipsTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
+template <class ELFT>
+bool MipsTargetInfo<ELFT>::relocNeedsGot(uint32_t Type,
+                                         const SymbolBody &S) const {
   return false;
 }
 
-bool MipsTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
+template <class ELFT>
+bool MipsTargetInfo<ELFT>::relocNeedsPlt(uint32_t Type,
+                                         const SymbolBody &S) const {
   return false;
 }
 
-void MipsTargetInfo::relocateOne(uint8_t *Buf, uint8_t *BufEnd,
-                                 const void *RelP, uint32_t Type,
-                                 uint64_t BaseAddr, uint64_t SymVA) const {
-  typedef ELFFile<ELF32LE>::Elf_Rel Elf_Rel;
+template <class ELFT>
+void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Buf, uint8_t *BufEnd,
+                                       const void *RelP, uint32_t Type,
+                                       uint64_t BaseAddr,
+                                       uint64_t SymVA) const {
+  const bool IsLE = ELFT::TargetEndianness == support::little;
+  typedef typename ELFFile<ELFT>::Elf_Rel Elf_Rel;
   auto &Rel = *reinterpret_cast<const Elf_Rel *>(RelP);
 
   switch (Type) {
   case R_MIPS_32:
-    add32le(Buf + Rel.r_offset, SymVA);
+    add32<IsLE>(Buf + Rel.r_offset, SymVA);
     break;
   default:
     error("unrecognized reloc " + Twine(Type));