From d7daacb46372132ae3f0121647074936c304b572 Mon Sep 17 00:00:00 2001 From: Raghu Gandham Date: Tue, 31 Jul 2012 12:07:22 -0700 Subject: [PATCH] MIPS support to the linker Change-Id: I37ec2d6c51d82bb9e9dbfef4b38c85366bead255 Signed-off-by: Chris Dearman Signed-off-by: Raghu Gandham Signed-off-by: Bhanu Chetlapalli --- linker/Android.mk | 15 ++-- linker/arch/mips/begin.S | 107 +++++++++++++++++++++++ linker/debugger.c | 6 ++ linker/dlfcn.c | 6 +- linker/linker.c | 177 +++++++++++++++++++++++++++++++++++---- linker/linker.h | 42 ++++++---- linker/linker_format.c | 14 ---- 7 files changed, 311 insertions(+), 56 deletions(-) create mode 100644 linker/arch/mips/begin.S diff --git a/linker/Android.mk b/linker/Android.mk index c9d053f84..d207f955f 100644 --- a/linker/Android.mk +++ b/linker/Android.mk @@ -16,7 +16,8 @@ LOCAL_LDFLAGS := -shared 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 @@ LOCAL_CFLAGS += \ -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 000000000..b782947d9 --- /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 756b5cfa5..7a1dd1584 100644 --- a/linker/debugger.c +++ b/linker/debugger.c @@ -131,7 +131,9 @@ static void logSignalSummary(int signum, const siginfo_t* info) 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 @@ void debugger_signal_handler(int n, siginfo_t* info, void* unused) 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 @@ void debugger_init() 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 3d0384fdc..13064e50e 100644 --- a/linker/dlfcn.c +++ b/linker/dlfcn.c @@ -170,13 +170,13 @@ int dlclose(void *handle) #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 @@ static Elf32_Sym libdl_symtab[] = { 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 0a931308b..e0c3bceeb 100644 --- a/linker/linker.c +++ b/linker/linker.c @@ -142,9 +142,9 @@ static char tmp_err_buf[768]; 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 @@ _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount) *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 @@ soinfo_do_lookup(soinfo *si, const char *name, Elf32_Addr *offset) 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 @@ verify_elf_header(const Elf32_Ehdr* hdr) 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 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) 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 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) 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 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) 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 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) } 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 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) *((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 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) 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 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) 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 @@ static int soinfo_relocate(soinfo *si, Elf32_Rel *rel, unsigned count) 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 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset) 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 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset) 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 @@ static int soinfo_link_image(soinfo *si, unsigned wr_offset) 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 0956ac526..cb2eab65b 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -150,11 +150,19 @@ struct soinfo 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 @@ struct soinfo extern soinfo libdl_info; -#ifdef ANDROID_ARM_LINKER +#include + +#if defined(ANDROID_ARM_LINKER) + +// These aren't defined in . +#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 . +#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 . -#endif +#endif /* ANDROID_*_LINKER */ #ifndef DT_INIT_ARRAY #define DT_INIT_ARRAY 25 @@ -227,10 +237,10 @@ Elf32_Sym *soinfo_find_symbol(soinfo* si, const void *addr); 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 cded68a09..d30574020 100644 --- a/linker/linker_format.c +++ b/linker/linker_format.c @@ -413,20 +413,6 @@ format_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSi 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