MIPS support to the linker

Change-Id: I37ec2d6c51d82bb9e9dbfef4b38c85366bead255
Signed-off-by: Chris Dearman <chris@mips.com>
Signed-off-by: Raghu Gandham <raghu@mips.com>
Signed-off-by: Bhanu Chetlapalli <bhanu@mips.com>
diff --git a/linker/Android.mk b/linker/Android.mk
index c9d053f..d207f95 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -16,7 +16,8 @@
 LOCAL_CFLAGS += -fno-stack-protector \
         -Wstrict-overflow=5 \
         -fvisibility=hidden \
-        -std=gnu99
+        -std=gnu99 \
+        -Wall -Wextra
 
 # Set LINKER_DEBUG to either 1 or 0
 #
@@ -33,11 +34,15 @@
     -I$(LOCAL_PATH)/../libc/arch-$(TARGET_ARCH)/bionic
 
 ifeq ($(TARGET_ARCH),arm)
-LOCAL_CFLAGS += -DANDROID_ARM_LINKER
-else
-  ifeq ($(TARGET_ARCH),x86)
+    LOCAL_CFLAGS += -DANDROID_ARM_LINKER
+endif
+
+ifeq ($(TARGET_ARCH),x86)
     LOCAL_CFLAGS += -DANDROID_X86_LINKER
-  endif
+endif
+
+ifeq ($(TARGET_ARCH),mips)
+    LOCAL_CFLAGS += -DANDROID_MIPS_LINKER
 endif
 
 LOCAL_MODULE:= linker
diff --git a/linker/arch/mips/begin.S b/linker/arch/mips/begin.S
new file mode 100644
index 0000000..b782947
--- /dev/null
+++ b/linker/arch/mips/begin.S
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+    .text
+    .align    4
+    .type    __start,@function
+
+    .ent    __start
+    .globl    __start
+__start:
+    .set    noreorder
+    bal    1f
+    nop
+1:  .cpload    $31
+    .set    reorder
+
+    /* Discover the load address */
+    la    $t0, 1f
+    bal    1f
+1:  subu    $t0, $ra, $t0
+
+#define DT_PLTGOT 3
+#define DT_MIPS_LOCAL_GOTNO 0x7000000a
+
+    /* Search dynamic table for DT_MIPS_LOCAL_GOTNO and DT_PLTGOT values */
+    la    $t1, _DYNAMIC
+    addu    $t1, $t0
+    li    $t3, DT_PLTGOT
+    li    $t4, DT_MIPS_LOCAL_GOTNO
+0:
+    lw    $t2, 0($t1)
+    beqz    $t2, .Lrelocate_local_got
+
+    bne    $t2, $t3, 1f    /* DT_PLTGOT? */
+    lw    $s0, 4($t1)
+    addu    $s0, $t0
+    b    2f
+
+1:  bne    $t2, $t4, 1f    /* DT_MIPS_LOCAL_GOTNO? */
+    lw    $s1, 4($t1)
+    b    2f
+
+1:
+2:  addu    $t1, 8
+    b    0b
+
+.Lrelocate_local_got:
+    /*
+     * Relocate the local GOT entries
+     * got[0] is address of lazy resolver function
+     * got[1] may be used for a GNU extension
+     */
+
+    addu    $s0, 4
+    subu    $s1, 1
+    lw    $t1, ($s0)
+    bgez    $t1, 9f
+    addu    $s0, 4
+    subu    $s1, 1
+    b    9f
+
+1:  lw    $t1, ($s0)
+    addu    $t1, $t0
+    sw    $t1, ($s0)
+    addu    $s0, 4
+9:  subu    $s1, 1
+    bgez    $s1, 1b
+
+    /* call linker_init */
+    move    $a0, $sp
+    addiu    $sp, -4*4        /* space for arg saves in linker_init */
+    la    $t9, __linker_init
+    jalr    $t9
+    move    $t9, $v0
+    addu    $sp, 4*4        /* restore sp */
+    j    $t9
+    .end    __start
+
+    .section .ctors, "wa"
+    .globl __CTOR_LIST__
+__CTOR_LIST__:
+    .long -1
diff --git a/linker/debugger.c b/linker/debugger.c
index 756b5cf..7a1dd15 100644
--- a/linker/debugger.c
+++ b/linker/debugger.c
@@ -131,7 +131,9 @@
         case SIGBUS:    signame = "SIGBUS";     break;
         case SIGFPE:    signame = "SIGFPE";     break;
         case SIGSEGV:   signame = "SIGSEGV";    break;
+#if defined(SIGSTKFLT)
         case SIGSTKFLT: signame = "SIGSTKFLT";  break;
+#endif
         case SIGPIPE:   signame = "SIGPIPE";    break;
         default:        signame = "???";        break;
     }
@@ -214,7 +216,9 @@
         case SIGABRT:
         case SIGFPE:
         case SIGPIPE:
+#ifdef SIGSTKFLT
         case SIGSTKFLT:
+#endif
             (void) tgkill(getpid(), gettid(), n);
             break;
         default:    // SIGILL, SIGBUS, SIGSEGV
@@ -235,6 +239,8 @@
     sigaction(SIGBUS, &act, NULL);
     sigaction(SIGFPE, &act, NULL);
     sigaction(SIGSEGV, &act, NULL);
+#if defined(SIGSTKFLT)
     sigaction(SIGSTKFLT, &act, NULL);
+#endif
     sigaction(SIGPIPE, &act, NULL);
 }
diff --git a/linker/dlfcn.c b/linker/dlfcn.c
index 3d0384f..13064e5 100644
--- a/linker/dlfcn.c
+++ b/linker/dlfcn.c
@@ -170,13 +170,13 @@
 #define ANDROID_LIBDL_STRTAB \
                       "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_unwind_find_exidx\0"
 
-#elif defined(ANDROID_X86_LINKER)
+#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
 //                     0000000 00011111 111112 22222222 2333333 3333444444444455
 //                     0123456 78901234 567890 12345678 9012345 6789012345678901
 #define ANDROID_LIBDL_STRTAB \
                       "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0dl_iterate_phdr\0"
 #else
-#error Unsupported architecture. Only ARM and x86 are presently supported.
+#error Unsupported architecture. Only ARM, MIPS, and x86 are presently supported.
 #endif
 
 
@@ -218,7 +218,7 @@
       st_info: STB_GLOBAL << 4,
       st_shndx: 1,
     },
-#elif defined(ANDROID_X86_LINKER)
+#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
     { st_name: 36,
       st_value: (Elf32_Addr) &dl_iterate_phdr,
       st_info: STB_GLOBAL << 4,
diff --git a/linker/linker.c b/linker/linker.c
index 0a93130..e0c3bce 100644
--- a/linker/linker.c
+++ b/linker/linker.c
@@ -142,9 +142,9 @@
 static char __linker_dl_err_buf[768];
 #define DL_ERR(fmt, x...)                                                     \
     do {                                                                      \
-        format_buffer(__linker_dl_err_buf, sizeof(__linker_dl_err_buf),            \
+        format_buffer(__linker_dl_err_buf, sizeof(__linker_dl_err_buf),       \
                  "%s[%d]: " fmt, __func__, __LINE__, ##x);                    \
-        ERROR(fmt "\n", ##x);                                                      \
+        ERROR(fmt "\n", ##x);                                                 \
     } while(0)
 
 const char *linker_get_error(void)
@@ -350,7 +350,7 @@
    *pcount = 0;
     return NULL;
 }
-#elif defined(ANDROID_X86_LINKER)
+#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
 /* Here, we only have to provide a callback to iterate across all the
  * loaded libraries. gcc_eh does the rest. */
 int
@@ -455,7 +455,7 @@
             lsi = (soinfo *)d[1];
             if (!validate_soinfo(lsi)) {
                 DL_ERR("%5d bad DT_NEEDED pointer in %s",
-                       pid, si->name);
+                       pid, lsi->name);
                 return NULL;
             }
 
@@ -691,6 +691,8 @@
     if (hdr->e_machine != EM_ARM) return -1;
 #elif defined(ANDROID_X86_LINKER)
     if (hdr->e_machine != EM_386) return -1;
+#elif defined(ANDROID_MIPS_LINKER)
+    if (hdr->e_machine != EM_MIPS) return -1;
 #endif
     return 0;
 }
@@ -975,7 +977,7 @@
     Elf32_Rel *start = rel;
     unsigned idx;
 
-    for (idx = 0; idx < count; ++idx) {
+    for (idx = 0; idx < count; ++idx, ++rel) {
         unsigned type = ELF32_R_TYPE(rel->r_info);
         unsigned sym = ELF32_R_SYM(rel->r_info);
         unsigned reloc = (unsigned)(rel->r_offset + si->load_bias);
@@ -984,6 +986,9 @@
 
         DEBUG("%5d Processing '%s' relocation at index %d\n", pid,
               si->name, idx);
+        if (type == 0) { // R_*_NONE
+            continue;
+        }
         if(sym != 0) {
             sym_name = (char *)(strtab + symtab[sym].st_name);
             s = soinfo_do_lookup(si, sym_name, &offset);
@@ -1015,9 +1020,8 @@
                 case R_ARM_GLOB_DAT:
                 case R_ARM_ABS32:
                 case R_ARM_RELATIVE:    /* Don't care. */
-                case R_ARM_NONE:        /* Don't care. */
 #elif defined(ANDROID_X86_LINKER)
-                case R_386_JUMP_SLOT:
+                case R_386_JMP_SLOT:
                 case R_386_GLOB_DAT:
                 case R_386_32:
                 case R_386_RELATIVE:    /* Dont' care. */
@@ -1046,15 +1050,15 @@
             } else {
                 /* We got a definition.  */
 #if 0
-            if((base == 0) && (si->base != 0)){
-                    /* linking from libraries to main image is bad */
-                DL_ERR("%5d cannot locate '%s'...",
-                       pid, strtab + symtab[sym].st_name);
-                return -1;
-            }
+                if((base == 0) && (si->base != 0)){
+                        /* linking from libraries to main image is bad */
+                    DL_ERR("%5d cannot locate '%s'...",
+                           pid, strtab + symtab[sym].st_name);
+                    return -1;
+                }
 #endif
                 sym_addr = (unsigned)(s->st_value + offset);
-	    }
+            }
             COUNT_RELOC(RELOC_SYMBOL);
         } else {
             s = NULL;
@@ -1094,7 +1098,7 @@
             *((unsigned*)reloc) += sym_addr - rel->r_offset;
             break;
 #elif defined(ANDROID_X86_LINKER)
-        case R_386_JUMP_SLOT:
+        case R_386_JMP_SLOT:
             COUNT_RELOC(RELOC_ABSOLUTE);
             MARK(rel->r_offset);
             TRACE_TYPE(RELO, "%5d RELO JMP_SLOT %08x <- %08x %s\n", pid,
@@ -1108,6 +1112,25 @@
                        reloc, sym_addr, sym_name);
             *((unsigned*)reloc) = sym_addr;
             break;
+#elif defined(ANDROID_MIPS_LINKER)
+    case R_MIPS_JUMP_SLOT:
+            COUNT_RELOC(RELOC_ABSOLUTE);
+            MARK(rel->r_offset);
+            TRACE_TYPE(RELO, "%5d RELO JMP_SLOT %08x <- %08x %s\n", pid,
+                       reloc, sym_addr, sym_name);
+            *((unsigned*)reloc) = sym_addr;
+            break;
+    case R_MIPS_REL32:
+            COUNT_RELOC(RELOC_ABSOLUTE);
+            MARK(rel->r_offset);
+            TRACE_TYPE(RELO, "%5d RELO REL32 %08x <- %08x %s\n", pid,
+                       reloc, sym_addr, (sym_name) ? sym_name : "*SECTIONHDR*");
+            if (s) {
+                *((unsigned*)reloc) += sym_addr;
+            } else {
+                *((unsigned*)reloc) += si->base;
+            }
+            break;
 #endif /* ANDROID_*_LINKER */
 
 #if defined(ANDROID_ARM_LINKER)
@@ -1154,8 +1177,6 @@
                        reloc, s->st_size, sym_addr, sym_name);
             memcpy((void*)reloc, (void*)sym_addr, s->st_size);
             break;
-        case R_ARM_NONE:
-            break;
 #endif /* ANDROID_ARM_LINKER */
 
         default:
@@ -1163,11 +1184,79 @@
                   pid, type, rel, (int) (rel - start));
             return -1;
         }
-        rel++;
     }
     return 0;
 }
 
+#ifdef ANDROID_MIPS_LINKER
+int mips_relocate_got(struct soinfo *si)
+{
+    unsigned *got;
+    unsigned local_gotno, gotsym, symtabno;
+    Elf32_Sym *symtab, *sym;
+    unsigned g;
+
+    got = si->plt_got;
+    local_gotno = si->mips_local_gotno;
+    gotsym = si->mips_gotsym;
+    symtabno = si->mips_symtabno;
+    symtab = si->symtab;
+
+    /*
+     * got[0] is address of lazy resolver function
+     * got[1] may be used for a GNU extension
+     * set it to a recognisable address in case someone calls it
+     * (should be _rtld_bind_start)
+     * FIXME: maybe this should be in a separate routine
+     */
+
+    if ((si->flags & FLAG_LINKER) == 0) {
+        g = 0;
+        got[g++] = 0xdeadbeef;
+        if (got[g] & 0x80000000) {
+            got[g++] = 0xdeadfeed;
+        }
+        /*
+         * Relocate the local GOT entries need to be relocated
+         */
+        for (; g < local_gotno; g++) {
+            got[g] += si->load_bias;
+        }
+    }
+
+    /* Now for the global GOT entries */
+    sym = symtab + gotsym;
+    got = si->plt_got + local_gotno;
+    for (g = gotsym; g < symtabno; g++, sym++, got++) {
+        const char *sym_name;
+        unsigned base;
+        Elf32_Sym *s;
+
+        /* This is an undefined reference... try to locate it */
+        sym_name = si->strtab + sym->st_name;
+        s = soinfo_do_lookup(si, sym_name, &base);
+        if (s == NULL) {
+            /* We only allow an undefined symbol if this is a weak
+               reference..   */
+            s = &symtab[g];
+            if (ELF32_ST_BIND(s->st_info) != STB_WEAK) {
+                DL_ERR("%5d cannot locate '%s'...\n", pid, sym_name);
+                return -1;
+            }
+            *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 = base + s->st_value;
+        }
+    }
+    return 0;
+}
+#endif
+
 /* Please read the "Initialization and Termination functions" functions.
  * of the linker design note in bionic/linker/README.TXT to understand
  * what the following code is doing.
@@ -1442,8 +1531,10 @@
             si->plt_got = (unsigned *)(base + *d);
             break;
         case DT_DEBUG:
+#if !defined(ANDROID_MIPS_LINKER)
             // Set the DT_DEBUG entry to the addres of _r_debug for GDB
             *d = (int) &_r_debug;
+#endif
             break;
          case DT_RELA:
             DL_ERR("%5d DT_RELA not supported", pid);
@@ -1491,6 +1582,50 @@
             DEBUG("%5d Text segment should be writable during relocation.\n",
                   pid);
             break;
+#if defined(ANDROID_MIPS_LINKER)
+        case DT_NEEDED:
+        case DT_STRSZ:
+        case DT_SYMENT:
+        case DT_RELENT:
+             break;
+        case DT_MIPS_RLD_MAP:
+            /* Set the DT_MIPS_RLD_MAP entry to the addres of _r_debug for GDB */
+            {
+              struct r_debug **dp = (struct r_debug **)*d;
+              *dp = &_r_debug;
+            }
+            break;
+        case DT_MIPS_RLD_VERSION:
+        case DT_MIPS_FLAGS:
+        case DT_MIPS_BASE_ADDRESS:
+        case DT_MIPS_UNREFEXTNO:
+        case DT_MIPS_RWPLT:
+            break;
+
+        case DT_MIPS_PLTGOT:
+#if 0
+            /* not yet... */
+            si->mips_pltgot = (unsigned *)(si->base + *d);
+#endif
+            break;
+
+        case DT_MIPS_SYMTABNO:
+            si->mips_symtabno = *d;
+            break;
+
+        case DT_MIPS_LOCAL_GOTNO:
+            si->mips_local_gotno = *d;
+            break;
+
+        case DT_MIPS_GOTSYM:
+            si->mips_gotsym = *d;
+            break;
+
+        default:
+            DEBUG("%5d Unused DT entry: type 0x%08x arg 0x%08x\n",
+                  pid, d[-1], d[0]);
+            break;
+#endif
         }
     }
 
@@ -1552,6 +1687,12 @@
             goto fail;
     }
 
+#ifdef ANDROID_MIPS_LINKER
+    if(mips_relocate_got(si)) {
+        goto fail;
+    }
+#endif
+
     si->flags |= FLAG_LINKED;
     DEBUG("[ %5d finished linking %s ]\n", pid, si->name);
 
diff --git a/linker/linker.h b/linker/linker.h
index 0956ac5..cb2eab6 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -150,11 +150,19 @@
     void (*init_func)(void);
     void (*fini_func)(void);
 
-#ifdef ANDROID_ARM_LINKER
+#if defined(ANDROID_ARM_LINKER)
     /* ARM EABI section used for stack unwinding. */
     unsigned *ARM_exidx;
     unsigned ARM_exidx_count;
+#elif defined(ANDROID_MIPS_LINKER)
+#if 0
+     /* not yet */
+     unsigned *mips_pltgot
 #endif
+     unsigned mips_symtabno;
+     unsigned mips_local_gotno;
+     unsigned mips_gotsym;
+#endif /* ANDROID_*_LINKER */
 
     unsigned refcount;
     struct link_map linkmap;
@@ -169,29 +177,31 @@
 
 extern soinfo libdl_info;
 
-#ifdef ANDROID_ARM_LINKER
 
+#include <asm/elf.h>
+
+#if defined(ANDROID_ARM_LINKER)
+
+// These aren't defined in <arch-arm/asm/elf.h>.
+#define R_ARM_REL32      3
 #define R_ARM_COPY       20
 #define R_ARM_GLOB_DAT   21
 #define R_ARM_JUMP_SLOT  22
 #define R_ARM_RELATIVE   23
 
-/* According to the AAPCS specification, we only
- * need the above relocations. However, in practice,
- * the following ones turn up from time to time.
- */
-#define R_ARM_ABS32      2
-#define R_ARM_REL32      3
+#elif defined(ANDROID_MIPS_LINKER)
+
+// These aren't defined in <arch-arm/mips/elf.h>.
+#define R_MIPS_JUMP_SLOT       127
+
+#define DT_MIPS_PLTGOT         0x70000032
+#define DT_MIPS_RWPLT          0x70000034
 
 #elif defined(ANDROID_X86_LINKER)
 
-#define R_386_32         1
-#define R_386_PC32       2
-#define R_386_GLOB_DAT   6
-#define R_386_JUMP_SLOT  7
-#define R_386_RELATIVE   8
+// x86 has everything it needs in <arch-arm/x86/elf.h>.
 
-#endif
+#endif /* ANDROID_*_LINKER */
 
 #ifndef DT_INIT_ARRAY
 #define DT_INIT_ARRAY      25
@@ -227,10 +237,10 @@
 Elf32_Sym *soinfo_lookup(soinfo *si, const char *name);
 void soinfo_call_constructors(soinfo *si);
 
-#ifdef ANDROID_ARM_LINKER 
+#if defined(ANDROID_ARM_LINKER)
 typedef long unsigned int *_Unwind_Ptr;
 _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount);
-#elif defined(ANDROID_X86_LINKER)
+#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
 int dl_iterate_phdr(int (*cb)(struct dl_phdr_info *, size_t, void *), void *);
 #endif
 
diff --git a/linker/linker_format.c b/linker/linker_format.c
index cded68a..d305740 100644
--- a/linker/linker_format.c
+++ b/linker/linker_format.c
@@ -413,20 +413,6 @@
     format_number(buffer, buffsize, value, base, "0123456789");
 }
 
-/* Write an octal into a buffer, assumes buffsize > 2 */
-static void
-format_octal(char *buffer, size_t buffsize, uint64_t value, int isSigned)
-{
-    format_integer(buffer, buffsize, value, 8, isSigned);
-}
-
-/* Write a decimal into a buffer, assumes buffsize > 2 */
-static void
-format_decimal(char *buffer, size_t buffsize, uint64_t value, int isSigned)
-{
-    format_integer(buffer, buffsize, value, 10, isSigned);
-}
-
 /* Write an hexadecimal into a buffer, isCap is true for capital alphas.
  * Assumes bufsize > 2 */
 static void