/* 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.
*
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]))
{
return str;
}
-static
+static
void debug_sym(Elf32_Sym *symtab,char *strtab,int symtab_index)
{
if(_dl_debug_symbols)
}
}
-static
+static
void debug_reloc(Elf32_Sym *symtab,char *strtab, ELF_RELOC *rpnt)
{
if(_dl_debug_reloc)
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
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);
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);
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 */
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);
};
#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);
};
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 */
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)
{
*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
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 */
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)
*(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;
};
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);
-}