Bug 60465 - [4.9/5 Regression] Compiling glibc-2.17,2.18 with gcc-4.8.2 and binutils-2.23.2,2.24 results in segfaults in _start / elf_get_dynamic_info
Summary: [4.9/5 Regression] Compiling glibc-2.17,2.18 with gcc-4.8.2 and binutils-2.23...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: other (show other bugs)
Version: 4.8.2
: P3 normal
Target Milestone: 4.9.4
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 60558 (view as bug list)
Depends on:
Blocks:
 
Reported: 2014-03-08 12:08 UTC by devurandom
Modified: 2018-07-22 19:43 UTC (History)
8 users (show)

See Also:
Host: ia64-unknown-linux-gnu
Target: ia64-unknown-linux-gnu
Build: ia64-unknown-linux-gnu
Known to work: 4.7.3
Known to fail: 4.8.2, 4.9.3, 5.3.0
Last reconfirmed: 2014-08-14 00:00:00


Attachments
backtrace (glibc-2.18,binutils-2.24,gcc-4.8.2) (2.49 KB, text/x-log)
2014-03-08 12:08 UTC, devurandom
Details
emerge --info (2.37 KB, application/x-info)
2014-03-08 12:09 UTC, devurandom
Details
gdb session (9.92 KB, text/plain)
2014-03-19 05:35 UTC, Mike Frysinger
Details
rtld.i preprocessed source (123.96 KB, text/plain)
2014-08-14 11:57 UTC, Mike Frysinger
Details
rtld.s generated -- passing w/r187038 (91.06 KB, text/plain)
2014-08-16 14:27 UTC, Mike Frysinger
Details
rtld.s generated -- failing w/r187042 (91.05 KB, text/plain)
2014-08-16 14:28 UTC, Mike Frysinger
Details
Reduced testcase (130 bytes, text/x-csrc)
2014-08-18 14:07 UTC, Eric Botcazou
Details
Better reduced testcase (171 bytes, text/plain)
2014-08-19 21:46 UTC, Eric Botcazou
Details
0001-ia64-don-t-use-dynamic-relocations-for-local-symbols.patch (1.11 KB, patch)
2015-12-28 17:46 UTC, Sergei Trofimovich
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description devurandom 2014-03-08 12:08:38 UTC
Created attachment 32309 [details]
backtrace (glibc-2.18,binutils-2.24,gcc-4.8.2)

I tried to build glibc-2.17 and 2.18 with binutils-2.23.2 or 2.24 and gcc-4.8.2, but always run into the same segfault when loading programs with the new runtime linker.

Please find a backtrace for the glibc-2.18, binutils-2.24, gcc-4.8.2 combination attached.

It crashes in exactly the same way when running without --library-path and when running with ../usr/bin/locale (from glibc-2.18) or /bin/date as argument.

Since glibc-2.17 compiled fine with gcc-4.7.3, I assume that gcc-4.8.2 is to blame.

The context of the crash is (according to glibc-2.18/elf/get-dynamic-info.h):
elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp) {
  ElfW(Dyn) **info;
  info = l->l_info;
  info[DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn;
}

GDB reports this function being called as:
elf_get_dynamic_info (temp=0x0, l=0x2000000800051458 <_rtld_local+2456>)

What I find to be suspicious is the changed order of parameters.

I would like to check this with valgrind, too, but it is not available on ia64 (i.e. anything but x86, ppc and arm).

See-Also: https://bugs.gentoo.org/show_bug.cgi?id=503838
Comment 1 devurandom 2014-03-08 12:09:09 UTC
Created attachment 32310 [details]
emerge --info
Comment 2 Mike Frysinger 2014-03-18 07:18:30 UTC
*** Bug 60558 has been marked as a duplicate of this bug. ***
Comment 3 devurandom 2014-03-18 07:29:42 UTC
 Mike Frysinger 2014-03-18 07:17:05 UTC

if you build current master glibc w/gcc-4.8.2 & binutils-2.23.2 like so:
  ../configure --prefix=/usr libc_cv_ehdr_start=no
  make -j4

then try to run a simple app like so:
  echo 'main(){puts("HI");}' | gcc -x c - -o a.out
  ./elf/ld.so --library-path $PWD ./a.out

it crashes like so:
  Segmentation fault (core dumped)

i suspect bad code generation because:
  - recompiling only elf/rtld.c w/gcc-4.7.3 produces a working build
  - the C code looks correct (via poking; see below)
  - tweaking the code slightly produces a working build w/4.8:
-  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;
+    {
+      size_t i = DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
+                 + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM;
+      info[i] = dyn;
+    }

throwing it into a debugger shows:
$ gdb --args ./elf/ld.so --library-path $PWD ./a.out 
Reading symbols from /home/vapier/glibc/build/elf/ld.so...done.

(gdb) run
Starting program: /home/vapier/glibc/build/./elf/ld.so --library-path /home/vapier/glibc/build ./a.out

Program received signal SIGSEGV, Segmentation fault.
0x200000080000b010 in elf_get_dynamic_info (temp=0x0, l=0x2000000800051b08 <_rtld_local+2456>) at get-dynamic-info.h:61
61                   + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn;

(gdb) list
56            else if ((d_tag_utype) DT_VALTAGIDX (dyn->d_tag) < DT_VALNUM)
57              info[DT_VALTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
58                   + DT_VERSIONTAGNUM + DT_EXTRANUM] = dyn;
59            else if ((d_tag_utype) DT_ADDRTAGIDX (dyn->d_tag) < DT_ADDRNUM)
60              info[DT_ADDRTAGIDX (dyn->d_tag) + DT_NUM + DT_THISPROCNUM
61                   + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn;
62            ++dyn;
63          }
64
65      #define DL_RO_DYN_TEMP_CNT      8

the info pointer is sane:

(gdb) p &_rtld_global._dl_rtld_map.l_info 
$1 = (Elf64_Dyn *(*)[77]) 0x20000008000515d8 <_rtld_local+2520>
(gdb) print info
$2 = (Elf64_Dyn **) 0x20000008000515d8 <_rtld_local+2520>

as is the dyn tag:

(gdb) print dyn
$3 = (Elf64_Dyn *) 0x200000080004c8d8
(gdb) print *dyn
$4 = {
  d_tag = 0x6ffffef5, 
  d_un = {
    d_val = 0x2d8, 
    d_ptr = 0x2d8
  }
}

that calculated offset is 0x4c and the link map is big enough to hold it:

(gdb) print sizeof(_rtld_local._dl_rtld_map.l_info) / sizeof(_rtld_local._dl_rtld_map.l_info[0])
$5 = 0x4d

but the assembly is clearly wrong:
(gdb) display/i $pc
1: x/i $pc
=> 0x200000080000b271 <_dl_start+2737>:       (p07) st8 [r14]=r15

(gdb) p $r15
$6 = 0x200000080004c8d8

(gdb) p $r14
$7 = 0x51838

$r15 is set to the right value (dyn), but r14 is now incomplete.  stepping through the previous ~20 insns shows that the right value doesn't get near $r14 ... but my ia64 asm skills are not great, so i could be missing something.
Comment 4 Andreas Schwab 2014-03-18 10:31:49 UTC
How about showing the previous ~20 insns here.
Comment 5 Mike Frysinger 2014-03-19 05:35:14 UTC
Created attachment 32385 [details]
gdb session

here's the trace from the first insn where dyn is pointing to DT_GNU_HASH.  at this point, it has processed DT_SONAME and DT_HASH.

$ readelf -d build/elf/ld.so

Dynamic section at offset 0x3c8b8 contains 20 entries:
  Tag        Type                         Name/Value
 0x000000000000000e (SONAME)             Library soname: [ld-linux-ia64.so.2]
 0x0000000000000004 (HASH)               0x190
 0x000000006ffffef5 (GNU_HASH)           0x2d8
 0x0000000000000005 (STRTAB)             0x998
Comment 6 devurandom 2014-03-25 18:23:33 UTC
Did anyone figure out what's going on? Did the gdb log bring new insights?
Comment 7 Mike Frysinger 2014-03-25 22:17:41 UTC
it's beyond my (ia64 beginners) experience to track this down further

i can certainly make available ssh access to interested devs ... it's a fast system on a fast edu connection
Comment 8 devurandom 2014-03-25 23:01:04 UTC
(In reply to Mike Frysinger from comment #7)
> it's beyond my (ia64 beginners) experience to track this down further
> 
> i can certainly make available ssh access to interested devs ... it's a fast
> system on a fast edu connection

Similar situation and offer from me: Slow system on a fast connection available for the dev who wants to track this down, but lacks an own machine.
Comment 9 Mike Frysinger 2014-08-12 14:11:39 UTC
i've verified that 4.8.0 & 4.9.1 fail as well :/

binutils 2.24 doesn't help
Comment 10 Mike Frysinger 2014-08-14 03:26:02 UTC
i've bisected things back to r188118.  before that commit, gcc compiles rtld.c fine and produces a working ldso.  starting at that commit, we get segfaults.
Comment 11 Andrew Pinski 2014-08-14 03:46:23 UTC
This sounds like the same bug which I fixed with the set of patches at https://gcc.gnu.org/ml/gcc-patches/2014-08/msg00932.html .
Comment 12 Eric Botcazou 2014-08-14 07:12:58 UTC
> i've bisected things back to r188118.  before that commit, gcc compiles
> rtld.c fine and produces a working ldso.  starting at that commit, we get
> segfaults.

But not on the 4.7 branch, right?  In any case, we need preprocessed sources.
Comment 13 Eric Botcazou 2014-08-14 07:21:39 UTC
> i've bisected things back to r188118.  before that commit, gcc compiles
> rtld.c fine and produces a working ldso.  starting at that commit, we get
> segfaults.

In fact r188118 undoes a pessimization introduced just before in r188009 so the bug was very likely preexisting on the mainline.
Comment 14 Andrew Pinski 2014-08-14 07:35:14 UTC
(In reply to Eric Botcazou from comment #12)
> > i've bisected things back to r188118.  before that commit, gcc compiles
> > rtld.c fine and produces a working ldso.  starting at that commit, we get
> > segfaults.
> 
> But not on the 4.7 branch, right?  In any case, we need preprocessed sources.

I bet the function f mentioned in the testcase from https://gcc.gnu.org/ml/gcc-patches/2014-08/msg00932.html is enough to reproduce the issue.  

DT_ADDRTAGIDX (dyn->d_tag) gets preprocessed as (0x6ffffeff - dyn->d_tag).

DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM is the same as 34+0+16+3+12.
Comment 15 Mike Frysinger 2014-08-14 11:55:49 UTC
(In reply to Andrew Pinski from comment #11)

i tried 4.8.3 w/those two patches applied but still see the crash :(

(In reply to Eric Botcazou from comment #12)

correct, gcc-4.6.4 & gcc-4.7.3 work fine
Comment 16 Mike Frysinger 2014-08-14 11:57:36 UTC
Created attachment 33321 [details]
rtld.i preprocessed source

the preprocessed output is the same between 4.7.4 & 4.8.3 (checked with `diff`)

this was generated with:
$ gcc rtld.c -E -dD -std=gnu99 -fgnu89-inline  -O2 -Wall -Winline -Wundef -Wwrite-strings -fmerge-all-constants -frounding-math -g -Wstrict-prototypes   -fPIC -D'SYSCONFDIR="/etc"'    -U_FORTIFY_SOURCE   -I../include -I/home/vapier/glibc/build/elf  -I/home/vapier/glibc/build  -I../sysdeps/unix/sysv/linux/ia64  -I../sysdeps/ia64/nptl  -I../sysdeps/unix/sysv/linux/wordsize-64  -I../sysdeps/unix/sysv/linux  -I../sysdeps/nptl  -I../sysdeps/pthread  -I../sysdeps/gnu  -I../sysdeps/unix/inet  -I../sysdeps/unix/sysv  -I../sysdeps/unix  -I../sysdeps/posix  -I../sysdeps/ia64/fpu  -I../sysdeps/ia64  -I../sysdeps/wordsize-64  -I../sysdeps/ieee754/ldbl-96  -I../sysdeps/ieee754/dbl-64  -I../sysdeps/ieee754/flt-32  -I../sysdeps/ieee754  -I../sysdeps/generic  -I.. -I../libio -I.   -D_LIBC_REENTRANT -include ../include/libc-symbols.h  -DPIC -DSHARED  -DNOT_IN_libc=1 -DIS_IN_rtld=1 -DIN_LIB=rtld     -D_ASM_IA64_CURRENT_H -o /home/vapier/glibc/build/elf/rtld.i
Comment 17 Mike Frysinger 2014-08-14 12:27:46 UTC
(In reply to Eric Botcazou from comment #13)

fwiw, i took latest 4.8 branch and reverted that change, and ldso works

i'll test r188009 and related too though
Comment 18 Mike Frysinger 2014-08-16 14:22:34 UTC
thanks for your help Eric.  new bisection shows r187042 as a possible culprit.  feel free to de-cc yourself :).

Richard: any thoughts here ?  this change is a bit harder to test reverting in the latest 4.8 branch to see if it makes a difference.
Comment 19 Mike Frysinger 2014-08-16 14:27:44 UTC
Created attachment 33340 [details]
rtld.s generated -- passing w/r187038
Comment 20 Mike Frysinger 2014-08-16 14:28:17 UTC
Created attachment 33341 [details]
rtld.s generated -- failing w/r187042
Comment 21 Eric Botcazou 2014-08-16 15:40:51 UTC
> thanks for your help Eric.  new bisection shows r187042 as a possible
> culprit.  feel free to de-cc yourself :).

I'll keep investigating because this rings a bell, we had the same issue on SPARC 64-bit at some point and this might be a hole in the IA-64 back-end.
Comment 22 Richard Biener 2014-08-18 07:57:57 UTC
(In reply to Mike Frysinger from comment #18)
> thanks for your help Eric.  new bisection shows r187042 as a possible
> culprit.  feel free to de-cc yourself :).
> 
> Richard: any thoughts here ?  this change is a bit harder to test reverting
> in the latest 4.8 branch to see if it makes a difference.

Well, usual errors regarding to sizetype apply - you have to treat it
as sign-extending if you promote it to larger types (but I doubt that happens
or matters for ia64 as pointers should be DImode, right?).

But you should be able to spot code-gen differences and see where they originate
from (the revision wasn't supposed to change code-gen though zero differences
wasn't really possible).

I'll wait for Erics investigation.  (ia64 is a dead architecture IMNSHO)
Comment 23 Eric Botcazou 2014-08-18 14:07:56 UTC
Created attachment 33349 [details]
Reduced testcase

To be compiled at -O1.
Comment 24 Eric Botcazou 2014-08-18 21:14:35 UTC
I might have a plausible scenario, but I'd need more info:

  1. the options used to link the runtime linker

  2. the value of registers r25 and r23 right after:

   0x200000080000a8f0 <+304>:   [MMI]       ld8 r25=[r25]
   0x200000080000a8f1 <+305>:               ld8 r23=[r23]
   0x200000080000a8f2 <+306>:               nop.i 0x0;;

for the invocation of _dl_start that leads to the segfault.
Comment 25 Mike Frysinger 2014-08-19 00:16:11 UTC
here's the series of link commands:
gcc -nostdlib -nostartfiles -r -o elf/librtld.os \
  '-Wl,-(' /home/vapier/glibc/build/elf/dl-allobjs.os elf/rtld-libc.a -lgcc \
  '-Wl,-)' -Wl,-Map,elf/librtld.os.map
gcc -nostdlib -nostartfiles -shared -o elf/ld.so \
  -Wl,-z,combreloc -Wl,-z,relro -Wl,--hash-style=both -Wl,-z,defs \
  elf/librtld.os -Wl,--version-script=./ld.map \
      -Wl,-soname=ld-linux-ia64.so.2 \
      -Wl,-defsym=_begin=0

in my disassembly it's using r24 & r22, but i'm guessing that doesn't matter terribly much:
$ gdb --args ./elf/ld.so --library-path $PWD ./a.out 
Reading symbols from /home/vapier/glibc/build/elf/ld.so...done.
(gdb) b *_dl_start+304
Breakpoint 1 at 0xabb0: file get-dynamic-info.h, line 61.
(gdb) display /i $pc
(gdb) display $r24
(gdb) display $r22
(gdb) r
Starting program: /home/vapier/glibc/build/./elf/ld.so --library-path /home/vapier/glibc/build ./a.out

Breakpoint 1, elf_get_dynamic_info (temp=0x0, l=0x20000008000510c8 <_rtld_local+2456>) at get-dynamic-info.h:61
61                   + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn;
3: $r22 = 0x20000008000505d8
2: $r24 = 0x20000008000505d8
1: x/i $pc
=> 0x200000080000abb0 <_dl_start+304>:  [MMI]       ld8 r24=[r24]
(gdb) stepi
58                   + DT_VERSIONTAGNUM + DT_EXTRANUM] = dyn;
3: $r22 = 0x20000008000505d8
2: $r24 = 0x380050730
1: x/i $pc
=> 0x200000080000abb1 <_dl_start+305>:              ld8 r22=[r22]
(gdb) stepi
61                   + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn;
3: $r22 = 0x380050730
2: $r24 = 0x380050730
1: x/i $pc
=> 0x200000080000abb2 <_dl_start+306>:              nop.i 0x0;;
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x200000080000b5f1 in elf_get_dynamic_info (temp=0x0, l=0x20000008000510c8 <_rtld_local+2456>) at get-dynamic-info.h:61
61                   + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM] = dyn;
3: $r22 = 0x3800502b0
2: $r24 = 0x380050b10
1: x/i $pc
=> 0x200000080000b5f1 <_dl_start+2929>:       (p07) st8 [r14]=r15
(gdb)
Comment 26 Eric Botcazou 2014-08-19 21:46:34 UTC
Created attachment 33365 [details]
Better reduced testcase
Comment 27 Eric Botcazou 2014-08-19 22:02:22 UTC
Thanks.  This seems to be a conjunction of several factors, the initial one being that the 4.8+ compiler generates (e.g for the reduced testcase at -O):

        addl r14 = @ltoffx(_rtld_local#+15032385536), r1
        ;;
        ld8.mov r14 = [r14], _rtld_local#+15032385536

The huge number is not problematic per se, although it very likely runs afoul of some limitation/quirk here, since the value loaded from the GOT is truncated.

In fact it looks like the value loaded from the GOT is just the huge number, that is to say the value of _rtld_local has been zeroed during the relocation.

This may come from _rtld_local being in the .sdata section, in which case there is a relevant comment in sdata_symbolic_operand:

      /* Deny the stupid user trick of addressing outside the object.  Such
	 things quickly result in GPREL22 relocation overflows.  Of course,
	 they're also highly undefined.  From a pure pedant's point of view
	 they deserve a slap on the wrist (such as provided by a relocation
	 overflow), but that just leads to bugzilla noise.  */

In other words, the compiler skips the efficient @gprel relocation on purpose, only to generate the @ltoffx relocation, which doesn't work either here...
Comment 28 Sergei Trofimovich 2015-12-24 22:38:53 UTC
(In reply to Eric Botcazou from comment #27)
> Thanks.  This seems to be a conjunction of several factors, the initial one
> being that the 4.8+ compiler generates (e.g for the reduced testcase at -O):
> 
>         addl r14 = @ltoffx(_rtld_local#+15032385536), r1
>         ;;
>         ld8.mov r14 = [r14], _rtld_local#+15032385536
> 
> The huge number is not problematic per se, although it very likely runs
> afoul of some limitation/quirk here, since the value loaded from the GOT is
> truncated.
> 
> In fact it looks like the value loaded from the GOT is just the huge number,
> that is to say the value of _rtld_local has been zeroed during the
> relocation.
> 
> This may come from _rtld_local being in the .sdata section, in which case
> there is a relevant comment in sdata_symbolic_operand:
> 
>       /* Deny the stupid user trick of addressing outside the object.  Such
> 	 things quickly result in GPREL22 relocation overflows.  Of course,
> 	 they're also highly undefined.  From a pure pedant's point of view
> 	 they deserve a slap on the wrist (such as provided by a relocation
> 	 overflow), but that just leads to bugzilla noise.  */
> 
> In other words, the compiler skips the efficient @gprel relocation on
> purpose, only to generate the @ltoffx relocation, which doesn't work either
> here...

Hi Eric! I've poked this bug a bit more
and still don't understand what does this instruction mean:

    ld8.mov r14 = [r14], _rtld_local#+15032385536

Where is '_rtld_local#+15032385536' offset expected to be used?
Comment 29 Andreas Schwab 2015-12-25 09:28:35 UTC
'_rtld_local#+15032385536' is just a hint that is passed through to the linker in the addend of the R_IA64_LDXMOV relocation for the insn.  That tells the linker the addend of the R_IA64_LTOFF22X relocation associated with the corresponding addl insn when processing the R_IA64_LDXMOV relocation during the relaxing pass.  This is necessary because the addl/ld8 pair can be separated by arbitrary other insns.
Comment 30 Sergei Trofimovich 2015-12-25 16:49:26 UTC
Aha, got it.

I've factored out GNU_HASH section setup crash into separate
very simple function:

  void __attribute__ ((unused, noinline))
  do_it_again (struct link_map *l, ElfW(Dyn) *dyn)
  {
    ElfW(Dyn) **info = l->l_info;

    info[1879047935 - dyn->d_tag + 66] = dyn;
  }

gcc -O2 specialises this function on 'l=_rtld_local._dl_rtld_map' argument.
Broken code looks as:

  Program received signal SIGSEGV, Segmentation fault.
  0x200000080000a9f0 in do_it_again (dyn=0x200000080004e710, l=0x20000008000533f8 <_rtld_local+2456>) at get-dynamic-info.h:42
  42        info[1879047935 - dyn->d_tag + 66] = dyn;
  (gdb) disassemble
  Dump of assembler code for function do_it_again:
     0x200000080000a9c0 <+0>:     [MMI]       ld8 r14=[r32];;
     0x200000080000a9c1 <+1>:                 shladd r15=r14,3,r0
     0x200000080000a9c2 <+2>:                 addl r14=163312,r1;;
     0x200000080000a9d0 <+16>:    [MMI]       ld8 r14=[r14];;
     0x200000080000a9d1 <+17>:                adds r14=992,r14
     0x200000080000a9d2 <+18>:                nop.i 0x0;;
     0x200000080000a9e0 <+32>:    [MMI]       nop.m 0x0
     0x200000080000a9e1 <+33>:                sub r14=r14,r15
     0x200000080000a9e2 <+34>:                nop.i 0x0;;
  => 0x200000080000a9f0 <+48>:    [MIB]       st8 [r14]=r32
     0x200000080000a9f1 <+49>:                nop.i 0x0
     0x200000080000a9f2 <+50>:                br.ret.sptk.many b0;;
  End of assembler dump.

It's a very early setup phase:

  (gdb) bt
  #0  0x200000080000a9f0 in do_it_again (dyn=0x200000080004e710, l=0x20000008000533f8 <_rtld_local+2456>)
      at get-dynamic-info.h:42
  #1  0x200000080000abd0 in do_it (l=0x20000008000533f8 <_rtld_local+2456>) at get-dynamic-info.h:78
  #2  0x200000080000ac20 in elf_get_dynamic_info (temp=0x0, l=0x20000008000533f8 <_rtld_local+2456>) at get-dynamic-info.h:104
  #3  0x200000080000b2a0 in _dl_start (arg=0x60000fffffffb350) at rtld.c:391
  #4  0x2000000800001b10 in _start ()

What I don't like here:

'ld' is a static relocatable binary but the specialised code
attempts to write by absolute address stored in ld's memory image:

    MEM[ MEM[r1 + 163312]
       + 992
       - dyn->d_tag /* absolute address, not r1 relative!*/ 
       ] = dyn;

I would expect something in '_start:'

    https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/ia64/dl-machine.h;h=57d761e48732cfa5de370e7022eef83ae74a9c65;hb=HEAD#l159

to store current gp(r1) address to make code above work. Or PIC
code not to rely on any relocations like that:

    MEM[ r1 /* real PIC */
       + MEM[r1 + 163312]
       + 992
       - dyn->d_tag
       ] = dyn;

Who is expected to setup that 'r1 + 163312' location?
Comment 31 Andreas Schwab 2015-12-25 17:22:21 UTC
@ltoffx on symbol+addend doesn't make any sense.  This computes an offset into the GOT, so any addend needs to be added _after_ loading the actual symbol address from the GOT.
Comment 32 Sergei Trofimovich 2015-12-26 16:11:59 UTC
(In reply to Andreas Schwab from comment #31)
> @ltoffx on symbol+addend doesn't make any sense.  This computes an offset
> into the GOT, so any addend needs to be added _after_ loading the actual
> symbol address from the GOT.

Aha. That +addend seems to be some sort of hint to assembler how large addend must be.

The real addend is 992.

The address '$r1 + 163312' points to a relocation of type R_IA64_REL64LSB itself:
  (gdb) print *(void**)($r1 + 163312)
  $1 = (void *) 0x380052a60

  readelf -a ld.so
  ...
  Relocation section '.rela.dyn' at offset 0xdd0 contains 51 entries:
    Offset          Info           Type           Sym. Value    Sym. Name + Addend
  ...
  000000052910  00000000006f R_IA64_REL64LSB                      380052a60

(or how objdump shows the same) objdump -r -R ld.so:
  DYNAMIC RELOCATION RECORDS
  OFFSET           TYPE              VALUE 
  ...
  0000000000052910 REL64LSB          *ABS*+0x0000000380052a60

That dynamic relocation is expected to be fixed after ld reads all elf header types, not before (we see crash exactly at elf header read time):

    https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/rtld.c;h=52160dfde6be42eba000c4e8136de0d190617270;hb=HEAD

 377   /* Figure out the run-time load address of the dynamic linker itself.  */
 378   bootstrap_map.l_addr = elf_machine_load_address ();
 379 
 380   /* Read our own dynamic section and fill in the info array.  */
 381   bootstrap_map.l_ld = (void *) bootstrap_map.l_addr + elf_machine_dynamic ();
 382   elf_get_dynamic_info (&bootstrap_map, NULL);
 383 
 ...
 392   if (bootstrap_map.l_addr || ! bootstrap_map.l_info[VALIDX(DT_GNU_PRELINKED)])
 393     {
 394       /* Relocate ourselves so we can do normal function calls and
 395          data access using the global offset table.  */
 396 
 397       ELF_DYNAMIC_RELOCATE (&bootstrap_map, 0, 0, 0);
 398     }
 399   bootstrap_map.l_relocated = 1;
 400 
 401   /* Please note that we don't allow profiling of this object and
 402      therefore need not test whether we have to allocate the array
 403      for the relocation results (as done in dl-reloc.c).  */
 404 
 405   /* Now life is sane; we can call functions and access global data.
 406      Set up to use the operating system facilities, and find out from
 407      the operating system's program loader where to find the program
 408      header table in core.  Put the rest of _dl_start into a separate
 409      function, that way the compiler cannot put accesses to the GOT
 410      before ELF_DYNAMIC_RELOCATE.  */

Note we crash at line 378 while relocations are processed later(!): at line 397. That's why writing simple selfcontained examples does not make the samples crash: running the example already has relocations adjusted correctly.

Behaviour started exhibiting itself as constant propagation got smarter and managed to specialise 'elf_get_dynamic_info ()' for 'elf_get_dynamic_info (&_rtld_local, ...)' where

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

specialises it to:

    addl r14=163312,r1;;
    ld8 r14=[r14];;
    adds r14=992,r14

Numbers explanation:

    printf "0x%x\n" $(( (0x6ffffeff + 66) * 8 + 2520))
        # 2520 is offset of '_rtld_local._dl_rtld_map.l_info'
    0x3800003e0
        # 0x3e0 = 992

large offset to an imm overflowing ${something} and pushes it (+ip offset?) out to a separate relocation:
  DYNAMIC RELOCATION RECORDS
  OFFSET           TYPE              VALUE 
  ...
  0000000000052910 REL64LSB          *ABS*+0x0000000380052a60

Sounds plausible?

Thus I'd say it's not a gcc/binutils bug and can/should be worked around in glibc.
Comment 33 Sergei Trofimovich 2015-12-26 19:34:40 UTC
Sent glibc workaround for review as:
https://sourceware.org/ml/libc-alpha/2015-12/msg00556.html
Comment 34 Sergei Trofimovich 2015-12-27 21:42:32 UTC
Wrote more details on how exactly things were broken:
http://trofi.github.io/posts/189-glibc-on-ia64-or-how-relocations-bootstrap.html
Comment 35 Sergei Trofimovich 2015-12-28 17:46:01 UTC
Created attachment 37180 [details]
0001-ia64-don-t-use-dynamic-relocations-for-local-symbols.patch

0001-ia64-don-t-use-dynamic-relocations-for-local-symbols.patch - how about this patch?

It tries to capture any local symbol into gprel64 addressing (as opposed to .got addressing).

That way relocation will be static.
Comment 36 Sergei Trofimovich 2015-12-28 22:09:03 UTC
Tested the patch on top of gcc-5.3.0 / glibc-2.22-r1.

Relocation in rtld.os is R_IA64_GPREL64I and is just an immediate offset in ld.so:

# objdump -r -d elf/rtld.os | grep -C4 rtld_local+0x38
    97c0:       11 00 00 00 01 00       [MIB]       nop.m 0x0
    97c6:       00 00 00 02 80 03                   nop.i 0x0
    97cc:       10 01 00 43                   (p07) br.cond.dpnt.few 98d0 <_dl_start+0x210>;;
    97d0:       04 90 00 4d 00 21       [MLX]       adds r18=64,r38
                        97d1: GPREL64I  _rtld_local+0x3800003e0
    97d6:       00 00 00 00 00 40                   movl r26=0x0
    97dc:       03 00 00 60 
    97e0:       05 b0 40 4d 02 21       [MLX]       adds r22=336,r38
                        97e1: GPREL64I  _rtld_local+0x37ffffb80

# objdump -r -d elf/ld.so | grep -C8 '_dl_start+0x210'

    b200:       11 00 00 00 01 00       [MIB]       nop.m 0x0
    b206:       00 00 00 02 80 03                   nop.i 0x0
    b20c:       10 01 00 43                   (p07) br.cond.dpnt.few b310 <_dl_start+0x210>;;
    b210:       04 90 00 4d 00 21       [MLX]       adds r18=64,r38
    b216:       80 03 00 00 00 40                   movl r26=0x380029110
    b21c:       03 41 88 64 
    b220:       05 b0 40 4d 02 21       [MLX]       adds r22=336,r38
    b226:       80 03 00 00 00 00                   movl r24=0x3800288b0;;

Sent for review as:
https://gcc.gnu.org/ml/gcc-patches/2015-12/msg02193.html
Comment 37 Sergei Trofimovich 2015-12-29 10:35:41 UTC
The patch also fixes this old bug:
    https://bugzilla.redhat.com/show_bug.cgi?id=33354

Despite being marked as fixed it fails on modern toolchain with slight overflow.
Address of local constant was done over .got as well:

.c source:
    static char *s90;
    void f() { s90 = "string 90"; }

Compiled as gcc -fpic -S.

before:

        .sbss
    s90:
        .skip   8

        .section        .rodata
    .LC0:
        stringz "string 90"

        .text
    f:
        ...
        addl r14 = @gprel(s90#), gp
        addl r15 = @ltoffx(.LC0), r1
        ld8.mov r15 = [r15], .LC0
        st8 [r14] = r15
        ...
 
 after:

        .sbss
    s90:
        .skip   8

        .section        .rodata
    .LC0:
        stringz "string 90"

        .text
    f:
        ...
        addl r14 = @gprel(s90#), gp
        movl r15 = @gprel(.LC0)
        add r15 = r1, r15
        st8 [r14] = r15
Comment 38 Jeffrey A. Law 2016-01-05 17:57:37 UTC
Author: law
Date: Tue Jan  5 17:57:05 2016
New Revision: 232080

URL: https://gcc.gnu.org/viewcvs?rev=232080&root=gcc&view=rev
Log:
[PATCH v2] ia64: don't use dynamic relocations for local symbols
	PR other/60465
	* config/ia64/ia64.c (ia64_expand_load_address): Use gprel64
	for local symbolic operands.
	* config/ia64/predicates.md (local_symbolic_operand64): New
	predicate.

        PR other/60465
	* gcc.target/ia64/pr60465-gprel64.c: New test.
	* gcc.target/ia64/pr60465-gprel64-c37.c: New test.

Added:
    trunk/gcc/testsuite/gcc.target/ia64/pr60465-gprel64-c37.c
    trunk/gcc/testsuite/gcc.target/ia64/pr60465-gprel64.c
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/config/ia64/ia64.c
    trunk/gcc/config/ia64/predicates.md
    trunk/gcc/testsuite/ChangeLog
Comment 39 Jeffrey A. Law 2016-01-05 17:58:18 UTC
Fixed on the trunk.  Patch is backportable to the active release branches, but I'm not sure it's worth the effort.
Comment 40 Mike Frysinger 2016-01-05 18:56:22 UTC
(In reply to Jeffrey A. Law from comment #39)

we should at least do gcc-5.  i'm guessing there won't be another 4.9.
Comment 41 Richard Biener 2016-01-08 12:59:41 UTC
There will be.
Comment 42 Mike Frysinger 2016-01-18 22:37:22 UTC
cc-ing the people listed as ia64 maintainers ... can we get someone to just approve the patch for gcc-4.9 & gcc-5 branches ?  i can take care of actually committing it.
Comment 43 Jeffrey A. Law 2016-01-19 05:32:10 UTC
Mike, consider this approval for backporting the fix to the release branches.
Comment 44 Mike Frysinger 2016-01-19 23:12:54 UTC
Author: vapier
Date: Tue Jan 19 23:12:22 2016
New Revision: 232594

URL: https://gcc.gnu.org/viewcvs?rev=232594&root=gcc&view=rev
Log:
ia64: don't use dynamic relocations for local symbols

Backported from trunk for PR other/60465.

Added:
    branches/gcc-5-branch/gcc/testsuite/gcc.target/ia64/pr60465-gprel64-c37.c
    branches/gcc-5-branch/gcc/testsuite/gcc.target/ia64/pr60465-gprel64.c
Modified:
    branches/gcc-5-branch/gcc/ChangeLog
    branches/gcc-5-branch/gcc/config/ia64/ia64.c
    branches/gcc-5-branch/gcc/config/ia64/predicates.md
    branches/gcc-5-branch/gcc/testsuite/ChangeLog
Comment 45 Mike Frysinger 2016-01-19 23:15:44 UTC
Author: vapier
Date: Tue Jan 19 23:15:12 2016
New Revision: 232595

URL: https://gcc.gnu.org/viewcvs?rev=232595&root=gcc&view=rev
Log:
ia64: don't use dynamic relocations for local symbols

Backported from trunk for PR other/60465.

Added:
    branches/gcc-4_9-branch/gcc/testsuite/gcc.target/ia64/pr60465-gprel64-c37.c
    branches/gcc-4_9-branch/gcc/testsuite/gcc.target/ia64/pr60465-gprel64.c
Modified:
    branches/gcc-4_9-branch/gcc/ChangeLog
    branches/gcc-4_9-branch/gcc/config/ia64/ia64.c
    branches/gcc-4_9-branch/gcc/config/ia64/predicates.md
    branches/gcc-4_9-branch/gcc/testsuite/ChangeLog
Comment 46 Mike Frysinger 2016-01-19 23:16:19 UTC
thanks all !
Comment 47 The Written Word 2018-07-22 18:50:13 UTC
This patch caused a regression on HP-UX/IA between gcc-4.9.3 and gcc-4.9.4. Reverting the patch makes the build on this platform succeed for 4.9.4. However, considering this platform is probably not even actively maintained on GCC anymore, this report might be meaningless.
Comment 48 Sergei Trofimovich 2018-07-22 19:33:13 UTC
(In reply to The Written Word from comment #47)
> This patch caused a regression on HP-UX/IA between gcc-4.9.3 and gcc-4.9.4.
> Reverting the patch makes the build on this platform succeed for 4.9.4.
> However, considering this platform is probably not even actively maintained
> on GCC anymore, this report might be meaningless.

I suggest filing a new bug report with details of what/how does not compile anymore. Perhaps it's easy to tweak.
Comment 49 The Written Word 2018-07-22 19:43:31 UTC
(In reply to Sergei Trofimovich from comment #48)
> I suggest filing a new bug report with details of what/how does not compile
> anymore. Perhaps it's easy to tweak.

Ok.