Bug 12928 - Wrong linker relaxation with TLSLDM relocation on alpha
Summary: Wrong linker relaxation with TLSLDM relocation on alpha
Status: RESOLVED FIXED
Alias: None
Product: binutils
Classification: Unclassified
Component: ld (show other bugs)
Version: 2.21
: P2 normal
Target Milestone: ---
Assignee: Richard Henderson
URL:
Keywords:
: 12608 (view as bug list)
Depends on:
Blocks:
 
Reported: 2011-06-24 07:53 UTC by Uros Bizjak
Modified: 2011-06-24 18:36 UTC (History)
2 users (show)

See Also:
Host:
Target: alphaev68-pc-linux-gnu
Build:
Last reconfirmed:


Attachments
Assembly code that fails with enabled linker relaxations (758 bytes, application/octet-stream)
2011-06-24 07:53 UTC, Uros Bizjak
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Uros Bizjak 2011-06-24 07:53:55 UTC
Created attachment 5818 [details]
Assembly code that fails with enabled linker relaxations

This is a followup from gcc PR 49515 [1].

Attached testcase fails with linker relaxations on alphaev68-pc-linux-gnu:

uros@monolith ~ $ gcc thr-init-2.s 
uros@monolith ~ $ ./a.out
a=2 fstat=33554434
Aborted
uros@monolith ~ $ gcc -Wl,--no-relax thr-init-2.s 
uros@monolith ~ $ ./a.out
uros@monolith ~ $ 

ld --version
GNU ld (GNU Binutils) 2.21
Copyright 2010 Free Software Foundation, Inc.
...

But the bug also exists in latest snapshot:
GNU ld (GNU Binutils) 2.21.52.20110623

The c source:

--cut here--
/* { dg-do run } */
/* { dg-require-effective-target tls_runtime } */
/* { dg-add-options tls } */

extern int printf (char *,...);
extern void abort() ;

static __thread int fstat ;
static __thread int fstat = 1;
static __thread int fstat ;

int test_code(int b)
{
  fstat += b ;
  return fstat;
}

int main (int ac, char *av[])
{
  int a = test_code(1);
  
  if ( a != 2 || fstat != 2 )
    {
    printf ("a=%d fstat=%d\n", a, fstat) ;
    abort ();
    }
  
  return 0;
}
--cut here--

compiled with -O2 -fpic to produce attached assembly code.

[1] http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49515
Comment 1 Uros Bizjak 2011-06-24 07:59:10 UTC
The difference to working code (test_code function) from gcc-4.4.5 is:

@@ -26,17 +26,18 @@
     ldah $1,fstat($0)        !dtprelhi
     ldq $26,0($30)
     lda $29,0($29)        !gpdisp!3
-    ldl $0,fstat($1)        !dtprello
-    addl $0,$9,$0
+    ldl $16,fstat($1)        !dtprello
+    addl $16,$9,$16
     ldq $9,8($30)
-    stl $0,fstat($1)        !dtprello
+    addl $31,$16,$0
+    stl $16,fstat($1)        !dtprello
     lda $30,16($30)
     ret $31,($26),1

Please note that $0 is used instead (extra addl is just a move from $16 to $0).
Comment 2 Uros Bizjak 2011-06-24 09:09:31 UTC
I think that I got the problem, but the wrong relaxation is in main.

in an object, we have following part of test_code:

  10:	00 00 5e b7 	stq	ra,0(sp)
  14:	08 00 3e b5 	stq	s0,8(sp)
  18:	09 04 f0 47 	mov	a0,s0
  1c:	00 00 1d 22 	lda	a0,0(gp)
			1c: TLSLDM	fstat
  20:	00 40 5b 6b 	jsr	ra,(t12),24 <test_code+0x24>
			20: LITUSE	.text+0x5
			20: HINT	__tls_get_addr
  24:	00 00 ba 27 	ldah	gp,0(ra)
			24: GPDISP	.text+0xc
  28:	00 00 20 24 	ldah	t0,0(v0)
			28: DTPRELHI	fstat
  2c:	00 00 5e a7 	ldq	ra,0(sp)

this links in the final link to:

   1200007b0:	00 00 5e b7 	stq	ra,0(sp)
   1200007b4:	08 00 3e b5 	stq	s0,8(sp)
   1200007b8:	09 04 f0 47 	mov	a0,s0
=> 1200007bc:	10 00 1f 22 	lda	a0,16
   1200007c0:	9e 00 00 00 	rduniq
-> 1200007c4:	00 04 00 42 	addq	a0,v0,v0
   1200007c8:	00 00 20 24 	ldah	t0,0(v0)
   1200007cc:	00 00 5e a7 	ldq	ra,0(sp)

Please note the load of constant 16 (0x10) to a0. a0 is later added to v0.

However, in main, we have:

  1c:	00 40 5b 6b 	jsr	ra,(t12),20 <main+0x20>
			1c: LITUSE	.text.startup+0x3
			1c: HINT	test_code
  20:	00 00 ba 27 	ldah	gp,0(ra)
			20: GPDISP	.text.startup+0x8
  24:	a1 55 00 40 	cmpeq	v0,0x2,t0
  28:	00 00 bd 23 	lda	gp,0(gp)
  2c:	09 04 e0 47 	mov	v0,s0
  30:	0e 00 20 e4 	beq	t0,6c <main+0x6c>
  34:	00 00 7d a7 	ldq	t12,0(gp)
			34: ELF_LITERAL	__tls_get_addr
  38:	00 00 1d 22 	lda	a0,0(gp)
			38: TLSLDM	fstat
  3c:	00 40 5b 6b 	jsr	ra,(t12),40 <main+0x40>
			3c: LITUSE	.text.startup+0x5
			3c: HINT	__tls_get_addr
  40:	00 00 ba 27 	ldah	gp,0(ra)
			40: GPDISP	.text.startup+0x8
  44:	00 00 00 24 	ldah	v0,0(v0)
			44: DTPRELHI	fstat
  48:	00 00 bd 23 	lda	gp,0(gp)
  4c:	00 00 20 a0 	ldl	t0,0(v0)
			4c: DTPRELLO	fstat

this links in the final link to:

   1200005dc:	72 00 40 d3 	bsr	ra,1200007a8 <test_code+0x8>
   1200005e0:	00 00 fe 2f 	unop	
   1200005e4:	a1 55 00 40 	cmpeq	v0,0x2,t0
   1200005e8:	00 00 fe 2f 	unop	
   1200005ec:	09 04 e0 47 	mov	v0,s0
   1200005f0:	0e 00 20 e4 	beq	t0,12000062c <main+0x6c>
=> 1200005f4:	10 00 7f 23 	lda	t12,16
   1200005f8:	00 00 fe 2f 	unop	
   1200005fc:	9e 00 00 00 	rduniq
-> 120000600:	00 04 00 42 	addq	a0,v0,v0
   120000604:	00 00 00 24 	ldah	v0,0(v0)
   120000608:	00 00 fe 2f 	unop	
   12000060c:	00 00 20 a0 	ldl	t0,0(v0)

Huh? Instead of loading a0 with 0x10, we load t12 instead, but a couple of insns down the insn stream, we still add a0 to v0.

This also explains what happened when test_code clobbered a0. If the constant value in a0, generated in test_code lived up to addq after the rduniq call in main, everything worked ok, but by pure luck.

The linker should eventually update both instructions.
Comment 3 Richard Henderson 2011-06-24 17:18:59 UTC
Confirmed.
Comment 4 Sourceware Commits 2011-06-24 17:38:20 UTC
CVSROOT:	/cvs/src
Module name:	src
Changes by:	rth@sourceware.org	2011-06-24 17:38:17

Modified files:
	bfd            : ChangeLog elf64-alpha.c 

Log message:
	PR ld/12928
	* elf64-alpha.c (elf64_alpha_relax_tls_get_addr): Recover the
	tlsgd insn before swapping adjacent insns.

Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/src/bfd/ChangeLog.diff?cvsroot=src&r1=1.5401&r2=1.5402
http://sourceware.org/cgi-bin/cvsweb.cgi/src/bfd/elf64-alpha.c.diff?cvsroot=src&r1=1.179&r2=1.180
Comment 5 Richard Henderson 2011-06-24 17:50:31 UTC
Fixed.
Comment 6 Uros Bizjak 2011-06-24 18:36:27 UTC
*** Bug 12608 has been marked as a duplicate of this bug. ***