Refactoring: move mips reloc to separate method

Change-Id: I712614853e3f0e515f5c2bdd8f0aaa5feeae8e55
diff --git a/linker/Android.mk b/linker/Android.mk
index d6e0095..720389f 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -16,8 +16,8 @@
 LOCAL_SRC_FILES_arm64   := arch/arm64/begin.S
 LOCAL_SRC_FILES_x86     := arch/x86/begin.c
 LOCAL_SRC_FILES_x86_64  := arch/x86_64/begin.S
-LOCAL_SRC_FILES_mips    := arch/mips/begin.S
-LOCAL_SRC_FILES_mips64  := arch/mips64/begin.S
+LOCAL_SRC_FILES_mips    := arch/mips/begin.S linker_mips.cpp
+LOCAL_SRC_FILES_mips64  := arch/mips64/begin.S linker_mips.cpp
 
 LOCAL_LDFLAGS := \
     -shared \
diff --git a/linker/linker.cpp b/linker/linker.cpp
index a438cfc..0b0afc3 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -122,14 +122,6 @@
 
 __LIBC_HIDDEN__ abort_msg_t* g_abort_message = nullptr; // For debuggerd.
 
-enum RelocationKind {
-  kRelocAbsolute = 0,
-  kRelocRelative,
-  kRelocCopy,
-  kRelocSymbol,
-  kRelocMax
-};
-
 #if STATS
 struct linker_stats_t {
   int count[kRelocMax];
@@ -137,30 +129,16 @@
 
 static linker_stats_t linker_stats;
 
-static void count_relocation(RelocationKind kind) {
+void count_relocation(RelocationKind kind) {
   ++linker_stats.count[kind];
 }
 #else
-static void count_relocation(RelocationKind) {
+void count_relocation(RelocationKind) {
 }
 #endif
 
 #if COUNT_PAGES
-static unsigned bitmask[4096];
-#if defined(__LP64__)
-#define MARK(offset) \
-    do { \
-      if ((((offset) >> 12) >> 5) < 4096) \
-          bitmask[((offset) >> 12) >> 5] |= (1 << (((offset) >> 12) & 31)); \
-    } while (0)
-#else
-#define MARK(offset) \
-    do { \
-      bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \
-    } while (0)
-#endif
-#else
-#define MARK(x) do {} while (0)
+uint32_t bitmask[4096];
 #endif
 
 // You shouldn't try to call memory-allocating functions in the dynamic linker.
@@ -539,7 +517,7 @@
   return gnu_hash_;
 }
 
-static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
+ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
     const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) {
   SymbolName symbol_name(name);
   ElfW(Sym)* s = nullptr;
@@ -1292,10 +1270,9 @@
   return 0;
 }
 #endif
-#endif
 
 template<typename ElfRelT>
-int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+bool soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
   for (size_t idx = 0; idx < count; ++idx, ++rel) {
     ElfW(Word) type = ELFW(R_TYPE)(rel->r_info);
     ElfW(Word) sym = ELFW(R_SYM)(rel->r_info);
@@ -1303,11 +1280,9 @@
     ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias);
     ElfW(Addr) sym_addr = 0;
     const char* sym_name = nullptr;
-#if !defined(__mips__)
     ElfW(Addr) addend = get_addend(rel, reloc);
-#endif
 
-    DEBUG("Processing '%s' relocation at index %zd", name, idx);
+    DEBUG("Processing '%s' relocation at index %zd", this->name, idx);
     if (type == R_GENERIC_NONE) {
       continue;
     }
@@ -1323,7 +1298,7 @@
         s = &symtab_[sym];
         if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
           DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name);
-          return -1;
+          return false;
         }
 
         /* IHI0044C AAELF 4.5.1.1:
@@ -1339,7 +1314,6 @@
          */
 
         switch (type) {
-#if !defined(__mips__)
           case R_GENERIC_JUMP_SLOT:
           case R_GENERIC_GLOB_DAT:
           case R_GENERIC_RELATIVE:
@@ -1371,10 +1345,9 @@
             sym_addr = reloc;
             break;
 #endif
-#endif
           default:
             DL_ERR("unknown weak reloc type %d @ %p (%zu)", type, rel, idx);
-            return -1;
+            return false;
         }
       } else {
         // We got a definition.
@@ -1384,7 +1357,6 @@
     }
 
     switch (type) {
-#if !defined(__mips__)
       case R_GENERIC_JUMP_SLOT:
         count_relocation(kRelocAbsolute);
         MARK(rel->r_offset);
@@ -1418,7 +1390,6 @@
                     reinterpret_cast<void*>(base + addend));
         *reinterpret_cast<ElfW(Addr)*>(reloc) = call_ifunc_resolver(base + addend);
         break;
-#endif
 
 #if defined(__aarch64__)
       case R_AARCH64_ABS64:
@@ -1441,7 +1412,7 @@
                  (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend)),
                  static_cast<ElfW(Addr)>(INT32_MIN),
                  static_cast<ElfW(Addr)>(UINT32_MAX));
-          return -1;
+          return false;
         }
         break;
       case R_AARCH64_ABS16:
@@ -1457,7 +1428,7 @@
                  (*reinterpret_cast<ElfW(Addr)*>(reloc) + (sym_addr + addend)),
                  static_cast<ElfW(Addr)>(INT16_MIN),
                  static_cast<ElfW(Addr)>(UINT16_MAX));
-          return -1;
+          return false;
         }
         break;
       case R_AARCH64_PREL64:
@@ -1480,7 +1451,7 @@
                  (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset)),
                  static_cast<ElfW(Addr)>(INT32_MIN),
                  static_cast<ElfW(Addr)>(UINT32_MAX));
-          return -1;
+          return false;
         }
         break;
       case R_AARCH64_PREL16:
@@ -1496,7 +1467,7 @@
                  (*reinterpret_cast<ElfW(Addr)*>(reloc) + ((sym_addr + addend) - rel->r_offset)),
                  static_cast<ElfW(Addr)>(INT16_MIN),
                  static_cast<ElfW(Addr)>(UINT16_MAX));
-          return -1;
+          return false;
         }
         break;
 
@@ -1511,7 +1482,7 @@
          * set to ET_EXEC.
          */
         DL_ERR("%s R_AARCH64_COPY relocations are not supported", name);
-        return -1;
+        return false;
       case R_AARCH64_TLS_TPREL64:
         TRACE_TYPE(RELO, "RELO TLS_TPREL64 *** %16llx <- %16llx - %16llx\n",
                    reloc, (sym_addr + addend), rel->r_offset);
@@ -1568,7 +1539,7 @@
          * set to ET_EXEC.
          */
         DL_ERR("%s R_ARM_COPY relocations are not supported", name);
-        return -1;
+        return false;
 #elif defined(__i386__)
       case R_386_32:
         count_relocation(kRelocRelative);
@@ -1583,87 +1554,15 @@
                    reloc, (sym_addr - reloc), sym_addr, reloc, sym_name);
         *reinterpret_cast<ElfW(Addr)*>(reloc) += (sym_addr - reloc);
         break;
-#elif defined(__mips__)
-      case R_MIPS_REL32:
-#if defined(__LP64__)
-        // MIPS Elf64_Rel entries contain compound relocations
-        // We only handle the R_MIPS_NONE|R_MIPS_64|R_MIPS_REL32 case
-        if (ELF64_R_TYPE2(rel->r_info) != R_MIPS_64 ||
-            ELF64_R_TYPE3(rel->r_info) != R_MIPS_NONE) {
-          DL_ERR("Unexpected compound relocation type:%d type2:%d type3:%d @ %p (%zu)",
-                 type, (unsigned)ELF64_R_TYPE2(rel->r_info),
-                 (unsigned)ELF64_R_TYPE3(rel->r_info), rel, idx);
-          return -1;
-        }
-#endif
-        count_relocation(kRelocAbsolute);
-        MARK(rel->r_offset);
-        TRACE_TYPE(RELO, "RELO REL32 %08zx <- %08zx %s", static_cast<size_t>(reloc),
-                   static_cast<size_t>(sym_addr), sym_name ? sym_name : "*SECTIONHDR*");
-        if (s) {
-          *reinterpret_cast<ElfW(Addr)*>(reloc) += sym_addr;
-        } else {
-          *reinterpret_cast<ElfW(Addr)*>(reloc) += base;
-        }
-        break;
 #endif
       default:
         DL_ERR("unknown reloc type %d @ %p (%zu)", type, rel, idx);
-        return -1;
-    }
-  }
-  return 0;
-}
-
-#if defined(__mips__)
-bool soinfo::mips_relocate_got(const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
-  ElfW(Addr)** got = plt_got_;
-  if (got == nullptr) {
-    return true;
-  }
-
-  // got[0] is the address of the lazy resolver function.
-  // got[1] may be used for a GNU extension.
-  // Set it to a recognizable address in case someone calls it (should be _rtld_bind_start).
-  // FIXME: maybe this should be in a separate routine?
-  if ((flags_ & FLAG_LINKER) == 0) {
-    size_t g = 0;
-    got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadbeef);
-    if (reinterpret_cast<intptr_t>(got[g]) < 0) {
-      got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadfeed);
-    }
-    // Relocate the local GOT entries.
-    for (; g < mips_local_gotno_; g++) {
-      got[g] = reinterpret_cast<ElfW(Addr)*>(reinterpret_cast<uintptr_t>(got[g]) + load_bias);
-    }
-  }
-
-  // Now for the global GOT entries...
-  ElfW(Sym)* sym = symtab_ + mips_gotsym_;
-  got = plt_got_ + mips_local_gotno_;
-  for (size_t g = mips_gotsym_; g < mips_symtabno_; g++, sym++, got++) {
-    // This is an undefined reference... try to locate it.
-    const char* sym_name = get_string(sym->st_name);
-    soinfo* lsi = nullptr;
-    ElfW(Sym)* s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group);
-    if (s == nullptr) {
-      // We only allow an undefined symbol if this is a weak reference.
-      s = &symtab_[g];
-      if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
-        DL_ERR("cannot locate \"%s\"...", sym_name);
         return false;
-      }
-      *got = 0;
-    } else {
-      // FIXME: is this sufficient?
-      // For reference see NetBSD link loader
-      // http://cvsweb.netbsd.org/bsdweb.cgi/src/libexec/ld.elf_so/arch/mips/mips_reloc.c?rev=1.53&content-type=text/x-cvsweb-markup
-      *got = reinterpret_cast<ElfW(Addr)*>(lsi->resolve_symbol_address(s));
     }
   }
   return true;
 }
-#endif
+#endif  // !defined(__mips__)
 
 void soinfo::call_array(const char* array_name __unused, linker_function_t* functions, size_t count, bool reverse) {
   if (functions == nullptr) {
@@ -2352,26 +2251,26 @@
 #if defined(USE_RELA)
   if (rela_ != nullptr) {
     DEBUG("[ relocating %s ]", name);
-    if (relocate(rela_, rela_count_, global_group, local_group)) {
+    if (!relocate(rela_, rela_count_, global_group, local_group)) {
       return false;
     }
   }
   if (plt_rela_ != nullptr) {
     DEBUG("[ relocating %s plt ]", name);
-    if (relocate(plt_rela_, plt_rela_count_, global_group, local_group)) {
+    if (!relocate(plt_rela_, plt_rela_count_, global_group, local_group)) {
       return false;
     }
   }
 #else
   if (rel_ != nullptr) {
     DEBUG("[ relocating %s ]", name);
-    if (relocate(rel_, rel_count_, global_group, local_group)) {
+    if (!relocate(rel_, rel_count_, global_group, local_group)) {
       return false;
     }
   }
   if (plt_rel_ != nullptr) {
     DEBUG("[ relocating %s plt ]", name);
-    if (relocate(plt_rel_, plt_rel_count_, global_group, local_group)) {
+    if (!relocate(plt_rel_, plt_rel_count_, global_group, local_group)) {
       return false;
     }
   }
diff --git a/linker/linker.h b/linker/linker.h
index 4f6b585..2afbaf6 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -287,7 +287,7 @@
   void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse);
   void call_function(const char* function_name, linker_function_t function);
   template<typename ElfRelT>
-  int relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
+  bool relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group);
 
  private:
   // This part of the structure is only available
@@ -318,6 +318,19 @@
   friend soinfo* get_libdl_info();
 };
 
+ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in,
+    const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group);
+
+enum RelocationKind {
+  kRelocAbsolute = 0,
+  kRelocRelative,
+  kRelocCopy,
+  kRelocSymbol,
+  kRelocMax
+};
+
+void count_relocation(RelocationKind kind);
+
 soinfo* get_libdl_info();
 
 void do_android_get_LD_LIBRARY_PATH(char*, size_t);
diff --git a/linker/linker_debug.h b/linker/linker_debug.h
index 0c7a784..5ded5ab 100644
--- a/linker/linker_debug.h
+++ b/linker/linker_debug.h
@@ -82,4 +82,23 @@
 
 #define TRACE_TYPE(t, x...)   do { if (DO_TRACE_##t) { TRACE(x); } } while (0)
 
+#if COUNT_PAGES
+extern uint32_t bitmask[];
+#if defined(__LP64__)
+#define MARK(offset) \
+    do { \
+      if ((((offset) >> 12) >> 5) < 4096) \
+          bitmask[((offset) >> 12) >> 5] |= (1 << (((offset) >> 12) & 31)); \
+    } while (0)
+#else
+#define MARK(offset) \
+    do { \
+      bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \
+    } while (0)
+#endif
+#else
+#define MARK(x) do {} while (0)
+
+#endif
+
 #endif /* _LINKER_DEBUG_H_ */
diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp
new file mode 100644
index 0000000..eb2ae84
--- /dev/null
+++ b/linker/linker_mips.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "linker.h"
+#include "linker_debug.h"
+#include "linker_relocs.h"
+
+template<>
+bool soinfo::relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+  for (size_t idx = 0; idx < count; ++idx, ++rel) {
+    ElfW(Word) type = ELFW(R_TYPE)(rel->r_info);
+    ElfW(Word) sym = ELFW(R_SYM)(rel->r_info);
+
+    ElfW(Addr) reloc = static_cast<ElfW(Addr)>(rel->r_offset + load_bias);
+    ElfW(Addr) sym_addr = 0;
+    const char* sym_name = nullptr;
+
+    DEBUG("Processing '%s' relocation at index %zd", this->name, idx);
+    if (type == R_GENERIC_NONE) {
+      continue;
+    }
+
+    ElfW(Sym)* s = nullptr;
+    soinfo* lsi = nullptr;
+
+    if (sym != 0) {
+      sym_name = get_string(symtab_[sym].st_name);
+      s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group);
+      if (s == nullptr) {
+        // mips does not support relocation with weak-undefined symbols
+        DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name);
+        return false;
+      } else {
+        // We got a definition.
+        sym_addr = lsi->resolve_symbol_address(s);
+      }
+      count_relocation(kRelocSymbol);
+    }
+
+    switch (type) {
+      case R_MIPS_REL32:
+#if defined(__LP64__)
+        // MIPS Elf64_Rel entries contain compound relocations
+        // We only handle the R_MIPS_NONE|R_MIPS_64|R_MIPS_REL32 case
+        if (ELF64_R_TYPE2(rel->r_info) != R_MIPS_64 ||
+            ELF64_R_TYPE3(rel->r_info) != R_MIPS_NONE) {
+          DL_ERR("Unexpected compound relocation type:%d type2:%d type3:%d @ %p (%zu)",
+                 type, (unsigned)ELF64_R_TYPE2(rel->r_info),
+                 (unsigned)ELF64_R_TYPE3(rel->r_info), rel, idx);
+          return false;
+        }
+#endif
+        count_relocation(s == nullptr ? kRelocAbsolute : kRelocRelative);
+        MARK(rel->r_offset);
+        TRACE_TYPE(RELO, "RELO REL32 %08zx <- %08zx %s", static_cast<size_t>(reloc),
+                   static_cast<size_t>(sym_addr), sym_name ? sym_name : "*SECTIONHDR*");
+        if (s != nullptr) {
+          *reinterpret_cast<ElfW(Addr)*>(reloc) += sym_addr;
+        } else {
+          *reinterpret_cast<ElfW(Addr)*>(reloc) += base;
+        }
+        break;
+      default:
+        DL_ERR("unknown reloc type %d @ %p (%zu)", type, rel, idx);
+        return false;
+    }
+  }
+  return true;
+}
+
+bool soinfo::mips_relocate_got(const soinfo_list_t& global_group, const soinfo_list_t& local_group) {
+  ElfW(Addr)** got = plt_got_;
+  if (got == nullptr) {
+    return true;
+  }
+
+  // got[0] is the address of the lazy resolver function.
+  // got[1] may be used for a GNU extension.
+  // Set it to a recognizable address in case someone calls it (should be _rtld_bind_start).
+  // FIXME: maybe this should be in a separate routine?
+  if ((flags_ & FLAG_LINKER) == 0) {
+    size_t g = 0;
+    got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadbeef);
+    if (reinterpret_cast<intptr_t>(got[g]) < 0) {
+      got[g++] = reinterpret_cast<ElfW(Addr)*>(0xdeadfeed);
+    }
+    // Relocate the local GOT entries.
+    for (; g < mips_local_gotno_; g++) {
+      got[g] = reinterpret_cast<ElfW(Addr)*>(reinterpret_cast<uintptr_t>(got[g]) + load_bias);
+    }
+  }
+
+  // Now for the global GOT entries...
+  ElfW(Sym)* sym = symtab_ + mips_gotsym_;
+  got = plt_got_ + mips_local_gotno_;
+  for (size_t g = mips_gotsym_; g < mips_symtabno_; g++, sym++, got++) {
+    // This is an undefined reference... try to locate it.
+    const char* sym_name = get_string(sym->st_name);
+    soinfo* lsi = nullptr;
+    ElfW(Sym)* s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group);
+    if (s == nullptr) {
+      // We only allow an undefined symbol if this is a weak reference.
+      s = &symtab_[g];
+      if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
+        DL_ERR("cannot locate \"%s\"...", sym_name);
+        return false;
+      }
+      *got = 0;
+    } else {
+      // FIXME: is this sufficient?
+      // For reference see NetBSD link loader
+      // http://cvsweb.netbsd.org/bsdweb.cgi/src/libexec/ld.elf_so/arch/mips/mips_reloc.c?rev=1.53&content-type=text/x-cvsweb-markup
+      *got = reinterpret_cast<ElfW(Addr)*>(lsi->resolve_symbol_address(s));
+    }
+  }
+  return true;
+}
+