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

Workaround for Alpha libstc++ unaligned access problem



Hi,

After staring at the exception handling code in the C runtime
environment a bit harder I have decided that, until the issues of the
DWARF2 frame info format can be resolved in gcc (which they will have
to be eventually - think IA-64), the only way to deal with this
problem is simply to have ld.so on the Alpha treat RELATIVE
relocations as potentially unaligned.

If you look at the exception handling code in the runtime environment
(in gcc/frame.c in the gcc source) you'll notice that it's already
assumed that any data in the DWARF2 frame info is potentially
unaligned ... I've been searching in vain for a reference to this
particular data structure in the DWARF2 spec but it's entirely
possible that it's just broken by design.

So, I just stole the code that gcc uses and stuck it in the dynamic
linker.  We don't need to do this for any other kind of relocations,
because the __EXCEPTION_TABLE__ pointer in the DWARF2 info is always
going to be a RELATIVE relocation.  I've tested this fix on my Alphas
and it does indeed solve the problem.  I don't think we really have to
worry about the loss in efficiency since the relocation is only done
once for each frame info, at process startup time.

Without further ado here is the patch:

--- glibc-2.1.3/sysdeps/alpha/dl-machine.h~	Sat Feb 20 13:20:58 1999
+++ glibc-2.1.3/sysdeps/alpha/dl-machine.h	Mon Feb 21 14:10:15 2000
@@ -424,6 +424,20 @@
 
 #ifdef RESOLVE
 
+/* Stolen from gcc - quite obviously gcc-specific */
+union unaligned {
+  void *p;
+  unsigned b2 __attribute__ ((mode (HI)));
+  unsigned b4 __attribute__ ((mode (SI)));
+  unsigned b8 __attribute__ ((mode (DI)));
+} __attribute__ ((packed));
+static inline unsigned long
+read_8byte (void *p)
+{ union unaligned *up = p; return up->b8; }
+static inline void
+write_8byte (void *p, unsigned long val)
+{ union unaligned *up = p; up->b8 = val; }
+
 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
    MAP is the object containing the reloc.  */
 static inline void
@@ -453,7 +467,13 @@
       /* Already done in dynamic linker.  */
       if (map != &_dl_rtld_map)
 #endif
-	*reloc_addr += map->l_addr;
+	{
+	  /* Because DWARF2 exception handling is broken by design,
+             these are often unaligned. */
+	  Elf64_Addr tmp = read_8byte(reloc_addr);
+	  tmp += map->l_addr;
+	  write_8byte(reloc_addr, tmp);
+	}
     }
   else if (r_type == R_ALPHA_NONE)
     return;


Reply to: