OSDN Git Service

ldso/mips: Fix relocation parsing so that they work on both mips32/mips64
[uclinux-h8/uclibc-ng.git] / ldso / ldso / mips / elfinterp.c
index a73a89a..a56ee81 100644 (file)
  * SUCH DAMAGE.
  */
 
-#if defined (__SUPPORT_LD_DEBUG__)
-static const char *_dl_reltypes_tab[] =
-{
-               [0]             "R_MIPS_NONE",  "R_MIPS_16",    "R_MIPS_32",
-               [3]             "R_MIPS_REL32", "R_MIPS_26",    "R_MIPS_HI16",
-               [6]             "R_MIPS_LO16",  "R_MIPS_GPREL16",       "R_MIPS_LITERAL",
-               [9]             "R_MIPS_GOT16", "R_MIPS_PC16",  "R_MIPS_CALL16",
-               [12]    "R_MIPS_GPREL32",
-               [16]    "R_MIPS_SHIFT5",        "R_MIPS_SHIFT6",        "R_MIPS_64",
-               [19]    "R_MIPS_GOT_DISP",      "R_MIPS_GOT_PAGE",      "R_MIPS_GOT_OFST",
-               [22]    "R_MIPS_GOT_HI16",      "R_MIPS_GOT_LO16",      "R_MIPS_SUB",
-               [25]    "R_MIPS_INSERT_A",      "R_MIPS_INSERT_B",      "R_MIPS_DELETE",
-               [28]    "R_MIPS_HIGHER",        "R_MIPS_HIGHEST",       "R_MIPS_CALL_HI16",
-               [31]    "R_MIPS_CALL_LO16",     "R_MIPS_SCN_DISP",      "R_MIPS_REL16",
-               [34]    "R_MIPS_ADD_IMMEDIATE", "R_MIPS_PJUMP", "R_MIPS_RELGOT",
-               [37]    "R_MIPS_JALR",
-};
-
-static const char *
-_dl_reltypes(int type)
-{
-  static char buf[22];  
-  const char *str;
-  
-  if (type >= (int)(sizeof (_dl_reltypes_tab)/sizeof(_dl_reltypes_tab[0])) ||
-      NULL == (str = _dl_reltypes_tab[type]))
-  {
-    str =_dl_simple_ltoa( buf, (unsigned long)(type));
-  }
-  return str;
-}
-
-static 
-void debug_sym(Elf32_Sym *symtab,char *strtab,int symtab_index)
-{
-  if(_dl_debug_symbols)
-  {
-    if(symtab_index){
-      _dl_dprintf(_dl_debug_file, "\n%s\n\tvalue=%x\tsize=%x\tinfo=%x\tother=%x\tshndx=%x",
-                 strtab + symtab[symtab_index].st_name,
-                 symtab[symtab_index].st_value,
-                 symtab[symtab_index].st_size,
-                 symtab[symtab_index].st_info,
-                 symtab[symtab_index].st_other,
-                 symtab[symtab_index].st_shndx);
-    }
-  }
-}
-
-static void debug_reloc(Elf32_Sym *symtab,char *strtab, ELF_RELOC *rpnt)
-{
-  if(_dl_debug_reloc)
-  {
-    int symtab_index;
-    const char *sym;
-    symtab_index = ELF32_R_SYM(rpnt->r_info);
-    sym = symtab_index ? strtab + symtab[symtab_index].st_name : "sym=0x0";
-    
-  if(_dl_debug_symbols)
-         _dl_dprintf(_dl_debug_file, "\n\t");
-  else
-         _dl_dprintf(_dl_debug_file, "\n%s\n\t", sym);
-#ifdef ELF_USES_RELOCA
-    _dl_dprintf(_dl_debug_file, "%s\toffset=%x\taddend=%x",
-               _dl_reltypes(ELF32_R_TYPE(rpnt->r_info)),
-               rpnt->r_offset,
-               rpnt->r_addend);
-#else
-    _dl_dprintf(_dl_debug_file, "%s\toffset=%x\n",
-               _dl_reltypes(ELF32_R_TYPE(rpnt->r_info)),
-               rpnt->r_offset);
-#endif
-  }
-}
-#endif
+#include "ldso.h"
 
-extern int _dl_linux_resolve(void);
+extern int _dl_runtime_resolve(void);
+extern int _dl_runtime_pltresolve(void);
 
 #define OFFSET_GP_GOT 0x7ff0
 
-unsigned long _dl_linux_resolver(unsigned long sym_index,
+unsigned long __dl_runtime_resolve(unsigned long sym_index,
        unsigned long old_gpreg)
 {
        unsigned long *got = (unsigned long *) (old_gpreg - OFFSET_GP_GOT);
        struct elf_resolve *tpnt = (struct elf_resolve *) got[1];
-       Elf32_Sym *sym;
+       ElfW(Sym) *sym;
        char *strtab;
        unsigned long local_gotno;
        unsigned long gotsym;
@@ -121,25 +48,30 @@ unsigned long _dl_linux_resolver(unsigned long sym_index,
        char **got_addr;
        char *symname;
 
-       gotsym = tpnt->mips_gotsym;
-       local_gotno = tpnt->mips_local_gotno;
+       gotsym = tpnt->dynamic_info[DT_MIPS_GOTSYM_IDX];
+       local_gotno = tpnt->dynamic_info[DT_MIPS_LOCAL_GOTNO_IDX];
 
-       sym = ((Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr)) + sym_index;
-       strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
+       sym = ((ElfW(Sym) *) tpnt->dynamic_info[DT_SYMTAB]) + sym_index;
+       strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
        symname = strtab + sym->st_name;
 
-       new_addr = (unsigned long) _dl_find_hash(strtab + sym->st_name,
-                tpnt->symbol_scope, tpnt, resolver);
-        
+       new_addr = (unsigned long) _dl_find_hash(symname,
+                       tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL);
+       if (unlikely(!new_addr)) {
+               _dl_dprintf (2, "%s: can't resolve symbol '%s'\n",
+                               _dl_progname, symname);
+               _dl_exit (1);
+       }
+
        /* Address of jump instruction to fix up */
-       instr_addr = (unsigned long) (got + local_gotno + sym_index - gotsym); 
+       instr_addr = (unsigned long) (got + local_gotno + sym_index - gotsym);
        got_addr = (char **) instr_addr;
-        
+
 #if defined (__SUPPORT_LD_DEBUG__)
        if (_dl_debug_bindings)
        {
                _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
-               if(_dl_debug_detail) _dl_dprintf(_dl_debug_file, 
+               if (_dl_debug_detail) _dl_dprintf(_dl_debug_file,
                                "\n\tpatched %x ==> %x @ %x\n", *got_addr, new_addr, got_addr);
        }
        if (!_dl_debug_nofixups) {
@@ -152,105 +84,252 @@ unsigned long _dl_linux_resolver(unsigned long sym_index,
        return new_addr;
 }
 
-void _dl_parse_lazy_relocation_information(struct elf_resolve *tpnt, 
-       unsigned long rel_addr, unsigned long rel_size, int type)
+unsigned long
+__dl_runtime_pltresolve(struct elf_resolve *tpnt, int reloc_entry)
 {
-       /* Nothing to do */
-       return;
+       ELF_RELOC *this_reloc;
+       char *strtab;
+       Elf32_Sym *symtab;
+       int symtab_index;
+       char *rel_addr;
+       char *new_addr;
+       char **got_addr;
+       unsigned long instr_addr;
+       char *symname;
+
+       rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
+       this_reloc = (ELF_RELOC *)(intptr_t)(rel_addr + reloc_entry);
+       symtab_index = ELF32_R_SYM(this_reloc->r_info);
+
+       symtab = (Elf32_Sym *)(intptr_t)tpnt->dynamic_info[DT_SYMTAB];
+       strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
+       symname = strtab + symtab[symtab_index].st_name;
+
+       /* Address of the jump instruction to fix up. */
+       instr_addr = ((unsigned long)this_reloc->r_offset +
+                     (unsigned long)tpnt->loadaddr);
+       got_addr = (char **)instr_addr;
+
+       /* Get the address of the GOT entry. */
+       new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL);
+       if (unlikely(!new_addr)) {
+               _dl_dprintf(2, "%s: can't resolve symbol '%s' in lib '%s'.\n", _dl_progname, symname, tpnt->libname);
+               _dl_exit(1);
+       }
+
+#if defined (__SUPPORT_LD_DEBUG__)
+       if ((unsigned long)got_addr < 0x40000000) {
+               if (_dl_debug_bindings) {
+                       _dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
+                       if (_dl_debug_detail)
+                               _dl_dprintf(_dl_debug_file,
+                                           "\n\tpatched: %x ==> %x @ %x",
+                                           *got_addr, new_addr, got_addr);
+               }
+       }
+       if (!_dl_debug_nofixups) {
+               *got_addr = new_addr;
+       }
+#else
+       *got_addr = new_addr;
+#endif
+
+       return (unsigned long)new_addr;
 }
 
-int _dl_parse_copy_information(struct dyn_elf *xpnt, unsigned long rel_addr, 
-       unsigned long rel_size, int type)
+void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
+       unsigned long rel_addr, unsigned long rel_size)
 {
        /* Nothing to do */
-       return 0;
+       return;
 }
 
-
-int _dl_parse_relocation_information(struct elf_resolve *tpnt, 
-       unsigned long rel_addr, unsigned long rel_size, int type)
+int _dl_parse_relocation_information(struct dyn_elf *xpnt,
+       unsigned long rel_addr, unsigned long rel_size)
 {
-       Elf32_Sym *symtab;
-       Elf32_Rel *rpnt;
+       ElfW(Sym) *symtab;
+       ELF_RELOC *rpnt;
        char *strtab;
+       unsigned long i;
        unsigned long *got;
-       unsigned long *reloc_addr=NULL, old_val=0;
+       unsigned long *reloc_addr=NULL;
        unsigned long symbol_addr;
-       int i, reloc_type, symtab_index;
+       int reloc_type, symtab_index;
+       struct elf_resolve *tpnt = xpnt->dyn;
+       char *symname = NULL;
+#if defined (__SUPPORT_LD_DEBUG__)
+       unsigned long old_val=0;
+#endif
 
        /* Now parse the relocation information */
-       rel_size = rel_size / sizeof(Elf32_Rel);
-       rpnt = (Elf32_Rel *) (rel_addr + tpnt->loadaddr);
+       rel_size = rel_size / sizeof(ElfW(Rel));
+       rpnt = (ELF_RELOC *) rel_addr;
 
-       symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
-       strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
-       got = (unsigned long *) (tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
+       symtab = (ElfW(Sym) *) tpnt->dynamic_info[DT_SYMTAB];
+       strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
+       got = (unsigned long *) tpnt->dynamic_info[DT_PLTGOT];
 
        for (i = 0; i < rel_size; i++, rpnt++) {
                reloc_addr = (unsigned long *) (tpnt->loadaddr +
                        (unsigned long) rpnt->r_offset);
-               reloc_type = ELF32_R_TYPE(rpnt->r_info);
-               symtab_index = ELF32_R_SYM(rpnt->r_info);
+               reloc_type = ELF_R_TYPE(rpnt->r_info);
+               symtab_index = ELF_R_SYM(rpnt->r_info);
                symbol_addr = 0;
 
-               if (!symtab_index && tpnt->libtype == program_interpreter)
-                       continue;
-
-#if defined (__SUPPORT_LD_DEBUG__)
                debug_sym(symtab,strtab,symtab_index);
                debug_reloc(symtab,strtab,rpnt);
-               old_val = *reloc_addr;
+               symname = strtab + symtab[symtab_index].st_name;
+#if defined (__SUPPORT_LD_DEBUG__)
+               if (reloc_addr)
+                       old_val = *reloc_addr;
 #endif
 
+               if (reloc_type == R_MIPS_JUMP_SLOT || reloc_type == R_MIPS_COPY) {
+                       symbol_addr = (unsigned long)_dl_find_hash(symname,
+                                                                  tpnt->symbol_scope,
+                                                                  tpnt,
+                                                                  elf_machine_type_class(reloc_type), NULL);
+                       if (unlikely(!symbol_addr && ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK))
+                               return 1;
+               }
+               if (!symtab_index) {
+                       /* Relocs against STN_UNDEF are usually treated as using a
+                       * symbol value of zero, and using the module containing the
+                       * reloc itself.
+                       */
+                       symbol_addr = symtab[symtab_index].st_value;
+               }
+
                switch (reloc_type) {
+#if defined USE_TLS && USE_TLS
+# if _MIPS_SIM == _MIPS_SIM_ABI64
+               case R_MIPS_TLS_DTPMOD64:
+               case R_MIPS_TLS_DTPREL64:
+               case R_MIPS_TLS_TPREL64:
+# else
+               case R_MIPS_TLS_DTPMOD32:
+               case R_MIPS_TLS_DTPREL32:
+               case R_MIPS_TLS_TPREL32:
+# endif
+                       {
+                               struct elf_resolve *tpnt_tls = NULL;
+
+                               if (ELF32_ST_BIND(symtab[symtab_index].st_info) != STB_LOCAL) {
+                                       symbol_addr = (unsigned long) _dl_find_hash(symname, tpnt->symbol_scope,
+                                               tpnt, elf_machine_type_class(reloc_type), &tpnt_tls);
+                               }
+                           /* In case of a TLS reloc, tpnt_tls NULL means we have an 'anonymous'
+                              symbol.  This is the case for a static tls variable, so the lookup
+                              module is just that one is referencing the tls variable. */
+                           if (!tpnt_tls)
+                               tpnt_tls = tpnt;
+
+                               switch (reloc_type) {
+                                       case R_MIPS_TLS_DTPMOD64:
+                                       case R_MIPS_TLS_DTPMOD32:
+                                               if (tpnt_tls)
+                                                       *(ElfW(Word) *)reloc_addr = tpnt_tls->l_tls_modid;
+#ifdef __SUPPORT_LD_DEBUG__
+                                               _dl_dprintf(2, "TLS_DTPMOD : %s, %d, %d\n",
+                                                       symname, old_val, *((unsigned int *)reloc_addr));
+#endif
+                                               break;
+
+                                       case R_MIPS_TLS_DTPREL64:
+                                       case R_MIPS_TLS_DTPREL32:
+                                               *(ElfW(Word) *)reloc_addr +=
+                                                       TLS_DTPREL_VALUE (symbol_addr);
+#ifdef __SUPPORT_LD_DEBUG__
+                                               _dl_dprintf(2, "TLS_DTPREL : %s, %x, %x\n",
+                                                       symname, old_val, *((unsigned int *)reloc_addr));
+#endif
+                                               break;
+
+                                       case R_MIPS_TLS_TPREL32:
+                                       case R_MIPS_TLS_TPREL64:
+                                               CHECK_STATIC_TLS((struct link_map *)tpnt_tls);
+                                               *(ElfW(Word) *)reloc_addr +=
+                                                       TLS_TPREL_VALUE (tpnt_tls, symbol_addr);
+#ifdef __SUPPORT_LD_DEBUG__
+                                               _dl_dprintf(2, "TLS_TPREL  : %s, %x, %x\n",
+                                                       symname, old_val, *((unsigned int *)reloc_addr));
+#endif
+                                               break;
+                               }
+
+                               break;
+                       }
+#endif /* USE_TLS */
+#if _MIPS_SIM == _MIPS_SIM_ABI64
+               case (R_MIPS_64 << 8) | R_MIPS_REL32:
+#else  /* O32 || N32 */
                case R_MIPS_REL32:
+#endif /* O32 || N32 */
                        if (symtab_index) {
-                               if (symtab_index < tpnt->mips_gotsym)
+                               if (symtab_index < tpnt->dynamic_info[DT_MIPS_GOTSYM_IDX])
                                        *reloc_addr +=
                                                symtab[symtab_index].st_value +
                                                (unsigned long) tpnt->loadaddr;
                                else {
-                                       *reloc_addr += got[symtab_index + tpnt->mips_local_gotno -
-                                               tpnt->mips_gotsym];
+                                       *reloc_addr += got[symtab_index + tpnt->dynamic_info[DT_MIPS_LOCAL_GOTNO_IDX] -
+                                               tpnt->dynamic_info[DT_MIPS_GOTSYM_IDX]];
                                }
                        }
                        else {
                                *reloc_addr += (unsigned long) tpnt->loadaddr;
                        }
                        break;
+               case R_MIPS_JUMP_SLOT:
+                       *reloc_addr = symbol_addr;
+                       break;
+               case R_MIPS_COPY:
+                       if (symbol_addr) {
+#if defined (__SUPPORT_LD_DEBUG__)
+                               if (_dl_debug_move)
+                                       _dl_dprintf(_dl_debug_file,
+                                                   "\n%s move %d bytes from %x to %x",
+                                                   symname, symtab[symtab_index].st_size,
+                                                   symbol_addr, reloc_addr);
+#endif
+
+                               _dl_memcpy((char *)reloc_addr,
+                                          (char *)symbol_addr,
+                                          symtab[symtab_index].st_size);
+                       }
+                       break;
                case R_MIPS_NONE:
                        break;
                default:
                        {
-                               int reloc_type = ELF32_R_TYPE(rpnt->r_info);
                                _dl_dprintf(2, "\n%s: ",_dl_progname);
 
                                if (symtab_index)
-                                       _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
+                                       _dl_dprintf(2, "symbol '%s': ", symname);
 
 #if defined (__SUPPORT_LD_DEBUG__)
-                               _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
+                               _dl_dprintf(2, "can't handle reloc type '%s' in lib '%s'\n", _dl_reltypes(reloc_type), tpnt->libname);
 #else
-                               _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
-#endif                 
+                               _dl_dprintf(2, "can't handle reloc type %x in lib '%s'\n", reloc_type, tpnt->libname);
+#endif
                                _dl_exit(1);
                        }
-               };
+               }
 
-       };
+       }
 #if defined (__SUPPORT_LD_DEBUG__)
-       if(_dl_debug_reloc && _dl_debug_detail)
+       if (_dl_debug_reloc && _dl_debug_detail && reloc_addr)
                _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x\n", old_val, *reloc_addr, reloc_addr);
 #endif
 
        return 0;
 }
 
-void _dl_perform_mips_global_got_relocations(struct elf_resolve *tpnt)
+/* Relocate the global GOT entries for the object */
+void _dl_perform_mips_global_got_relocations(struct elf_resolve *tpnt, int lazy)
 {
-       Elf32_Sym *sym;
+       ElfW(Sym) *sym;
        char *strtab;
-       unsigned long i;
+       unsigned long i, tmp_lazy;
        unsigned long *got_entry;
 
        for (; tpnt ; tpnt = tpnt->next) {
@@ -260,38 +339,43 @@ void _dl_perform_mips_global_got_relocations(struct elf_resolve *tpnt)
                        continue;
 
                /* Setup the loop variables */
-               got_entry = (unsigned long *) (tpnt->loadaddr +
-                       tpnt->dynamic_info[DT_PLTGOT]) + tpnt->mips_local_gotno;
-               sym = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] +
-                       (unsigned long) tpnt->loadaddr) + tpnt->mips_gotsym;
-               strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] +
-                       (unsigned long) tpnt->loadaddr);
-               i = tpnt->mips_symtabno - tpnt->mips_gotsym;
+               got_entry = (unsigned long *) (tpnt->dynamic_info[DT_PLTGOT])
+                       + tpnt->dynamic_info[DT_MIPS_LOCAL_GOTNO_IDX];
+               sym = (ElfW(Sym) *) tpnt->dynamic_info[DT_SYMTAB] + tpnt->dynamic_info[DT_MIPS_GOTSYM_IDX];
+               strtab = (char *) tpnt->dynamic_info[DT_STRTAB];
+               i = tpnt->dynamic_info[DT_MIPS_SYMTABNO_IDX] - tpnt->dynamic_info[DT_MIPS_GOTSYM_IDX];
 
+#if defined (__SUPPORT_LD_DEBUG__)
+               if (_dl_debug_reloc)
+                       _dl_dprintf(2, "_dl_perform_mips_global_got_relocations for '%s'\n", tpnt->libname);
+#endif
+               tmp_lazy = lazy && !tpnt->dynamic_info[DT_BIND_NOW];
                /* Relocate the global GOT entries for the object */
-               while(i--) {
+               while (i--) {
                        if (sym->st_shndx == SHN_UNDEF) {
-                               if (ELF32_ST_TYPE(sym->st_info) == STT_FUNC && sym->st_value)
+                               if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && sym->st_value && tmp_lazy) {
                                        *got_entry = sym->st_value + (unsigned long) tpnt->loadaddr;
+                               }
                                else {
                                        *got_entry = (unsigned long) _dl_find_hash(strtab +
-                                               sym->st_name, tpnt->symbol_scope, NULL, copyrel);
+                                               sym->st_name, tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL);
                                }
                        }
                        else if (sym->st_shndx == SHN_COMMON) {
                                *got_entry = (unsigned long) _dl_find_hash(strtab +
-                                       sym->st_name, tpnt->symbol_scope, NULL, copyrel);
+                                       sym->st_name, tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL);
                        }
-                       else if (ELF32_ST_TYPE(sym->st_info) == STT_FUNC &&
-                               *got_entry != sym->st_value)
+                       else if (ELF_ST_TYPE(sym->st_info) == STT_FUNC &&
+                               *got_entry != sym->st_value && tmp_lazy) {
                                *got_entry += (unsigned long) tpnt->loadaddr;
-                       else if (ELF32_ST_TYPE(sym->st_info) == STT_SECTION) {
+                       }
+                       else if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
                                if (sym->st_other == 0)
                                        *got_entry += (unsigned long) tpnt->loadaddr;
                        }
                        else {
                                *got_entry = (unsigned long) _dl_find_hash(strtab +
-                                       sym->st_name, tpnt->symbol_scope, NULL, copyrel);
+                                       sym->st_name, tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT, NULL);
                        }
 
                        got_entry++;
@@ -299,3 +383,4 @@ void _dl_perform_mips_global_got_relocations(struct elf_resolve *tpnt)
                }
        }
 }
+