Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 12 Jan 2017 20:22:23 +0100
From:      Roman Divacky <rdivacky@vlakno.cz>
To:        Mark Millard <markmi@dsl-only.net>
Cc:        Ed Maste <emaste@freebsd.org>, FreeBSD Toolchain <freebsd-toolchain@freebsd.org>
Subject:   Re: /usr/bin/ld.lld on powerpc64: produces a.out for which: ld-elf.so.1: assert failed: /usr/src/libexec/rtld-elf/powerpc64/reloc.c:374
Message-ID:  <20170112192223.GA49469@vlakno.cz>
In-Reply-To: <EF97071B-AE4A-4520-A997-52249B8DAB5A@dsl-only.net>
References:  <7139F615-8F18-4EDC-9051-5FFEC0C4057F@dsl-only.net> <CAPyFy2CSmNyUjQcXyq4qUWp_A=Qr81O7fpRbZ5GcfFHFhdSntw@mail.gmail.com> <CF2D86C8-8EEB-4536-8D83-6F8C676EFEF6@dsl-only.net> <20170111194844.GA16135@vlakno.cz> <8242A7B9-7ED3-4861-8209-F3728113D188@dsl-only.net> <20170111210658.GA20265@vlakno.cz> <CAPyFy2DG3ucUkxDCwRJ10a-nhC1=YvVrwR7v0dw6LJk=e61nvQ@mail.gmail.com> <EF97071B-AE4A-4520-A997-52249B8DAB5A@dsl-only.net>

next in thread | previous in thread | raw e-mail | index | archive | help
Can you check if the TOC is correct? LLD assumes this:

static uint64_t PPC64TocOffset = 0x8000;

uint64_t getPPC64TocBase() {
  // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
  // TOC starts where the first of these sections starts. We always create a
  // .got when we see a relocation that uses it, so for us the start is always
  // the .got.
  uint64_t TocVA = In<ELF64BE>::Got->getVA();

  // Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000
  // thus permitting a full 64 Kbytes segment. Note that the glibc startup
  // code (crt1.o) assumes that you can get from the TOC base to the
  // start of the .toc section with only a single (signed) 16-bit relocation.
  return TocVA + PPC64TocOffset;
}

Perhaps thats not true on FreeBSD? Especially the hardcoded constant seems suspicious.
 When it comes to the actual PLT entry, there's this comment in the code:

  // FIXME: What we should do, in theory, is get the offset of the function
  // descriptor in the .opd section, and use that as the offset from %r2 (the
  // TOC-base pointer). Instead, we have the GOT-entry offset, and that will
  // be a pointer to the function descriptor in the .opd section. Using
  // this scheme is simpler, but requires an extra indirection per PLT dispatch.

So I think that while it's different it might not be wrong. What might be wrong
is the TOC entry (either it's content or it's position).

I suspect there might be some Linux vs FreeBSD difference that prevents this from working.

Roman

On Thu, Jan 12, 2017 at 12:37:53AM -0800, Mark Millard wrote:
> On 2017-Jan-11, at 1:23 PM, Ed Maste <emaste at freebsd.org> wrote:
> 
> > On 11 January 2017 at 21:06, Roman Divacky <rdivacky at vlakno.cz> wrote:
> >> Looks like a progress :) Three questions...
> >> 
> >> Is the readelf -a reasonable now?
> > 
> > FYI, I just committed an ELF Tool Chain fix (r311941) so readelf
> > should display the relocation types properly now.
> 
> Thanks. I updated to -r311950 to pick this up.
> 
> >> If you compile with -g, does the
> >> backtrace make a bit more sense? And finally, can you try to "nexti/stepi" in gdb from
> >> _start to see where things go wrong? Possibly doing it both for ld linked a.out
> >> and lld linked a.out and compare where things differ.
> 
> I had compiled with -g. It never gets to main. . .
> 
> # /usr/local/bin/gdb a.out
> . . .
> Reading symbols from a.out...done.
> (gdb) start
> Temporary breakpoint 1 at 0x1001045c: file main.c, line 3.
> Starting program: /root/c_tests/a.out 
> 
> Program received signal SIGSEGV, Segmentation fault.
> 0x000000001001056c in ?? ()
> 
> Note that the temporary breakpoint is never hit.
> 
> (gdb) bt
> #0  0x000000001001056c in ?? ()
> #1  0x00000000100100d8 in ?? ()
> #2  0x00000000500279b0 in ._rtld_start () at /usr/src/libexec/rtld-elf/powerpc64/rtld_start.S:104
> Backtrace stopped: frame did not save the PC
> 
> (gdb) up 2
> #2  0x00000000500279b0 in ._rtld_start () at /usr/src/libexec/rtld-elf/powerpc64/rtld_start.S:104
> 104		blrl	/* _start(argc, argv, envp, obj, cleanup, ps_strings) */
> (gdb) disass
> Dump of assembler code for function ._rtld_start:
>    0x0000000050027930 <+0>:	stdu    r1,-144(r1)
>    0x0000000050027934 <+4>:	std     r3,96(r1)
>    0x0000000050027938 <+8>:	std     r4,104(r1)
>    0x000000005002793c <+12>:	std     r5,112(r1)
>    0x0000000050027940 <+16>:	std     r8,136(r1)
>    0x0000000050027944 <+20>:	bl      0x50027950 <._rtld_start+32>
>    0x0000000050027948 <+24>:	.long 0x0
>    0x000000005002794c <+28>:	.long 0x30e40
>    0x0000000050027950 <+32>:	mflr    r3
>    0x0000000050027954 <+36>:	ld      r4,0(r3)
>    0x0000000050027958 <+40>:	add     r3,r4,r3
>    0x000000005002795c <+44>:	ld      r4,-32768(r2)
>    0x0000000050027960 <+48>:	subf    r4,r4,r2
>    0x0000000050027964 <+52>:	bl      0x50027c64 <reloc_non_plt_self>
>    0x0000000050027968 <+56>:	nop
>    0x000000005002796c <+60>:	ld      r4,104(r1)
>    0x0000000050027970 <+64>:	addi    r3,r4,-8
>    0x0000000050027974 <+68>:	addi    r4,r1,128
>    0x0000000050027978 <+72>:	addi    r5,r1,120
>    0x000000005002797c <+76>:	bl      0x50028608 <_rtld>
>    0x0000000050027980 <+80>:	nop
>    0x0000000050027984 <+84>:	ld      r2,8(r3)
>    0x0000000050027988 <+88>:	ld      r11,16(r3)
>    0x000000005002798c <+92>:	ld      r3,0(r3)
>    0x0000000050027990 <+96>:	mtlr    r3
>    0x0000000050027994 <+100>:	ld      r3,96(r1)
>    0x0000000050027998 <+104>:	ld      r4,104(r1)
>    0x000000005002799c <+108>:	ld      r5,112(r1)
>    0x00000000500279a0 <+112>:	ld      r6,120(r1)
>    0x00000000500279a4 <+116>:	ld      r7,128(r1)
>    0x00000000500279a8 <+120>:	ld      r8,136(r1)
>    0x00000000500279ac <+124>:	blrl
> => 0x00000000500279b0 <+128>:	li      r0,1
>    0x00000000500279b4 <+132>:	sc      
>    0x00000000500279b8 <+136>:	nop
>    0x00000000500279bc <+140>:	nop
> End of assembler dump.
> 
> So setting a breakpoint at 0x00000000500279ac and
> trying again:
> 
> (gdb) run
> Starting program: /root/c_tests/a.out 
> 
> Breakpoint 3, ._rtld_start () at /usr/src/libexec/rtld-elf/powerpc64/rtld_start.S:104
> 104		blrl	/* _start(argc, argv, envp, obj, cleanup, ps_strings) */
> (gdb) info registers
> r0             0x50027980	1342339456
> r1             0xffffffffffffdaf0	18446744073709542128
> r2             0x10028138	268599608
> r3             0x1	1
> r4             0xffffffffffffdbb8	18446744073709542328
> r5             0xffffffffffffdbc8	18446744073709542344
> r6             0x5004c000	1342488576
> r7             0x50058b30	1342540592
> r8             0x0	0
> r9             0x0	0
> r10            0x0	0
> r11            0x0	0
> r12            0x20000000	536870912
> r13            0x50057010	1342533648
> r14            0x0	0
> r15            0x0	0
> r16            0x0	0
> r17            0x0	0
> r18            0x0	0
> r19            0x0	0
> r20            0x0	0
> r21            0x0	0
> r22            0x0	0
> r23            0x0	0
> r24            0x0	0
> r25            0x0	0
> r26            0x0	0
> r27            0x0	0
> r28            0x0	0
> r29            0x0	0
> r30            0x0	0
> r31            0x0	0
> pc             0x500279ac	0x500279ac <._rtld_start+124>
> msr            <unavailable>
> cr             0x22000c00	570428416
> lr             0x10010000	0x10010000
> ctr            0x50043a80	1342454400
> xer            0x20000000	536870912
> (gdb) stepi
> 0x0000000010010000 in ?? ()
> 
> and that is effectively at ._start .
> 
> NOTE: There is no ._start name in the disassembly
> listed by objdump.
> 
> By contrast for -fuse-ld=bfd building a.out objdump shows:
> 
> 0000000010000438 <._start> mflr    r0
> 000000001000043c <._start+0x4> mfcr    r12
> 0000000010000440 <._start+0x8> std     r31,-8(r1)
> 0000000010000444 <._start+0xc> std     r0,16(r1)
> 0000000010000448 <._start+0x10> stw     r12,8(r1)
> 000000001000044c <._start+0x14> stdu    r1,-176(r1)
> . . .
> 
> 
> In gdb for ld.lld used:
> 
> Reading symbols from a.out...done.
> (gdb) br *0x00000000500279ac
> Breakpoint 1 at 0x500279ac
> (gdb) run
> Starting program: /root/c_tests/a.out 
> 
> Breakpoint 1, ._rtld_start () at /usr/src/libexec/rtld-elf/powerpc64/rtld_start.S:104
> 104		blrl	/* _start(argc, argv, envp, obj, cleanup, ps_strings) */
> (gdb) stepi
> 0x0000000010010000 in ?? ()
> (gdb) 
> 0x0000000010010004 in ?? ()
> (gdb) display/i $pc
> 1: x/i $pc
> => 0x10010004:	mfcr    r12
> (gdb) stepi
> 0x0000000010010008 in ?? ()
> 1: x/i $pc
> => 0x10010008:	std     r31,-8(r1)
> (gdb) 
> 0x000000001001000c in ?? ()
> 1: x/i $pc
> => 0x1001000c:	std     r0,16(r1)
> 
> . . .
> 
> (gdb) 
> 0x00000000100100a0 in ?? ()
> 1: x/i $pc
> => 0x100100a0:	beq     0x100100ac
> (gdb) 
> 0x00000000100100ac in ?? ()
> 1: x/i $pc
> => 0x100100ac:	cmpldi  r8,0
> (gdb) 
> 0x00000000100100b0 in ?? ()
> 1: x/i $pc
> => 0x100100b0:	beq     0x100100c0
> (gdb) 
> 0x00000000100100c0 in ?? ()
> 1: x/i $pc
> => 0x100100c0:	addis   r3,r2,0
> (gdb) 
> 0x00000000100100c4 in ?? ()
> 1: x/i $pc
> => 0x100100c4:	ld      r3,32552(r3)
> (gdb) 
> 0x00000000100100c8 in ?? ()
> 1: x/i $pc
> => 0x100100c8:	cmpldi  r3,0
> (gdb) 
> 0x00000000100100cc in ?? ()
> 1: x/i $pc
> => 0x100100cc:	beq     0x100100e0
> (gdb) 
> 0x00000000100100d0 in ?? ()
> 1: x/i $pc
> => 0x100100d0:	mr      r3,r7
> (gdb) 
> 0x00000000100100d4 in ?? ()
> 1: x/i $pc
> => 0x100100d4:	bl      0x10010560
> 
> Note: Below is from plt :
> 
> Disassembly of section .plt:
> 0000000010010560 <.plt> std     r2,40(r1)
> 0000000010010564 <.plt+0x4> addis   r11,r2,0
> 0000000010010568 <.plt+0x8> ld      r12,32512(r11)
> 000000001001056c <.plt+0xc> ld      r11,0(r12) <<<<<===== Fails here.
> 0000000010010570 <.plt+0x10> mtctr   r11
> 0000000010010574 <.plt+0x14> ld      r2,8(r12)
> 0000000010010578 <.plt+0x18> ld      r11,16(r12)
> 000000001001057c <.plt+0x1c> bctr
> 
> (By setting breakpoints in the 3 such .plt code blocks:
> this is the first .plt code block executed and it fails.)
> 
> The .plt is different from what ld.bfd generates:
> no __glink_PLTresolve or its use and the code does
> not appear strictly equivalent to me.
> 
> Back to gdb based information:
> 
> (gdb) info registers
> r0             0x500279b0	1342339504
> r1             0xffffffffffffda40	18446744073709541952
> r2             0x10028138	268599608
> r3             0x50058b30	1342540592
> r4             0x0	0
> r5             0xffffffffffffdbc8	18446744073709542344
> r6             0x5004c000	1342488576
> r7             0x50058b30	1342540592
> r8             0x0	0
> r9             0x0	0
> r10            0x0	0
> r11            0x0	0
> r12            0x22000c00	570428416
> r13            0x50057010	1342533648
> r14            0x0	0
> r15            0x0	0
> r16            0x0	0
> r17            0x0	0
> r18            0x0	0
> r19            0x0	0
> r20            0x0	0
> r21            0x0	0
> r22            0x0	0
> r23            0x0	0
> r24            0x0	0
> r25            0x10028138	268599608
> r26            0x0	0
> r27            0x0	0
> r28            0x1	1
> r29            0xffffffffffffdbb8	18446744073709542328
> r30            0xffffffffffffdbc8	18446744073709542344
> r31            0xffffffffffffda40	18446744073709541952
> pc             0x10010560	0x10010560
> msr            <unavailable>
> cr             0x42000c00	1107299328
> lr             0x100100d8	0x100100d8
> ctr            0x50043a80	1342454400
> xer            0x20000000	536870912
> 
> (gdb) 
> 0x0000000010010560 in ?? ()
> 1: x/i $pc
> => 0x10010560:	std     r2,40(r1)
> (gdb) 
> 0x0000000010010564 in ?? ()
> 1: x/i $pc
> => 0x10010564:	addis   r11,r2,0
> (gdb) 
> 0x0000000010010568 in ?? ()
> 1: x/i $pc
> => 0x10010568:	ld      r12,32512(r11)
> (gdb) 
> 0x000000001001056c in ?? ()
> 1: x/i $pc
> => 0x1001056c:	ld      r11,0(r12)
> (gdb) 
> 
> Program received signal SIGSEGV, Segmentation fault.
> 0x000000001001056c in ?? ()
> 1: x/i $pc
> => 0x1001056c:	ld      r11,0(r12)
> 
> The source code (from lib/csu/powerpc64/crt1.c ) is:
> 
> void
> _start(int argc, char **argv, char **env,
>     const struct Struct_Obj_Entry *obj __unused, void (*cleanup)(void),
>     struct ps_strings *ps_strings)
> {
> 
>         handle_argv(argc, argv, env);
> 
>         if (ps_strings != (struct ps_strings *)0)
>                 __ps_strings = ps_strings;
> 
>         if (&_DYNAMIC != NULL)
>                 atexit(cleanup);
>         else
>                 _init_tls();
> 
> #ifdef GCRT
>         atexit(_mcleanup);
>         monstartup(&eprol, &etext);
> #endif
> 
>         handle_static_init(argc, argv, env);
>         exit(main(argc, argv, env));
> }
> 
> The 3 plt code blocks are for:
> 
> atexit
> _init_tls
> exit
> 
> from what I can tell, possibly not in that order.
> 
> Overall: The plt handling seems to be broken.
> 
> 
> > You can also build rtld with additional debugging by adding -DDEBUG to
> > CFLAGS. In libexec/rtld-elf/Makefile there's an example command line
> > for building it locally, but I've just added CFLAGS+=-DDEBUG to the
> > Makefile in my test tree and built it along with the rest of my full
> > cross build.
> 
> # svnlite diff /usr/src/libexec/rtld-elf/Makefile
> Index: /usr/src/libexec/rtld-elf/Makefile
> ===================================================================
> --- /usr/src/libexec/rtld-elf/Makefile	(revision 311950)
> +++ /usr/src/libexec/rtld-elf/Makefile	(working copy)
> @@ -17,6 +17,7 @@
>  		malloc.c xmalloc.c debug.c libmap.c
>  MAN=		rtld.1
>  CSTD?=		gnu99
> +CFLAGS+=-DDEBUG
>  CFLAGS+=	-Wall -DFREEBSD_ELF -DIN_RTLD -ffreestanding
>  CFLAGS+=	-I${SRCTOP}/lib/csu/common
>  .if exists(${.CURDIR}/${MACHINE_ARCH})
> 
> The above did not seem to make much of a difference for the
> code involved, likely because crt1.c is from
> lib/csu/powerpc64/ instead of from libexec/rtld-elf/ .
> 
> 
> ===
> Mark Millard
> markmi at dsl-only.net



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20170112192223.GA49469>