NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

toolchain/43314: pc relative relocations are "off by 1*size" on vax

>Number:         43314
>Category:       toolchain
>Synopsis:       toolchain
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    toolchain-manager
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun May 16 11:05:00 +0000 2010
>Originator:     Martin Husemann
>Release:        NetBSD 5.99.29
The NetBSD Foundation, Inc.
System: NetBSD 5.99.29 NetBSD 5.99.29 (DEAD) #31: 
Wed May 12 19:07:50 CEST 2010 vax
Architecture: vax
Machine: vax

While analyzing the broken C++ exception handling on vax I ran into this
problem (.eh* frame descriptions use 32bit pc relative references to a
32bit value storing the function pointer to call as "personality" function,
but the unwinding code dereferences a 32bit value 4 bytes below that pointer
and crashes).

I couldn't find the ultimate reference describing the %pcrel() asm pseudo
op, so I'm not sure if this is a bug in binutils, or if gcc should be
modified (patch below) to output different references.


Run this small sample program and wonder, why the relative addresses are
4, 2 or 1 byte lower than expected:

 * demo program to show strange %pcrel behaviour on vax:
 * exception handling frame descriptors include code like
 *  .4byte  %pcrel32(DW.ref.__gxx_personality_v0)
 * to reference pc relatvie to a 4 byte value containing a pointer
 * to __gxx_personality_v0 (a "peronality" function), but the unwinding
 * code ends up dereferencing the memory 4 byte below that value.

#include <stdio.h>
#include <inttypes.h>

int main(int argc, char **argv)
        const char *str;
        extern const long ref4;
        extern const short ref2;
        extern const signed char ref1;

 ".section .rodata\n"
 ".globl ref_string\n"
 "ref_string: .string \"abcdefghijklmnopq\"\n"
 ".globl ref4\n"
 ".4byte  %%pcrel32(ref_string+8)\n"
 ".globl ref2\n"
 ".2byte %%pcrel16(ref_string+6)\n"
 ".globl ref1\n"
 ".byte %%pcrel8(ref_string+5)\n"
 ".text" ::); 

        /* Test 4 byte pcrelative access */
        str = (const char*)((uintptr_t)&ref4 + ref4);
        printf("pcrel32 at %p value %ld pointing to %p -> %s\n",
                &ref4, ref4, str, str);
        str = (const char*)((uintptr_t)&ref2 + ref2);
        printf("pcrel16 at %p value %d pointing to %p -> %s\n",
                &ref2, ref2, str, str);
        str = (const char*)((uintptr_t)&ref1 + ref1);
        printf("pcrel8 at %p value %d pointing to %p -> %s\n",
                &ref1, ref1, str, str);

        return 0;

It prints:

pcrel32 at 0x272c8 value -14 pointing to 0x272ba -> efghijklmnopq
pcrel16 at 0x272cc value -18 pointing to 0x272ba -> efghijklmnopq
pcrel8 at 0x272ce value -20 pointing to 0x272ba -> efghijklmnopq

but the output should start at ref_string+8 ("ijkl...") etc.

If this is not considered a binutils bug, we could adjust gcc like this

Index: elf.h
RCS file: /cvsroot/src/gnu/dist/gcc4/gcc/config/vax/elf.h,v
retrieving revision 1.3
diff -c -u -r1.3 elf.h
--- elf.h       31 Mar 2007 05:55:11 -0000      1.3
+++ elf.h       16 May 2010 10:56:13 -0000
@@ -107,5 +107,5 @@
     fputs (integer_asm_op (SIZE, FALSE), FILE);                \
     fprintf (FILE, "%%pcrel%d(", SIZE * 8);            \
     assemble_name (FILE, LABEL);                       \
-    fputc (')', FILE);                                 \
+    fprintf (FILE, "+%d)", SIZE);                      \
   } while (0)

Home | Main Index | Thread Index | Old Index