aboutsummaryrefslogtreecommitdiff
path: root/ldso/ldso/sh/elfinterp.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldso/ldso/sh/elfinterp.c')
-rw-r--r--ldso/ldso/sh/elfinterp.c189
1 files changed, 178 insertions, 11 deletions
diff --git a/ldso/ldso/sh/elfinterp.c b/ldso/ldso/sh/elfinterp.c
index 964b2ea37..00596e2c9 100644
--- a/ldso/ldso/sh/elfinterp.c
+++ b/ldso/ldso/sh/elfinterp.c
@@ -43,17 +43,82 @@
extern int _dl_linux_resolve(void);
-unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
+#ifdef __SH_FDPIC__
+static int
+is_in_module (void *address, struct elf32_fdpic_loadmap *map)
+{
+ int i;
+ for (i = 0; i < map->nsegs; i++)
+ if (address >= map->segs[i].addr
+ && address < map->segs[i].addr + map->segs[i].p_memsz)
+ return 1;
+ return 0;
+}
+#endif
+
+#ifdef __SH_FDPIC__
+struct funcdesc_value volatile *
+#else
+unsigned long
+#endif
+_dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
{
ELF_RELOC *this_reloc;
char *strtab;
Elf32_Sym *symtab;
int symtab_index;
char *rel_addr;
+ char *symname;
char *new_addr;
+
+#ifdef __SH_FDPIC__
+ struct elf_resolve *new_tpnt;
+ struct funcdesc_value funcval;
+ struct funcdesc_value volatile *got_addr;
+#else
char **got_addr;
unsigned long instr_addr;
- char *symname;
+#endif
+
+#ifdef __SH_FDPIC__
+ /* In FDPIC the parameters are not what they appear to be, but they can be
+ used to derive the right information.
+
+ On entry,
+ TPNT is the elf_resolve pointer for the calling function, OR, if the
+ relocation has already been done by a competing thread, it might be
+ the elf_resolve pointer for the function we are about to call.
+ RELOC_ENTRY is the address of the lazy PLT stub. The actual value will
+ be at address reloc_entry-4.
+
+ In order to calculate the true values of the parameters, we need to
+ detect, and possibly correct, the race condition. */
+ struct elf_resolve *module = tpnt;
+ void *lazyPLT = (void *)reloc_entry;
+
+ if (!is_in_module (lazyPLT, module->loadaddr.map))
+ {
+ /* The race condition has occurred! */
+
+ /* Traverse the module list and find the one containing lazyPLT. */
+ while (module->prev)
+ module = module->prev;
+ for (; module; module = module->next)
+ if (is_in_module (lazyPLT, module->loadaddr.map))
+ break;
+
+ if (!module)
+ {
+ /* No module was found. This shouldn't ever happen. */
+ _dl_dprintf (2, "%s: internal error in lazy relocation\n", _dl_progname);
+ _dl_exit(1);
+ }
+ }
+
+ /* We now know the proper values for the parameters. */
+ tpnt = module;
+ reloc_entry = ((int*)lazyPLT)[-1];
+#endif
rel_addr = (char *)tpnt->dynamic_info[DT_JMPREL];
@@ -64,32 +129,62 @@ unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
strtab = (char *)tpnt->dynamic_info[DT_STRTAB];
symname = strtab + symtab[symtab_index].st_name;
+#ifdef __SH_FDPIC__
+ /* Address of GOT entry fix up */
+ got_addr = (struct funcdesc_value *)
+ DL_RELOC_ADDR (tpnt->loadaddr, this_reloc->r_offset);
+#else
/* Address of jump instruction to fix up */
instr_addr = (unsigned long) (this_reloc->r_offset + tpnt->loadaddr);
got_addr = (char **) instr_addr;
+#endif
/* Get the address of the GOT entry */
- new_addr = _dl_find_hash(symname, tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT);
+ new_addr = _dl_lookup_hash(symname, tpnt->symbol_scope, tpnt, ELF_RTYPE_CLASS_PLT
+#if __SH_FDPIC__
+ , &new_tpnt
+#endif
+ );
if (unlikely(!new_addr)) {
_dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname);
_dl_exit(1);
}
+#ifdef __SH_FDPIC__
+ funcval.entry_point = new_addr;
+ funcval.got_value = new_tpnt->loadaddr.got_value;
+#endif
+
#if defined (__SUPPORT_LD_DEBUG__)
if ((unsigned long) got_addr < 0x20000000) {
if (_dl_debug_bindings) {
_dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
if (_dl_debug_detail) _dl_dprintf(_dl_debug_file,
+#ifdef __SH_FDPIC__
+ "\n\tpatched (%x,%x) ==> (%x,%x) @ %x\n",
+ got_addr->entry_point, got_addr->got_value,
+ funcval.entry_point, funcval.got_value,
+ got_addr);
+#else
"\n\tpatched %x ==> %x @ %x\n", *got_addr, new_addr, got_addr);
+#endif
}
}
if (!_dl_debug_nofixups)
- *got_addr = new_addr;
+#endif
+#ifdef __SH_FDPIC__
+ /* To deal with a race condtion, the GOT value must be written first. */
+ got_addr->got_value = funcval.got_value;
+ got_addr->entry_point = funcval.entry_point;
#else
*got_addr = new_addr;
#endif
+#ifdef __SH_FDPIC__
+ return got_addr;
+#else
return (unsigned long) new_addr;
+#endif
}
@@ -158,16 +253,30 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
#if defined (__SUPPORT_LD_DEBUG__)
unsigned long old_val;
#endif
+#ifdef __SH_FDPIC__
+ struct funcdesc_value funcval;
+ struct elf_resolve *symbol_tpnt;
+#endif
- reloc_addr = (unsigned long *)(intptr_t) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
+ reloc_addr = (unsigned long *) DL_RELOC_ADDR(tpnt->loadaddr, rpnt->r_offset);
reloc_type = ELF32_R_TYPE(rpnt->r_info);
symtab_index = ELF32_R_SYM(rpnt->r_info);
symbol_addr = 0;
symname = strtab + symtab[symtab_index].st_name;
+#ifdef __SH_FDPIC__
+ if (ELF_ST_BIND (symtab[symtab_index].st_info) == STB_LOCAL) {
+ symbol_addr = (unsigned long) DL_RELOC_ADDR(tpnt->loadaddr, symtab[symtab_index].st_value);
+ symbol_tpnt = tpnt;
+ } else
+#endif
if (symtab_index) {
- symbol_addr = (unsigned long) _dl_find_hash(symname, scope, tpnt,
- elf_machine_type_class(reloc_type));
+ symbol_addr = (unsigned long) _dl_lookup_hash(symname, scope, tpnt,
+ elf_machine_type_class(reloc_type)
+#ifdef __SH_FDPIC__
+ , &symbol_tpnt
+#endif
+ );
/*
* We want to allow undefined references to weak symbols - this might
@@ -185,6 +294,10 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
return 1;
}
}
+#ifdef __SH_FDPIC__
+ else
+ symbol_tpnt = tpnt;
+#endif
#if defined (__SUPPORT_LD_DEBUG__)
old_val = *reloc_addr;
@@ -213,8 +326,34 @@ _dl_do_reloc (struct elf_resolve *tpnt,struct dyn_elf *scope,
(unsigned long) reloc_addr;
break;
case R_SH_RELATIVE:
- *reloc_addr = (unsigned long) tpnt->loadaddr + rpnt->r_addend;
+ *reloc_addr = (unsigned long) DL_RELOC_ADDR (tpnt->loadaddr, rpnt->r_addend);
+ break;
+#ifdef __SH_FDPIC__
+ case R_SH_FUNCDESC_VALUE:
+ funcval.entry_point = (void*)symbol_addr;
+ /* The addend of FUNCDESC_VALUE
+ relocations referencing global
+ symbols must be ignored, because it
+ may hold the address of a lazy PLT
+ entry. */
+ if (ELF_ST_BIND(symtab[symtab_index].st_info) == STB_LOCAL)
+ funcval.entry_point += *reloc_addr;
+ if (symbol_addr)
+ funcval.got_value
+ = symbol_tpnt->loadaddr.got_value;
+ else
+ funcval.got_value = 0;
+ *(struct funcdesc_value *)reloc_addr = funcval;
+ break;
+ case R_SH_FUNCDESC:
+ if (symbol_addr)
+ *reloc_addr = (unsigned long)_dl_funcdesc_for(
+ (char *)symbol_addr + *reloc_addr,
+ symbol_tpnt->loadaddr.got_value);
+ else
+ *reloc_addr = 0;
break;
+#endif
default:
return -1;
@@ -233,7 +372,12 @@ _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct dyn_elf *scope,
ELF_RELOC *rpnt, Elf32_Sym *symtab, char *strtab)
{
int reloc_type;
+#ifdef __SH_FDPIC__
+ struct funcdesc_value volatile *reloc_addr;
+ struct funcdesc_value funcval;
+#else
unsigned long *reloc_addr;
+#endif
#if defined (__SUPPORT_LD_DEBUG__)
unsigned long old_val;
#endif
@@ -241,24 +385,43 @@ _dl_do_lazy_reloc (struct elf_resolve *tpnt, struct dyn_elf *scope,
(void)symtab;
(void)strtab;
- reloc_addr = (unsigned long *)(intptr_t) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
+ reloc_addr = DL_RELOC_ADDR (tpnt->loadaddr, rpnt->r_offset);
reloc_type = ELF32_R_TYPE(rpnt->r_info);
#if defined (__SUPPORT_LD_DEBUG__)
+#ifdef __SH_FDPIC__
+ old_val = (unsigned long)reloc_addr->entry_point;
+#else
old_val = *reloc_addr;
#endif
+#endif
switch (reloc_type) {
case R_SH_NONE:
break;
+#ifdef __SH_FDPIC__
+ case R_SH_FUNCDESC_VALUE:
+ funcval = *reloc_addr;
+ funcval.entry_point = DL_RELOC_ADDR(tpnt->loadaddr, funcval.entry_point);
+ funcval.got_value = tpnt->loadaddr.got_value;
+ *reloc_addr = funcval;
+ break;
+#else
case R_SH_JMP_SLOT:
- *reloc_addr += (unsigned long) tpnt->loadaddr;
+ *reloc_addr = (unsigned long) DL_RELOC_ADDR (tpnt->loadaddr, *reloc_addr);
break;
+#endif
default:
return -1;
}
#if defined (__SUPPORT_LD_DEBUG__)
if (_dl_debug_reloc && _dl_debug_detail)
- _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val, *reloc_addr, reloc_addr);
+ _dl_dprintf(_dl_debug_file, "\tpatched: %x ==> %x @ %x", old_val,
+#ifdef __SH_FDPIC__
+ reloc_addr->entry_point,
+#else
+ *reloc_addr,
+#endif
+ reloc_addr);
#endif
return 0;
@@ -275,3 +438,7 @@ int _dl_parse_relocation_information(struct dyn_elf *rpnt,
{
return _dl_parse(rpnt->dyn, rpnt->dyn->symbol_scope, rel_addr, rel_size, _dl_do_reloc);
}
+
+#if defined __SH_FDPIC__ && !defined IS_IN_libdl
+# include "../../libc/sysdeps/linux/sh/crtreloc.c"
+#endif