diff mbox

Bug#539378: [hppa]: fails to load nfs module: Global Offset Table

Message ID 20090731233828.GF26333@bombadil.infradead.org (mailing list archive)
State Superseded
Headers show

Commit Message

kyle mcmartin July 31, 2009, 11:38 p.m. UTC
On Fri, Jul 31, 2009 at 06:00:48PM -0400, Carlos O'Donell wrote:
> On Fri, Jul 31, 2009 at 5:26 PM, John David
> Anglin<dave@hiauly1.hia.nrc.ca> wrote:
> > I don't have more details...  The idea is as Carlos outlined.  There's
> > code in the binutils elf32-hppa.c and elf64-hppa.c files to implement
> > the above for dynamic libraries.  That's what made me think of it.
> 
> Binutils is not involved in the kernel module loader, instead
> arch/parisc/kernel/module.c (get_fdesc) chooses where the gp will
> point to.
> 
> If you set gp to the middle of the GOT table, *and* implement
> long/short ldd access on 64-bit, then you would get a total of 8191
> possible slots per module.
> 
> Personally I think the lower risk, quicker fix, is to implement a fix
> for 64-bit kernels that uses ldd in format 3 for all offsets > 15
> bytes, and thus allow you to set MAX_GOTS to 4095.
> 
> Note: ldd format 3 can't be used to load immediate values between 15
> and -16 bytes.
> 

Is it as simple as:


I don't think we need to worry about the initial 15-bytes displacement,
since they're all within the first got_entry? (The resulting assembly
looks alright from a 64-bit toolchain:

kyle@shortfin ~ $ cat foo.S
	.text
a:
	ldd 32760(%r27),%r27
	break	0,0

0000000000000000 <a>:
   0:	53 7b ff f0 	ldd 7ff8(dp),dp

int main(void) {
        unsigned int opcode = 0x537b0000;
        opcode |= re_assemble_16(32760);
        printf("0x%x\n", opcode);
        return 0;
}

kyle@shortfin ~ $ ./foo
0x537bfff0

Looks pretty happy?
--
To unsubscribe from this list: send the line "unsubscribe linux-parisc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Carlos O'Donell Aug. 1, 2009, 1:49 a.m. UTC | #1
On Fri, Jul 31, 2009 at 7:38 PM, Kyle McMartin<kyle@mcmartin.ca> wrote:
> Is it as simple as:
>
> diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c
> index ef5caf2..0502fab 100644
> --- a/arch/parisc/kernel/module.c
> +++ b/arch/parisc/kernel/module.c
> @@ -82,13 +82,6 @@
>                return -ENOEXEC;                        \
>        }
>
> -/* Maximum number of GOT entries. We use a long displacement ldd from
> - * the bottom of the table, which has a maximum signed displacement of
> - * 0x3fff; however, since we're only going forward, this becomes
> - * 0x1fff, and thus, since each GOT entry is 8 bytes long we can have
> - * at most 1023 entries */
> -#define MAX_GOTS       1023
> -
>  /* three functions to determine where in the module core
>  * or init pieces the location is */
>  static inline int in_init(struct module *me, void *loc)
> @@ -126,6 +119,14 @@ struct stub_entry {
>  };
>  #endif
>
> +/* Maximum number of GOT entries. We use a long displacement ldd from
> + * the bottom of the table, which has 16-bit signed displacement from
> + * %dp. Because we only use the forward direction, we're limited to
> + * 15-bits - 1, and because each GOT entry is 8-bytes wide, we're limited
> + * to 4095 entries.
> + */
> +#define MAX_GOTS       (((1 << 15) - 1) / sizeof(struct got_entry))
> +

OK

>  /* Field selection types defined by hppa */
>  #define rnd(x)                 (((x)+0x1000)&~0x1fff)
>  /* fsel: full 32 bits */
> @@ -151,6 +152,15 @@ static inline int reassemble_14(int as14)
>                ((as14 & 0x2000) >> 13));
>  }
>
> +/* Unusual 16-bit encoding, for wide mode only.  */
> +static inline int reassemble_16a(int as16)
> +{
> +       int s, t;
> +       t = (as16 << 1) & 0xffff;
> +       s = (as16 & 0x8000);
> +       return (t ^ s ^ (s >> 1)) | (s >> 15);
> +}
> +

OK

>  static inline int reassemble_17(int as17)
>  {
>        return (((as17 & 0x10000) >> 16) |
> @@ -460,12 +470,16 @@ static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
>  */
>        switch (stub_type) {
>        case ELF_STUB_GOT:
> +               unsigned int d = get_got(me, value, addend) & 0x7fff;
> +
>                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);
> +               if (d > 15)
> +                       stub->insns[0] |= reassemble_16a(d);
> +

You need to rewrite stub->insn[0[, the long format 3 ldd is a
different opcode, see the PA 2.0 manual, it will no longer be
0x537b0000.

You also still need a <= 15 byte case which uses the old short format
5 ldd with a 14-bit immediate.

>                break;
>        case ELF_STUB_MILLI:
>                stub->insns[0] = 0x20200000;    /* ldil 0,%r1           */
>
> I don't think we need to worry about the initial 15-bytes displacement,
> since they're all within the first got_entry? (The resulting assembly
> looks alright from a 64-bit toolchain:

No, we still have to worry about the initial 15-bytes. Within the
first 15-bytes you have one GOT entry (%dp + 0) and thus you need to
add the case for the short format 3 ldd.

Thanks for hacking this up!

Cheers,
Carlos.
--
To unsubscribe from this list: send the line "unsubscribe linux-parisc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c
index ef5caf2..0502fab 100644
--- a/arch/parisc/kernel/module.c
+++ b/arch/parisc/kernel/module.c
@@ -82,13 +82,6 @@ 
 		return -ENOEXEC;			\
 	}
 
-/* Maximum number of GOT entries. We use a long displacement ldd from
- * the bottom of the table, which has a maximum signed displacement of
- * 0x3fff; however, since we're only going forward, this becomes
- * 0x1fff, and thus, since each GOT entry is 8 bytes long we can have
- * at most 1023 entries */
-#define MAX_GOTS	1023
-
 /* three functions to determine where in the module core
  * or init pieces the location is */
 static inline int in_init(struct module *me, void *loc)
@@ -126,6 +119,14 @@  struct stub_entry {
 };
 #endif
 
+/* Maximum number of GOT entries. We use a long displacement ldd from
+ * the bottom of the table, which has 16-bit signed displacement from
+ * %dp. Because we only use the forward direction, we're limited to
+ * 15-bits - 1, and because each GOT entry is 8-bytes wide, we're limited
+ * to 4095 entries.
+ */
+#define MAX_GOTS	(((1 << 15) - 1) / sizeof(struct got_entry))
+
 /* Field selection types defined by hppa */
 #define rnd(x)			(((x)+0x1000)&~0x1fff)
 /* fsel: full 32 bits */
@@ -151,6 +152,15 @@  static inline int reassemble_14(int as14)
 		((as14 & 0x2000) >> 13));
 }
 
+/* Unusual 16-bit encoding, for wide mode only.  */
+static inline int reassemble_16a(int as16)
+{
+	int s, t;
+	t = (as16 << 1) & 0xffff;
+	s = (as16 & 0x8000);
+	return (t ^ s ^ (s >> 1)) | (s >> 15);
+}
+
 static inline int reassemble_17(int as17)
 {
 	return (((as17 & 0x10000) >> 16) |
@@ -460,12 +470,16 @@  static Elf_Addr get_stub(struct module *me, unsigned long value, long addend,
  */
 	switch (stub_type) {
 	case ELF_STUB_GOT:
+		unsigned int d = get_got(me, value, addend) & 0x7fff;
+
 		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);
+		if (d > 15)
+			stub->insns[0] |= reassemble_16a(d);
+
 		break;
 	case ELF_STUB_MILLI:
 		stub->insns[0] = 0x20200000;	/* ldil 0,%r1		*/