From 822ff8e1cd49480810b622b0cf8c1f64b38962b9 Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Sat, 26 Dec 2015 17:56:22 +0000 Subject: [PATCH] elf/get-dynamic-info.h: fix early GNU_HASH processing on ia64 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 --- 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