Commit 6e1b9585 authored by James Bottomley's avatar James Bottomley Committed by Kyle McMartin

[PARISC] Fix PCREL22F relocation problem for most modules

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: default avatarJames Bottomley <jejb@parisc-linux.org>
Signed-off-by: default avatarKyle McMartin <kyle@parisc-linux.org>
parent 1c63b4b8
...@@ -89,6 +89,12 @@ static inline int is_local(struct module *me, void *loc) ...@@ -89,6 +89,12 @@ static inline int is_local(struct module *me, void *loc)
return is_init(me, loc) || is_core(me, 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__ #ifndef __LP64__
struct got_entry { struct got_entry {
...@@ -364,8 +370,14 @@ static Elf_Addr get_fdesc(struct module *me, unsigned long value) ...@@ -364,8 +370,14 @@ static Elf_Addr get_fdesc(struct module *me, unsigned long value)
} }
#endif /* __LP64__ */ #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, 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; unsigned long i;
struct stub_entry *stub; struct stub_entry *stub;
...@@ -396,7 +408,7 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, ...@@ -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); stub->insns[1] |= reassemble_17(rrsel(value, addend) / 4);
#else #else
/* for 64-bit we have two kinds of stubs: /* for 64-bit we have three kinds of stubs:
* for normal function calls: * for normal function calls:
* ldd 0(%dp),%dp * ldd 0(%dp),%dp
* ldd 10(%dp), %r1 * ldd 10(%dp), %r1
...@@ -408,18 +420,23 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, ...@@ -408,18 +420,23 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
* ldo 0(%r1), %r1 * ldo 0(%r1), %r1
* ldd 10(%r1), %r1 * ldd 10(%r1), %r1
* bve,n (%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[0] = 0x537b0000; /* ldd 0(%dp),%dp */
stub->insns[1] = 0x53610020; /* ldd 10(%dp),%r1 */ stub->insns[1] = 0x53610020; /* ldd 10(%dp),%r1 */
stub->insns[2] = 0xe820d000; /* bve (%r1) */ stub->insns[2] = 0xe820d000; /* bve (%r1) */
stub->insns[3] = 0x537b0030; /* ldd 18(%dp),%dp */ stub->insns[3] = 0x537b0030; /* ldd 18(%dp),%dp */
stub->insns[0] |= reassemble_14(get_got(me, value, addend) & 0x3fff); stub->insns[0] |= reassemble_14(get_got(me, value, addend) & 0x3fff);
} break;
else case ELF_STUB_MILLI:
{
stub->insns[0] = 0x20200000; /* ldil 0,%r1 */ stub->insns[0] = 0x20200000; /* ldil 0,%r1 */
stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */ stub->insns[1] = 0x34210000; /* ldo 0(%r1), %r1 */
stub->insns[2] = 0x50210020; /* ldd 10(%r1),%r1 */ stub->insns[2] = 0x50210020; /* ldd 10(%r1),%r1 */
...@@ -427,7 +444,17 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, ...@@ -427,7 +444,17 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
stub->insns[0] |= reassemble_21(lrsel(value, addend)); stub->insns[0] |= reassemble_21(lrsel(value, addend));
stub->insns[1] |= reassemble_14(rrsel(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 #endif
return (Elf_Addr)stub; return (Elf_Addr)stub;
...@@ -539,14 +566,14 @@ int apply_relocate_add(Elf_Shdr *sechdrs, ...@@ -539,14 +566,14 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
break; break;
case R_PARISC_PCREL17F: case R_PARISC_PCREL17F:
/* 17-bit PC relative address */ /* 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; val = (val - dot - 8)/4;
CHECK_RELOC(val, 17) CHECK_RELOC(val, 17)
*loc = (*loc & ~0x1f1ffd) | reassemble_17(val); *loc = (*loc & ~0x1f1ffd) | reassemble_17(val);
break; break;
case R_PARISC_PCREL22F: case R_PARISC_PCREL22F:
/* 22-bit PC relative address; only defined for pa20 */ /* 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", DEBUGP("STUB FOR %s loc %lx+%lx at %lx\n",
strtab + sym->st_name, (unsigned long)loc, addend, strtab + sym->st_name, (unsigned long)loc, addend,
val) val)
...@@ -643,13 +670,23 @@ int apply_relocate_add(Elf_Shdr *sechdrs, ...@@ -643,13 +670,23 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
strtab + sym->st_name, strtab + sym->st_name,
loc, val); loc, val);
/* can we reach it locally? */ /* can we reach it locally? */
if(!is_local(me, (void *)val)) { if(!is_local_section(me, (void *)val, (void *)dot)) {
if (strncmp(strtab + sym->st_name, "$$", 2)
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) == 0)
val = get_stub(me, val, addend, 1, val = get_stub(me, val, addend, ELF_STUB_MILLI,
is_init(me, loc)); is_init(me, loc));
else else
val = get_stub(me, val, addend, 0, val = get_stub(me, val, addend, ELF_STUB_GOT,
is_init(me, loc)); is_init(me, loc));
} }
DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n", DEBUGP("STUB FOR %s loc %lx, val %lx+%lx at %lx\n",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment