Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / arch / parisc / kernel / module.c
index f27cfe4..aee3118 100644 (file)
@@ -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 {
@@ -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;
@@ -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
@@ -408,18 +420,23 @@ 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      */
@@ -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[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;
@@ -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)
@@ -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",