Skip to content

Commit

Permalink
[PARISC] Fix PCREL22F relocation problem for most modules
Browse files Browse the repository at this point in the history
The new problem, which has been affecting many more modules was that
our new ioremap really takes chunks out of our vmalloc space.  The net
result being that any two kernel vmalloc's now have to slot into the
chunked up space.  So the vmallocs for a modules init and core sectons
are no longer necessarily contiguous.  Unfortunately, the module loader
thinks that any internal symbol references should be satisfiable using the
jump instruction, which isn't true if the symbol is referenced from init
to core and vmalloc placed them a long way apart.

Fix this by introducing a new stub type for intra module inter sectional
jumps and using it.

Signed-off-by: James Bottomley <jejb@parisc-linux.org>
Signed-off-by: Kyle McMartin <kyle@parisc-linux.org>
  • Loading branch information
James Bottomley authored and Kyle McMartin committed Jun 27, 2006
1 parent 1c63b4b commit 6e1b958
Showing 1 changed file with 50 additions and 13 deletions.
63 changes: 50 additions & 13 deletions arch/parisc/kernel/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ static inline int is_local(struct module *me, void *loc)
return is_init(me, loc) || is_core(me, loc);
}

static inline int is_local_section(struct module *me, void *loc, void *dot)
{
return (is_init(me, loc) && is_init(me, dot)) ||
(is_core(me, loc) && is_core(me, dot));
}


#ifndef __LP64__
struct got_entry {
Expand Down Expand Up @@ -364,8 +370,14 @@ static Elf_Addr get_fdesc(struct module *me, unsigned long value)
}
#endif /* __LP64__ */

enum elf_stub_type {
ELF_STUB_GOT,
ELF_STUB_MILLI,
ELF_STUB_DIRECT,
};

static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
int millicode, int init_section)
enum elf_stub_type stub_type, int init_section)
{
unsigned long i;
struct stub_entry *stub;
Expand Down Expand Up @@ -396,7 +408,7 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
stub->insns[1] |= reassemble_17(rrsel(value, addend) / 4);

#else
/* for 64-bit we have two kinds of stubs:
/* for 64-bit we have three kinds of stubs:
* for normal function calls:
* ldd 0(%dp),%dp
* ldd 10(%dp), %r1
Expand All @@ -408,26 +420,41 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
* ldo 0(%r1), %r1
* ldd 10(%r1), %r1
* bve,n (%r1)
*
* for direct branches (jumps between different section of the
* same module):
* ldil 0, %r1
* ldo 0(%r1), %r1
* bve,n (%r1)
*/
if (!millicode)
{
switch (stub_type) {
case ELF_STUB_GOT:
stub->insns[0] = 0x537b0000; /* ldd 0(%dp),%dp */
stub->insns[1] = 0x53610020; /* ldd 10(%dp),%r1 */
stub->insns[2] = 0xe820d000; /* bve (%r1) */
stub->insns[3] = 0x537b0030; /* ldd 18(%dp),%dp */

stub->insns[0] |= reassemble_14(get_got(me, value, addend) & 0x3fff);
}
else
{
break;
case ELF_STUB_MILLI:
stub->insns[0] = 0x20200000; /* ldil 0,%r1 */
stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */
stub->insns[2] = 0x50210020; /* ldd 10(%r1),%r1 */
stub->insns[3] = 0xe820d002; /* bve,n (%r1) */

stub->insns[0] |= reassemble_21(lrsel(value, addend));
stub->insns[1] |= reassemble_14(rrsel(value, addend));
break;
case ELF_STUB_DIRECT:
stub->insns[0] = 0x20200000; /* ldil 0,%r1 */
stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */
stub->insns[2] = 0xe820d002; /* bve,n (%r1) */

stub->insns[0] |= reassemble_21(lrsel(value, addend));
stub->insns[1] |= reassemble_14(rrsel(value, addend));
break;
}

#endif

return (Elf_Addr)stub;
Expand Down Expand Up @@ -539,14 +566,14 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
break;
case R_PARISC_PCREL17F:
/* 17-bit PC relative address */
val = get_stub(me, val, addend, 0, is_init(me, loc));
val = get_stub(me, val, addend, ELF_STUB_GOT, is_init(me, loc));
val = (val - dot - 8)/4;
CHECK_RELOC(val, 17)
*loc = (*loc & ~0x1f1ffd) | reassemble_17(val);
break;
case R_PARISC_PCREL22F:
/* 22-bit PC relative address; only defined for pa20 */
val = get_stub(me, val, addend, 0, is_init(me, loc));
val = get_stub(me, val, addend, ELF_STUB_GOT, is_init(me, loc));
DEBUGP("STUB FOR %s loc %lx+%lx at %lx\n",
strtab + sym->st_name, (unsigned long)loc, addend,
val)
Expand Down Expand Up @@ -643,13 +670,23 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
strtab + sym->st_name,
loc, val);
/* can we reach it locally? */
if(!is_local(me, (void *)val)) {
if (strncmp(strtab + sym->st_name, "$$", 2)
if(!is_local_section(me, (void *)val, (void *)dot)) {

if (is_local(me, (void *)val))
/* this is the case where the
* symbol is local to the
* module, but in a different
* section, so stub the jump
* in case it's more than 22
* bits away */
val = get_stub(me, val, addend, ELF_STUB_DIRECT,
is_init(me, loc));
else if (strncmp(strtab + sym->st_name, "$$", 2)
== 0)
val = get_stub(me, val, addend, 1,
val = get_stub(me, val, addend, ELF_STUB_MILLI,
is_init(me, loc));
else
val = get_stub(me, val, addend, 0,
val = get_stub(me, val, addend, ELF_STUB_GOT,
is_init(me, loc));
}
DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n",
Expand Down

0 comments on commit 6e1b958

Please sign in to comment.