OSDN Git Service

Let ldso decide if it should relocate itselft a second time. This
[uclinux-h8/uClibc.git] / ldso / ldso / powerpc / elfinterp.c
index 5d731e4..103a0fb 100644 (file)
@@ -1,8 +1,9 @@
 /* vi: set sw=4 ts=4: */
 /* powerpc shared library loader suppport
  *
- * Copyright (C) 2001-2002,  David A. Schleef
- * Copyright (C) 2003, Erik Andersen
+ * Copyright (C) 2001-2002 David A. Schleef
+ * Copyright (C) 2003-2004 Erik Andersen
+ * Copyright (C) 2004 Joakim Tjernlund
  *
  * All rights reserved.
  *
@@ -46,9 +47,9 @@ static const char *_dl_reltypes_tab[] =
 static const char *
 _dl_reltypes(int type)
 {
-  static char buf[22];  
+  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]))
   {
@@ -57,7 +58,7 @@ _dl_reltypes(int type)
   return str;
 }
 
-static 
+static
 void debug_sym(Elf32_Sym *symtab,char *strtab,int symtab_index)
 {
   if(_dl_debug_symbols)
@@ -74,7 +75,7 @@ void debug_sym(Elf32_Sym *symtab,char *strtab,int symtab_index)
   }
 }
 
-static 
+static
 void debug_reloc(Elf32_Sym *symtab,char *strtab, ELF_RELOC *rpnt)
 {
   if(_dl_debug_reloc)
@@ -83,7 +84,7 @@ void debug_reloc(Elf32_Sym *symtab,char *strtab, ELF_RELOC *rpnt)
     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
@@ -112,16 +113,9 @@ void _dl_init_got(unsigned long *plt,struct elf_resolve *tpnt)
        Elf32_Word rel_offset_words;
        Elf32_Word dlrr = (Elf32_Word) _dl_linux_resolve;
 
-       //DPRINTF("init_got plt=%x, tpnt=%x\n", (unsigned long)plt,(unsigned long)tpnt);
-
        num_plt_entries = tpnt->dynamic_info[DT_PLTRELSZ] / sizeof(ELF_RELOC);
-       //DPRINTF("n_plt_entries %d\n",n_plt_entries);
-
        rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
-       //DPRINTF("rel_offset_words %x\n",rel_offset_words);
        data_words = (Elf32_Word) (plt + rel_offset_words);
-       //DPRINTF("data_words %x\n",data_words);
-
        tpnt->data_words = data_words;
 
        plt[PLT_LONGBRANCH_ENTRY_WORDS] = OPCODE_ADDIS_HI(11, 11, data_words);
@@ -146,10 +140,10 @@ void _dl_init_got(unsigned long *plt,struct elf_resolve *tpnt)
                tramp[4] = OPCODE_LI (12, (Elf32_Word) tpnt);
                tramp[5] = OPCODE_ADDIS_HI (12, 12, (Elf32_Word) tpnt);
 
-               /* Call _dl_runtime_resolve.  */
+               /* Call _dl_linux_resolve .  */
                tramp[6] = OPCODE_BA (dlrr);
        } else {
-               /* Get address of _dl_runtime_resolve in CTR.  */
+               /* Get address of _dl_linux_resolve in CTR.  */
                tramp[4] = OPCODE_LI(12,dlrr);
                tramp[5] = OPCODE_ADDIS_HI(12,12,dlrr);
                tramp[6] = OPCODE_MTCTR(12);
@@ -158,7 +152,7 @@ void _dl_init_got(unsigned long *plt,struct elf_resolve *tpnt)
                tramp[7] = OPCODE_LI(12,(Elf32_Word) tpnt);
                tramp[8] = OPCODE_ADDIS_HI(12,12,(Elf32_Word) tpnt);
 
-               /* Call _dl_runtime_resolve.  */
+               /* Call _dl_linux_resolve.  */
                tramp[9] = OPCODE_BCTR();
        }
        /* [16] unused */
@@ -200,7 +194,7 @@ unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
        debug_sym(symtab,strtab,symtab_index);
        debug_reloc(symtab,strtab,this_reloc);
 
-       if (ELF32_R_TYPE(this_reloc->r_info) != R_PPC_JMP_SLOT) {
+       if (unlikely(ELF32_R_TYPE(this_reloc->r_info) != R_PPC_JMP_SLOT)) {
                _dl_dprintf(2, "%s: Incorrect relocation type in jump relocation\n", _dl_progname);
                _dl_exit(1);
        };
@@ -215,9 +209,9 @@ unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
 #endif
 
        /* Get the address of the GOT entry */
-       finaladdr = (Elf32_Addr) _dl_find_hash(strtab + symtab[symtab_index].st_name, 
-                                               tpnt->symbol_scope, tpnt, resolver);
-       if (!finaladdr) {
+       finaladdr = (Elf32_Addr) _dl_find_hash(symname,
+                       tpnt->symbol_scope, ELF_RTYPE_CLASS_PLT);
+       if (unlikely(!finaladdr)) {
                _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname);
                _dl_exit(1);
        };
@@ -229,7 +223,7 @@ unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
        delta = finaladdr - (Elf32_Word)reloc_addr;
        if (delta<<6>>6 == delta) {
                *reloc_addr = OPCODE_B(delta);
-       } else if (finaladdr <= 0x01fffffc || finaladdr >= 0xfe000000) {
+       } else if (finaladdr <= 0x01fffffc) {
                *reloc_addr = OPCODE_BA (finaladdr);
        } else {
                /* Warning: we don't handle double-sized PLT entries */
@@ -255,111 +249,7 @@ unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
        return finaladdr;
 }
 
-static int
-_dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
-         unsigned long rel_addr, unsigned long rel_size,
-         int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
-                           ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
-{
-       unsigned int i;
-       char *strtab;
-       Elf32_Sym *symtab;
-       ELF_RELOC *rpnt;
-       int symtab_index;
-
-       /* Now parse the relocation information */
-       rpnt = (ELF_RELOC *)(intptr_t) (rel_addr + tpnt->loadaddr);
-       rel_size = rel_size / sizeof(ELF_RELOC);
-
-       symtab = (Elf32_Sym *)(intptr_t) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
-       strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
-
-         for (i = 0; i < rel_size; i++, rpnt++) {
-               int res;
-           
-               symtab_index = ELF32_R_SYM(rpnt->r_info);
-               
-               /* When the dynamic linker bootstrapped itself, it resolved some symbols.
-                  Make sure we do not do them again */
-               if (!symtab_index && tpnt->libtype == program_interpreter)
-                       continue;
-               if (symtab_index && tpnt->libtype == program_interpreter &&
-                   _dl_symbol(strtab + symtab[symtab_index].st_name))
-                       continue;
-
-#if defined (__SUPPORT_LD_DEBUG__)
-               debug_sym(symtab,strtab,symtab_index);
-               debug_reloc(symtab,strtab,rpnt);
-#endif
-
-               res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
-
-               if (res==0) continue;
-
-               _dl_dprintf(2, "\n%s: ",_dl_progname);
-               
-               if (symtab_index)
-                 _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
-                 
-               if (res <0)
-               {
-                       int reloc_type = ELF32_R_TYPE(rpnt->r_info);
-#if defined (__SUPPORT_LD_DEBUG__)
-                       _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
-#else
-                       _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
-#endif                 
-                       _dl_exit(-res);
-               }
-               else if (res >0)
-               {
-                       _dl_dprintf(2, "can't resolve symbol\n");
-                       return res;
-               }
-         }
-         return 0;
-}
-
-static int
-_dl_do_lazy_reloc (struct elf_resolve *tpnt, struct dyn_elf *scope,
-                  ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
-{
-       Elf32_Addr *reloc_addr;
-       Elf32_Word *plt, index, offset;
-
-       (void)scope;
-       (void)symtab;
-       (void)strtab;
-
-       reloc_addr = (Elf32_Addr *)(tpnt->loadaddr + rpnt->r_offset);
-#if defined (__SUPPORT_LD_DEBUG__)
-       if (ELF32_R_TYPE(rpnt->r_info) != R_PPC_JMP_SLOT) {
-               _dl_dprintf(2, "Reloc type != R_PPC_JMP_SLOT. Type is 0x%x\n", ELF32_R_TYPE(rpnt->r_info));
-               return -1;
-       }
-#endif
-       plt = (Elf32_Word *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
-       offset = reloc_addr - plt;
-       index = (offset - PLT_INITIAL_ENTRY_WORDS)/2;
-       reloc_addr[0] = OPCODE_LI(11,index*4);
-       reloc_addr[1] = OPCODE_B((PLT_TRAMPOLINE_ENTRY_WORDS + 2 - (offset+1)) * 4);
-
-       /* instructions were modified */
-       PPC_DCBST(reloc_addr);
-       PPC_DCBST(reloc_addr+1);
-       PPC_SYNC;
-       PPC_ICBI(reloc_addr);
-       PPC_ICBI(reloc_addr+1);
-       PPC_ISYNC;
-
-#if defined (__SUPPORT_LD_DEBUG__)
-       if(_dl_debug_reloc && _dl_debug_detail)
-               _dl_dprintf(_dl_debug_file, "\tpatched: %x", reloc_addr);
-#endif
-       return 0;
-}
-
-static int
+static inline int
 _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
              ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
 {
@@ -379,13 +269,13 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
                *reloc_addr = tpnt->loadaddr + rpnt->r_addend;
                return 0;
        }
-       if (reloc_type == R_PPC_NONE || reloc_type == R_PPC_COPY) /*  R_PPC_COPY is handled later */
+       if (reloc_type == R_PPC_NONE)
                return 0;
        symtab_index = ELF32_R_SYM(rpnt->r_info);
        symname      = strtab + symtab[symtab_index].st_name;
 
-       symbol_addr = (unsigned long) _dl_find_hash(symname, scope
-                                                   (reloc_type == R_PPC_JMP_SLOT ? tpnt : NULL), symbolrel);
+       symbol_addr = (unsigned long) _dl_find_hash(symname, scope->dyn->symbol_scope,
+                                                   elf_machine_type_class(reloc_type));
        /*
         * We want to allow undefined references to weak symbols - this might
         * have been intentional.  We should not be linking local symbols
@@ -412,10 +302,10 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
        case R_PPC_JMP_SLOT:
        {
                Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
-               
+
                if (delta<<6>>6 == delta) {
                        *reloc_addr = OPCODE_B(delta);
-               } else if (finaladdr <= 0x01fffffc || finaladdr >= 0xfe000000) {
+               } else if (finaladdr <= 0x01fffffc) {
                        *reloc_addr = OPCODE_BA (finaladdr);
                } else {
                        /* Warning: we don't handle double-sized PLT entries */
@@ -438,7 +328,6 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
                break;
        }
        case R_PPC_COPY:
-               /* This does not work yet, R_PPC_COPY is handled later, see if statemet above */
                if (symbol_addr) {
 #if defined (__SUPPORT_LD_DEBUG__)
                        if(_dl_debug_move)
@@ -460,23 +349,29 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
                *(short *)reloc_addr = finaladdr;
                break;
        case R_PPC_REL24:
-       {
-               Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
-               if(delta<<6>>6 != delta){
-                       _dl_dprintf(2, "%s: symbol '%s' R_PPC_REL24 is out of range.\n\tCompile shared libraries with -fPIC!\n",
-                                   _dl_progname, symname);
-                       _dl_exit(1);
+#if 0
+               {
+                       Elf32_Sword delta = finaladdr - (Elf32_Word)reloc_addr;
+                       if(unlikely(delta<<6>>6 != delta)) {
+                               _dl_dprintf(2, "%s: symbol '%s' R_PPC_REL24 is out of range.\n\t"
+                                               "Compile shared libraries with -fPIC!\n",
+                                               _dl_progname, symname);
+                               _dl_exit(1);
+                       }
+                       *reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
+                       break;
                }
-               *reloc_addr = (*reloc_addr & 0xfc000003) | (delta & 0x3fffffc);
-               break;
-       }
+#else
+               _dl_dprintf(2,"R_PPC_REL24: Compile shared libraries with -fPIC!\n");
+               _dl_exit(1);
+#endif
        default:
                _dl_dprintf(2, "%s: can't handle reloc type ", _dl_progname);
 #if defined (__SUPPORT_LD_DEBUG__)
-               _dl_dprintf(2, "%s ", _dl_reltypes[reloc_type]);
+               _dl_dprintf(2, "%s ", _dl_reltypes(reloc_type));
 #endif
                if (symtab_index)
-                       _dl_dprintf(2, "'%s'\n", strtab + symtab[symtab_index].st_name);
+                       _dl_dprintf(2, "'%s'\n", symname);
                return -1;
        };
 
@@ -492,65 +387,114 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
        return 0;
 }
 
-/* This is done as a separate step, because there are cases where
-   information is first copied and later initialized.  This results in
-   the wrong information being copied.  Someone at Sun was complaining about
-   a bug in the handling of _COPY by SVr4, and this may in fact be what he
-   was talking about.  Sigh. */
-static int
-_dl_do_copy (struct elf_resolve *tpnt, struct dyn_elf *scope,
-            ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
+void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
+       unsigned long rel_addr, unsigned long rel_size, int type)
 {
-       int reloc_type;
-       int symtab_index;
-       unsigned long *reloc_addr;
-       unsigned long symbol_addr;
-       int goof = 0;
-       char *symname;
-         
-       reloc_addr = (unsigned long *)(intptr_t) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
-       reloc_type = ELF32_R_TYPE(rpnt->r_info);
-       if (reloc_type != R_PPC_COPY) 
-               return 0;
-       symtab_index = ELF32_R_SYM(rpnt->r_info);
-       symbol_addr = 0;
-       symname      = strtab + symtab[symtab_index].st_name;
-               
-       if (symtab_index) {
-               symbol_addr = (unsigned long) _dl_find_hash(symname, scope, NULL, copyrel);
-               if (!symbol_addr) goof++;
+       struct elf_resolve *tpnt = rpnt->dyn;
+       Elf32_Word *plt, offset, i,  num_plt_entries, rel_offset_words;
+
+       (void) type;
+       num_plt_entries = rel_size / sizeof(ELF_RELOC);
+
+       rel_offset_words = PLT_DATA_START_WORDS(num_plt_entries);
+       plt = (Elf32_Word *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
+
+       /* Set up the lazy PLT entries.  */
+       offset = PLT_INITIAL_ENTRY_WORDS;
+       i = 0;
+       /* Warning: we don't handle double-sized PLT entries */
+       while (i < num_plt_entries) {
+               plt[offset  ] = OPCODE_LI(11, i * 4);
+               plt[offset+1] = OPCODE_B((PLT_TRAMPOLINE_ENTRY_WORDS + 2 - (offset+1)) * 4);
+               i++;
+               offset += 2;
        }
-       if (!goof) {
+       /* Now, we've modified code.  We need to write the changes from
+          the data cache to a second-level unified cache, then make
+          sure that stale data in the instruction cache is removed.
+          (In a multiprocessor system, the effect is more complex.)
+          Most of the PLT shouldn't be in the instruction cache, but
+          there may be a little overlap at the start and the end.
+
+          Assumes that dcbst and icbi apply to lines of 16 bytes or
+          more.  Current known line sizes are 16, 32, and 128 bytes.  */
+       for (i = 0; i < rel_offset_words; i += 4)
+               PPC_DCBST (plt + i);
+       PPC_DCBST (plt + rel_offset_words - 1);
+       PPC_SYNC;
+       PPC_ICBI (plt);
+       PPC_ICBI (plt + rel_offset_words - 1);
+       PPC_ISYNC;
+}
+
+static inline int
+_dl_parse(struct elf_resolve *tpnt, struct dyn_elf *scope,
+         unsigned long rel_addr, unsigned long rel_size,
+         int (*reloc_fnc) (struct elf_resolve *tpnt, struct dyn_elf *scope,
+                           ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab))
+{
+       unsigned int i;
+       char *strtab;
+       Elf32_Sym *symtab;
+       ELF_RELOC *rpnt;
+       int symtab_index;
+
+       /* Now parse the relocation information */
+       rpnt = (ELF_RELOC *)(intptr_t) (rel_addr + tpnt->loadaddr);
+       rel_size = rel_size / sizeof(ELF_RELOC);
+
+       symtab = (Elf32_Sym *)(intptr_t) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
+       strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
+
+         for (i = 0; i < rel_size; i++, rpnt++) {
+               int res;
+
+               symtab_index = ELF32_R_SYM(rpnt->r_info);
+
 #if defined (__SUPPORT_LD_DEBUG__)
-               if(_dl_debug_move)
-                 _dl_dprintf(_dl_debug_file,"\n%s move %x bytes from %x to %x",
-                            symname, symtab[symtab_index].st_size,
-                            symbol_addr, symtab[symtab_index].st_value);
+               debug_sym(symtab,strtab,symtab_index);
+               debug_reloc(symtab,strtab,rpnt);
 #endif
-                       _dl_memcpy((char *) reloc_addr,
-                                       (char *) (symbol_addr + (unsigned long)rpnt->r_addend), symtab[symtab_index].st_size);
-       }
 
-       return goof;
+               res = reloc_fnc (tpnt, scope, rpnt, symtab, strtab);
+
+               if (res==0) continue;
+
+               _dl_dprintf(2, "\n%s: ",_dl_progname);
+
+               if (symtab_index)
+                 _dl_dprintf(2, "symbol '%s': ", strtab + symtab[symtab_index].st_name);
+
+               if (unlikely(res <0))
+               {
+                       int reloc_type = ELF32_R_TYPE(rpnt->r_info);
+#if defined (__SUPPORT_LD_DEBUG__)
+                       _dl_dprintf(2, "can't handle reloc type %s\n ", _dl_reltypes(reloc_type));
+#else
+                       _dl_dprintf(2, "can't handle reloc type %x\n", reloc_type);
+#endif
+                       _dl_exit(-res);
+               }
+               if (unlikely(res >0))
+               {
+                       _dl_dprintf(2, "can't resolve symbol\n");
+                       return res;
+               }
+         }
+         return 0;
 }
 
-void _dl_parse_lazy_relocation_information(struct elf_resolve *tpnt, 
+int _dl_parse_relocation_information(struct dyn_elf *rpnt,
        unsigned long rel_addr, unsigned long rel_size, int type)
 {
-       (void) type;
-       (void)_dl_parse(tpnt, NULL, rel_addr, rel_size, _dl_do_lazy_reloc);
+       return _dl_parse(rpnt->dyn, rpnt, rel_addr, rel_size, _dl_do_reloc);
 }
 
-int _dl_parse_relocation_information(struct elf_resolve *tpnt, 
+/* Should be a static inline instead, but that conflicts with ld_elf.h */
+int _dl_parse_copy_information(struct dyn_elf *rpnt,
        unsigned long rel_addr, unsigned long rel_size, int type)
 {
-       (void) type;
-       return _dl_parse(tpnt, tpnt->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
+       /* Not used! */
+       return 0;
 }
 
-int _dl_parse_copy_information(struct dyn_elf *xpnt, unsigned long rel_addr, 
-       unsigned long rel_size, int type)
-{
-       (void) type;
-       return _dl_parse(xpnt->dyn, xpnt->next, rel_addr, rel_size, _dl_do_copy);
-}