[PATCH] elf/get-dynamic-info.h: fix early GNU_HASH processing on ia64

slyich@gmail.com slyich@gmail.com
Sat Dec 26 19:32:00 GMT 2015


From: Sergei Trofimovich <siarheit@google.com>

The following code when being
 called with l    = _rtld_local._dl_rtld_map
             info = l->l_info

  elf_get_dynamic_info(struct link_map *l,ElfW(Dyn) *dyn) {
    ...
    info[DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
         + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn;
    ...

led to generation of the follwoing assembly code:

    [MMI]       ld8 r14=[r32];;      # r14 = dyn->d_tag
                shladd r15=r14,3,r0  # r15 = dyn->d_tag * 8
                addl r14=163312,r1;; # r14 = gp + @ltoffx(_rtld_local#+0x380000000)
    [MMI]       ld8 r14=[r14];;
                adds r14=992,r14     # r14 = [abs_reloc] + 992
                nop.i 0x0;;
    [MMI]       nop.m 0x0
                sub r14=r14,r15
                nop.i 0x0;;
    [MIB]       st8 [r14]=r32        # [[abs_reloc] + 992] = dyn
                nop.i 0x0
                br.ret.sptk.many b0;;

This 'abs_reloc' is a relocation of R_IA64_REL64LSB type.

objdump -r -R ld.so:
  DYNAMIC RELOCATION RECORDS
  OFFSET           TYPE              VALUE
  ...
  0000000000052910 REL64LSB          *ABS*+0x0000000380052a60

After gcc's preprocessor and constant propagation phase
the code

  info[DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
       + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn;

became equivalent equivalent to:

   _rtld_local._dl_rtld_map.l_info[(0x6ffffeff - dyn->d_tag) + 66]

    (0x6ffffeff + 66) * 8 + 2520 = 0x3800003e0
        # 2520 is offset of '_rtld_local._dl_rtld_map.l_info'
        # 0x3e0 = 992

To workaround generation of that huge offset and relocation
I've moved index computation into a separate variable to trick
gcc into simpler code.

Bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60465#c32
Signed-off-by: Sergei Trofimovich <siarheit@google.com>
---
 elf/get-dynamic-info.h | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h
index dc8359d..45c51aa 100644
--- a/elf/get-dynamic-info.h
+++ b/elf/get-dynamic-info.h
@@ -66,8 +66,19 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp)
 	info[DT_VALTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
 	     + DT_VERSIONTAGNUM + DT_EXTRANUM] = dyn;
       else if ((d_tag_utype) DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM)
-	info[DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
-	     + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn;
+	{
+	  /* Use local 'tag_ix' to avoid gcc picking large offset:
+	       DT_ADDRTAGIDX (x) = 0x6ffffeff - x
+	     which leads gcc to use R_IA64_REL64LSB relocation.
+	     We cannot allow it as 'elf_get_dynamic_info' is called
+	     before relocations are processed by 'ld.so'.
+	     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60465#c32.
+	   */
+	  d_tag_utype tag_ix =
+	      DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
+	    + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM;
+	  info[tag_ix] = dyn;
+	}
       ++dyn;
     }
 
-- 
2.6.4



More information about the Libc-alpha mailing list