NetBSD Problem Report #48709

From martin@duskware.de  Fri Apr  4 19:28:35 2014
Return-Path: <martin@duskware.de>
Received: from mail.netbsd.org (mail.netbsd.org [149.20.53.66])
	(using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits))
	(Client CN "mail.netbsd.org", Issuer "Postmaster NetBSD.org" (verified OK))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 95940A5802
	for <gnats-bugs@gnats.NetBSD.org>; Fri,  4 Apr 2014 19:28:35 +0000 (UTC)
From: martin@NetBSD.org
Reply-To: martin@NetBSD.org
To: gnats-bugs@NetBSD.org
Subject: static threaded programs crash
X-Send-Pr-Version: 3.95

>Number:         48709
>Category:       port-alpha
>Synopsis:       static threaded programs crash
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    port-alpha-maintainer
>State:          closed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Apr 04 19:30:00 +0000 2014
>Closed-Date:    Tue Aug 12 12:59:19 +0000 2014
>Last-Modified:  Tue Aug 12 12:59:19 +0000 2014
>Originator:     Martin Husemann
>Release:        NetBSD 6.99.39
>Organization:
The NetBSD Foundation, Inc.
>Environment:
System: NetBSD gemini.duskware.de 6.99.39 NetBSD 6.99.39 (GENERIC-$Revision: 1.358 $) #11: Fri Apr 4 14:06:15 CEST 2014 martin@night-owl.duskware.de:/usr/src/sys/arch/alpha/compile/GENERIC alpha
Architecture: alpha
Machine: alpha
>Description:

The test program for staticaly linked TLS access dies on alpha with a segfault,
try:

	cd /usr/tests/lib/libc/tls && ./t_tls_static -l

but also this minimalistic program dies, if compiled with -static -pthread:

--8<--
#include <stdlib.h>
#include <pthread.h>
void *func(void *arg) { return 0; }
int main(int arg, char **argv)
{
	pthread_t t;
	pthread_create(&t, NULL, func, NULL);
	return 0;
}
-->8--

Nick and I traced this to register t12 getting corrupted in _libc_init when
calling __libc_thr_init. However, it is not that simple: the original object
code (with relocations) is fine:

Disassembly of section .text.startup:

0000000000000000 <_libc_init>:
   0:   00 00 bb 27     ldah    gp,0(t12)
                        0: GPDISP       .text.startup+0x4
   4:   00 00 bd 23     lda     gp,0(gp)
   8:   f0 ff de 23     lda     sp,-16(sp)
   c:   00 00 5e b7     stq     ra,0(sp)
[..]
  64:   00 00 7d a7     ldq     t12,0(gp)
                        64: ELF_LITERAL __guard_setup
  68:   00 40 5b 6b     jsr     ra,(t12),6c <_libc_init+0x6c>
                        68: LITUSE      .text.startup+0x3
                        68: HINT        __guard_setup
  6c:   00 00 ba 27     ldah    gp,0(ra)
                        6c: GPDISP      .text.startup+0x4
  70:   00 00 bd 23     lda     gp,0(gp)
  74:   00 00 7d a7     ldq     t12,0(gp)
                        74: ELF_LITERAL __libc_atomic_init
  78:   00 40 5b 6b     jsr     ra,(t12),7c <_libc_init+0x7c>
                        78: LITUSE      .text.startup+0x3
                        78: HINT        __libc_atomic_init
  7c:   00 00 ba 27     ldah    gp,0(ra)
                        7c: GPDISP      .text.startup+0x4
  80:   00 00 bd 23     lda     gp,0(gp)
  84:   00 00 7d a7     ldq     t12,0(gp)
                        84: ELF_LITERAL __libc_static_tls_setup
  88:   00 40 5b 6b     jsr     ra,(t12),8c <_libc_init+0x8c>
                        88: LITUSE      .text.startup+0x3
                        88: HINT        __libc_static_tls_setup
  8c:   00 00 ba 27     ldah    gp,0(ra)
                        8c: GPDISP      .text.startup+0x4
  90:   00 00 bd 23     lda     gp,0(gp)
  94:   00 00 7d a7     ldq     t12,0(gp)
                        94: ELF_LITERAL __libc_thr_init
  98:   00 40 5b 6b     jsr     ra,(t12),9c <_libc_init+0x9c>
                        98: LITUSE      .text.startup+0x3
                        98: HINT        __libc_thr_init

As you can see, t12 is used for the jsr call and set up right before the
function call. I don't see where it is restored, but I might have 
misunderstood something about the abi.

Now binutils is smart and optimizes this for static linked
(and small) binaries:

   1200467b4:   00 00 fe 2f     unop    
   1200467b8:   43 00 40 d3     bsr     ra,1200468c8 <__guard_setup+0x8>
   1200467bc:   00 00 fe 2f     unop    
   1200467c0:   00 00 fe 2f     unop    
   1200467c4:   00 00 fe 2f     unop    
   1200467c8:   15 00 40 d3     bsr     ra,120046820 <__libc_atomic_init>
   1200467cc:   00 00 fe 2f     unop    
   1200467d0:   00 00 fe 2f     unop    
   1200467d4:   00 00 fe 2f     unop    
   1200467d8:   9b 5a 5f d3     bsr     ra,12001d248 <__libc_static_tls_setup+0x8>
   1200467dc:   00 00 fe 2f     unop    
   1200467e0:   00 00 fe 2f     unop    
   1200467e4:   00 00 fe 2f     unop    
   1200467e8:   b1 f0 5e d3     bsr     ra,120002ab0 <__libc_thr_init>
   1200467ec:   00 00 fe 2f     unop    
   1200467f0:   00 00 fe 2f     unop    
   1200467f4:   00 00 fe 2f     unop    

The pattern is simple: the first two instructions in public functions
set up the gp register for this function. When it is known that the gp value
is not needed, or the same as in the calling function, the gp setup is skipped
and the jsr can be replaced by a bsr - but to past the two instructions (i.e.
typically function address + 8).

If you look at the disassembly above, you'll find some functions where the bsr
does not use the +8 offset: __libc_atomic_init and __libc_thr_init.

For the former, this is fine: it is an empty function and does not do
gp setup:

0000000120046820 <__libc_atomic_init>:
   120046820:   01 80 fa 6b     ret
   120046824:   00 00 fe 2f     unop    
   120046828:   1f 04 ff 47     nop     
   12004682c:   00 00 fe 2f     unop    

However, for __libc_thr_init it is not fine:

0000000120002ab0 <__libc_thr_init>:
   120002ab0:   06 00 bb 27     ldah    gp,6(t12)
   120002ab4:   78 f6 bd 23     lda     gp,-2440(gp)
   120002ab8:   c0 ff de 23     lda     sp,-64(sp)

So the
   1200467e8:   b1 f0 5e d3     bsr     ra,120002ab0 <__libc_thr_init>
actually modifies register t12 - and it is never restored.

I wonder if the "hidden" attribute of the function name is declared 
inconsistently somewhere (__libc_thr_init is aliased for libpthread), or
if this is a binutils bug -- or if I am just missing something.

Does anyone have an idea why binutils things it is ok to convert the
__libc_thr_init call to bsr without an offset?

>How-To-Repeat:
cd /usr/tests/lib/libc/tls
./t_tls_static -l

>Fix:
n/a

>Release-Note:

>Audit-Trail:
From: Martin Husemann <martin@duskware.de>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: port-alpha/48709: static threaded programs crash
Date: Sun, 27 Jul 2014 17:25:22 +0200

 I filed an upstream bug report:

 https://sourceware.org/bugzilla/show_bug.cgi?id=17205

 Martin

From: "Nick Hudson" <skrll@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/48709 CVS commit: src/external/gpl3/binutils/dist/bfd
Date: Mon, 11 Aug 2014 20:53:16 +0000

 Module Name:	src
 Committed By:	skrll
 Date:		Mon Aug 11 20:53:16 UTC 2014

 Modified Files:
 	src/external/gpl3/binutils/dist/bfd: ChangeLog elflink.c

 Log Message:
 Apply change from upstream to fix PR/48709 - port-alpha/48709: static
 threaded programs crash.

 With this fix the new weak symbol's st_other is not merged in, i.e. NOPV
 is not copied from the libc __libc_thr_init.

 * elflink.c (_bfd_elf_merge_symbol): If merging a new weak
 symbol that will be skipped, we don't have a new definition.


 To generate a diff of this commit:
 cvs rdiff -u -r1.5 -r1.6 src/external/gpl3/binutils/dist/bfd/ChangeLog
 cvs rdiff -u -r1.7 -r1.8 src/external/gpl3/binutils/dist/bfd/elflink.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/48709 CVS commit: [netbsd-7] src/external/gpl3/binutils/dist/bfd
Date: Tue, 12 Aug 2014 09:36:03 +0000

 Module Name:	src
 Committed By:	martin
 Date:		Tue Aug 12 09:36:03 UTC 2014

 Modified Files:
 	src/external/gpl3/binutils/dist/bfd [netbsd-7]: ChangeLog elflink.c

 Log Message:
 Pull up following revision(s) (requested by skrll in ticket #5):
 	external/gpl3/binutils/dist/bfd/ChangeLog: revision 1.6
 	external/gpl3/binutils/dist/bfd/elflink.c: revision 1.8
 Apply change from upstream to fix PR/48709 - port-alpha/48709: static
 threaded programs crash.
 With this fix the new weak symbol's st_other is not merged in, i.e. NOPV
 is not copied from the libc __libc_thr_init.
 * elflink.c (_bfd_elf_merge_symbol): If merging a new weak
 symbol that will be skipped, we don't have a new definition.


 To generate a diff of this commit:
 cvs rdiff -u -r1.5 -r1.5.4.1 src/external/gpl3/binutils/dist/bfd/ChangeLog
 cvs rdiff -u -r1.7 -r1.7.4.1 src/external/gpl3/binutils/dist/bfd/elflink.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: open->closed
State-Changed-By: skrll@NetBSD.org
State-Changed-When: Tue, 12 Aug 2014 12:59:19 +0000
State-Changed-Why:
Fixed.


>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2014 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.