Bug 12654 - R_386_TLS_LDO_32 relocations aren't handled properly with -pie
Summary: R_386_TLS_LDO_32 relocations aren't handled properly with -pie
Status: RESOLVED FIXED
Alias: None
Product: binutils
Classification: Unclassified
Component: ld (show other bugs)
Version: 2.21
: P2 normal
Target Milestone: ---
Assignee: unassigned
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-04-08 10:20 UTC by Matthias Klose
Modified: 2017-02-28 06:30 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Matthias Klose 2011-04-08 10:20:41 UTC
[ forwarded from https://launchpad.net/bugs/663294 ]

seen with the 2.21 branch and trunk, not seen when using gold (2.21 branch) as the linker. The original issue was seen with a firefox build.  Report from Chris Coulson:

The information that is wrong in the final executable isn't coming from the compiler, but is added by the linker (although the relocations in the .rel.text section are coming from the compiler). So, I did two builds of Firefox again - 1 with gcc-4.5 and 1 with gcc-4.4 to compare the jemalloc.o objects. There is one obvious difference - gcc-4.4 outputs an object using global-dynamic TLS (I see lots of R_386_TLS_GD relocations .rel.text where arenas_map is accessed), and gcc-4.5 seems to be outputting an object with local-dynamic TLS access model (I see lots of R_386_TLS_LDM and R_386_TLS_LDO_32 relocations).

So, I've managed to write a small reproducer now:

#include <stdlib.h>
#include <stdio.h>

static __thread int a;
static int *c;

int main(int argc, char *argv[])
{
 a = 2;
 c = &a;
 printf("c=%d\n", *c);
}

You can save this as test.c and compile in various ways:

- "gcc -o test -fPIC test.c" - This generates an intermediate object with global-dynamic TLS (you can verify this if you split the compile/link steps and inspect the resulting object with readelf) which is passed to the linker, and works properly

- "gcc -o test -fPIC -pie test.c" - This generates a pie which works correctly

- "gcc -o test -ftls-model=local-dynamic -fPIC test.c" - This generates an intermediate object with local-dynamic TLS (again, verified by splitting the compile/link steps and inspecting the object with readelf), and the linker generates a working binary

- "gcc -o test -ftls-model=local-dynamic -fPIC -pie test.c" - The resulting binary crashes, and inspecting the resulting binary shows the same issue we have with Firefox (it's accessing the wrong location where it should be accessing the TLS variable)

Disassembling main from the broken binary shows:

000005ac <main>:
 5ac: 55 push %ebp
 5ad: 89 e5 mov %esp,%ebp
 5af: 83 e4 f0 and $0xfffffff0,%esp
 5b2: 56 push %esi
 5b3: 53 push %ebx
 5b4: 83 ec 18 sub $0x18,%esp
 5b7: e8 eb ff ff ff call 5a7 <__i686.get_pc_thunk.bx>
 5bc: 81 c3 38 1a 00 00 add $0x1a38,%ebx
 5c2: 65 a1 00 00 00 00 mov %gs:0x0,%eax
 5c8: 90 nop
 5c9: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
 5cd: 89 c6 mov %eax,%esi
 5cf: 8d 05 00 00 00 00 lea 0x0,%eax
 5d5: 8d 04 06 lea (%esi,%eax,1),%eax
 5d8: c7 00 02 00 00 00 movl $0x2,(%eax)
 5de: 65 a1 00 00 00 00 mov %gs:0x0,%eax
 5e4: 90 nop
 5e5: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
 5e9: 89 c6 mov %eax,%esi
 5eb: 8d 05 00 00 00 00 lea 0x0,%eax
 5f1: 8d 04 06 lea (%esi,%eax,1),%eax
 5f4: 89 83 30 00 00 00 mov %eax,0x30(%ebx)
 5fa: 8b 83 30 00 00 00 mov 0x30(%ebx),%eax
 600: 8b 10 mov (%eax),%edx
 602: 8d 83 f8 e6 ff ff lea -0x1908(%ebx),%eax
 608: 89 54 24 04 mov %edx,0x4(%esp)
 60c: 89 04 24 mov %eax,(%esp)
 60f: e8 70 fe ff ff call 484 <printf@plt>
 614: 83 c4 18 add $0x18,%esp
 617: 5b pop %ebx
 618: 5e pop %esi
 619: 89 ec mov %ebp,%esp
 61b: 5d pop %ebp
 61c: c3 ret
 61d: 90 nop
 61e: 90 nop
 61f: 90 nop

And from the working binary:

08048454 <main>:
 8048454: 55 push %ebp
 8048455: 89 e5 mov %esp,%ebp
 8048457: 83 e4 f0 and $0xfffffff0,%esp
 804845a: 56 push %esi
 804845b: 53 push %ebx
 804845c: 83 ec 18 sub $0x18,%esp
 804845f: e8 61 00 00 00 call 80484c5 <__i686.get_pc_thunk.bx>
 8048464: 81 c3 90 1b 00 00 add $0x1b90,%ebx
 804846a: 65 a1 00 00 00 00 mov %gs:0x0,%eax
 8048470: 90 nop
 8048471: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
 8048475: 89 c6 mov %eax,%esi
 8048477: 8d 05 fc ff ff ff lea 0xfffffffc,%eax
 804847d: 8d 04 06 lea (%esi,%eax,1),%eax
 8048480: c7 00 02 00 00 00 movl $0x2,(%eax)
 8048486: 65 a1 00 00 00 00 mov %gs:0x0,%eax
 804848c: 90 nop
 804848d: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
 8048491: 89 c6 mov %eax,%esi
 8048493: 8d 05 fc ff ff ff lea 0xfffffffc,%eax
 8048499: 8d 04 06 lea (%esi,%eax,1),%eax
 804849c: 89 83 2c 00 00 00 mov %eax,0x2c(%ebx)
 80484a2: 8b 83 2c 00 00 00 mov 0x2c(%ebx),%eax
 80484a8: 8b 10 mov (%eax),%edx
 80484aa: 8d 83 9c e5 ff ff lea -0x1a64(%ebx),%eax
 80484b0: 89 54 24 04 mov %edx,0x4(%esp)
 80484b4: 89 04 24 mov %eax,(%esp)
 80484b7: e8 c8 fe ff ff call 8048384 <printf@plt>
 80484bc: 83 c4 18 add $0x18,%esp
 80484bf: 5b pop %ebx
 80484c0: 5e pop %esi
 80484c1: 89 ec mov %ebp,%esp
 80484c3: 5d pop %ebp
 80484c4: c3 ret

Note, the broken (pie) binary shows:

 5eb: 8d 05 00 00 00 00 lea 0x0,%eax

...where, the working (non pie) binary shows:

 8048493: 8d 05 fc ff ff ff lea 0xfffffffc,%eax

Note, I just hacked the intermediate test.o object and inserted 4 bytes of garbage from offset 0x25 from the start of the .text section (which is where the linker applies a R_386_TLS_LDO_32 relocation, and seems to be the one which isn't applied correctly). Then I ran the linker on it, and confirmed that the relocation is actually applied incorrectly (rather than not being applied at all), as the garbage i inserted in to the intermediate object was overwritten.

So, it just seems to be that R_386_TLS_LDO_32 relocations aren't handled properly with -pie
Comment 1 Ian Lance Taylor 2011-04-08 13:19:47 UTC
The code in elf_i386_relocate_section for R_386_TLS_LDO_32 checks info->shared where it should probably check !info->executable.
Comment 2 H.J. Lu 2011-04-08 14:13:23 UTC
(In reply to comment #1)
> The code in elf_i386_relocate_section for R_386_TLS_LDO_32 checks info->shared
> where it should probably check !info->executable.

Thanks, Ian.  I will check in a fix soon.
Comment 3 Sourceware Commits 2011-04-08 16:14:53 UTC
CVSROOT:	/cvs/src
Module name:	src
Changes by:	hjl@sourceware.org	2011-04-08 16:14:49

Modified files:
	bfd            : ChangeLog elf32-i386.c 
	ld/testsuite   : ChangeLog 
	ld/testsuite/ld-i386: i386.exp 
Added files:
	ld/testsuite/ld-i386: tlspie2.d tlspie2.s 

Log message:
	Properly handle R_386_TLS_LDO_32 for PIE.
	
	bfd/
	
	2011-04-08  H.J. Lu  <hongjiu.lu@intel.com>
	
	PR ld/12654
	* elf32-i386.c (elf_i386_relocate_section): Check !executable
	instead of shared for R_386_TLS_LDO_32.
	
	ld/testsuite/
	
	2011-04-08  H.J. Lu  <hongjiu.lu@intel.com>
	
	PR ld/12654
	* ld-i386/i386.exp: Run tlspie2.
	
	* ld-i386/tlspie2.d: New.
	* ld-i386/tlspie2.s: Likewise.

Patches:
http://sourceware.org/cgi-bin/cvsweb.cgi/src/bfd/ChangeLog.diff?cvsroot=src&r1=1.5293&r2=1.5294
http://sourceware.org/cgi-bin/cvsweb.cgi/src/bfd/elf32-i386.c.diff?cvsroot=src&r1=1.243&r2=1.244
http://sourceware.org/cgi-bin/cvsweb.cgi/src/ld/testsuite/ChangeLog.diff?cvsroot=src&r1=1.1382&r2=1.1383
http://sourceware.org/cgi-bin/cvsweb.cgi/src/ld/testsuite/ld-i386/tlspie2.d.diff?cvsroot=src&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/src/ld/testsuite/ld-i386/tlspie2.s.diff?cvsroot=src&r1=NONE&r2=1.1
http://sourceware.org/cgi-bin/cvsweb.cgi/src/ld/testsuite/ld-i386/i386.exp.diff?cvsroot=src&r1=1.36&r2=1.37
Comment 4 H.J. Lu 2011-04-08 16:16:26 UTC
Fixed.
Comment 5 Chris Coulson 2011-04-08 17:29:50 UTC
Thanks! That does the trick, and I have a working -pie build of firefox now too