Bug 20244 - ld fails to handle "op $imm, bar@GOT"
Summary: ld fails to handle "op $imm, bar@GOT"
Status: RESOLVED FIXED
Alias: None
Product: binutils
Classification: Unclassified
Component: ld (show other bugs)
Version: 2.26
: P2 normal
Target Milestone: 2.27
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks: 21168
  Show dependency treegraph
 
Reported: 2016-06-12 03:22 UTC by H.J. Lu
Modified: 2017-02-17 15:45 UTC (History)
1 user (show)

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


Attachments
Disassembly + relocation section of lxecg2e.o (6.73 KB, text/plain)
2017-02-15 16:27 UTC, Dopıng
Details

Note You need to log in before you can comment on or make changes to this bug.
Description H.J. Lu 2016-06-12 03:22:15 UTC
[hjl@gnu-tools-1 got-4]$ cat x.S
	.data
	.type	bar, @object
bar:
	.byte	1
	.size	bar, .-bar
	.globl	foo
	.type	foo, @object
foo:
	.byte	1
	.size	foo, .-foo
	.text
	.globl	_start
	.type	_start, @function
_start:
	movl	$0, bar@GOT
	movl	$0, foo@GOT
[hjl@gnu-tools-1 got-4]$ make LD=ld
gcc -B./ -m32 -fno-lto    -c -o x.o x.S
ld -m elf_i386 -o x x.o
./objdump -dwr x

x:     file format elf32-i386


Disassembly of section .text:

08048074 <_start>:
 8048074:	c7 05 f8 ff ff ff 00 00 00 00 	movl   $0x0,0xfffffff8
 804807e:	c7 05 fc ff ff ff 00 00 00 00 	movl   $0x0,0xfffffffc
[hjl@gnu-tools-1 got-4]$ 

bar@GOT should be the memory address, not the offset from GOT base.
Comment 1 cvs-commit@gcc.gnu.org 2016-06-12 04:25:07 UTC
The master branch has been updated by H.J. Lu <hjl@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=74d7f0aa5b1e27da215349fb32337e1d83aca7d7

commit 74d7f0aa5b1e27da215349fb32337e1d83aca7d7
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Sat Jun 11 20:44:24 2016 -0700

    Subtract GOT base only with a base register
    
    When relocating R_386_GOT32 in "op $0, bar@GOT", we shouldn't subtract
    GOT base without a base register and we should disallow it without a
    base register for PIC.
    
    bfd/
    
    	PR ld/20244
    	* elf32-i386.c (elf_i386_relocate_section): When relocating
    	R_386_GOT32, return error without a base register for PIC and
    	subtract the .got.plt section address only with a base register.
    
    ld/
    
    	PR ld/20244
    	* testsuite/ld-i386/i386.exp: Run pr20244-1a and pr20244-1b.
    	* testsuite/ld-i386/pr20244-1.s: New file.
    	* testsuite/ld-i386/pr20244-1a.d: Likewise.
    	* testsuite/ld-i386/pr20244-1b.d: Likewise.
    	* testsuite/ld-i386/pr20244-1c.d: Likewise.
Comment 2 H.J. Lu 2016-06-12 04:25:55 UTC
Fixed for trunk so far.
Comment 3 cvs-commit@gcc.gnu.org 2016-06-13 18:12:40 UTC
The master branch has been updated by H.J. Lu <hjl@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=712ec27916b5604d29d928dec060fd1ba0fd9edb

commit 712ec27916b5604d29d928dec060fd1ba0fd9edb
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Mon Jun 13 11:06:10 2016 -0700

    Add the GOT base for GOT32 relocs against IFUNC
    
    Add the GOT base for R_386_GOT32/R_386_GOT32X relocations against IFUNC
    symbols if there is no base register and disallow them for PIC.
    
    bfd/
    
    	PR ld/20244
    	* elf32-i386.c (elf_i386_relocate_section): Add the .got.plt
    	section address for R_386_GOT32/R_386_GOT32X relocations against
    	IFUNC symbols if there is no base register and return error for
    	PIC.
    
    ld/
    
    	PR ld/20244
    	* testsuite/ld-i386/i386.exp: Run pr20244-2a, pr20244-2b,
    	pr20244-2c and pr20244-2d.
    	* testsuite/ld-i386/no-plt.exp: Run pr20244-3a and pr20244-3b.
    	* testsuite/ld-i386/pr20244-2.s: New file.
    	* testsuite/ld-i386/pr20244-2a.d: Likewise.
    	* testsuite/ld-i386/pr20244-2b.d: Likewise.
    	* testsuite/ld-i386/pr20244-2c.d: Likewise.
    	* testsuite/ld-i386/pr20244-2d.d: Likewise.
    	* testsuite/ld-i386/pr20244-3a.c: Likewise.
    	* testsuite/ld-i386/pr20244-3b.S: Likewise.
    	* testsuite/ld-i386/pr20244-3c.S: Likewise.
    	* testsuite/ld-i386/pr20244-3d.S: Likewise.
Comment 4 cvs-commit@gcc.gnu.org 2016-06-14 22:53:14 UTC
The binutils-2_26-branch branch has been updated by H.J. Lu <hjl@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=9983f9d2d44dc6380671006f1d1cdea00587ccd6

commit 9983f9d2d44dc6380671006f1d1cdea00587ccd6
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Sat Jun 11 20:44:24 2016 -0700

    Subtract GOT base only with a base register
    
    When relocating R_386_GOT32 in "op $0, bar@GOT", we shouldn't subtract
    GOT base without a base register and we should disallow it without a
    base register for PIC.
    
    bfd/
    
    	PR ld/20244
    	* elf32-i386.c (elf_i386_relocate_section): When relocating
    	R_386_GOT32, return error without a base register for PIC and
    	subtract the .got.plt section address only with a base register.
    
    ld/
    
    	PR ld/20244
    	* testsuite/ld-i386/i386.exp: Run pr20244-1a and pr20244-1b.
    	* testsuite/ld-i386/pr20244-1.s: New file.
    	* testsuite/ld-i386/pr20244-1a.d: Likewise.
    	* testsuite/ld-i386/pr20244-1b.d: Likewise.
    	* testsuite/ld-i386/pr20244-1c.d: Likewise.
Comment 5 cvs-commit@gcc.gnu.org 2016-06-14 23:02:16 UTC
The binutils-2_26-branch branch has been updated by H.J. Lu <hjl@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=3cac3e2e8b3c2d70467ac14c07f78bf3088c2e42

commit 3cac3e2e8b3c2d70467ac14c07f78bf3088c2e42
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Mon Jun 13 09:27:12 2016 -0700

    Add the GOT base for GOT32 relocs against IFUNC
    
    Add the GOT base for R_386_GOT32/R_386_GOT32X relocations against IFUNC
    symbols if there is no base register and disallow them for PIC.
    
    bfd/
    
    	PR ld/20244
    	* elf32-i386.c (elf_i386_relocate_section): Add the .got.plt
    	section address for R_386_GOT32/R_386_GOT32X relocations against
    	IFUNC symbols if there is no base register and return error for
    	PIC.
    
    	* elf32-i386.c (elf_i386_relocate_section): Simplify IFUNC
    	GOT32 adjustment for static executables.
    
    ld/
    
    	PR ld/20244
    	* testsuite/ld-i386/i386.exp: Run pr20244-2a, pr20244-2b,
    	pr20244-2c and pr20244-2d.
    	* testsuite/ld-i386/no-plt.exp: Run pr20244-3a and pr20244-3b.
    	* testsuite/ld-i386/pr20244-2.s: New file.
    	* testsuite/ld-i386/pr20244-2a.d: Likewise.
    	* testsuite/ld-i386/pr20244-2b.d: Likewise.
    	* testsuite/ld-i386/pr20244-2c.d: Likewise.
    	* testsuite/ld-i386/pr20244-2d.d: Likewise.
    	* testsuite/ld-i386/pr20244-3a.c: Likewise.
    	* testsuite/ld-i386/pr20244-3b.S: Likewise.
    	* testsuite/ld-i386/pr20244-3c.S: Likewise.
    	* testsuite/ld-i386/pr20244-3d.S: Likewise.
    
    Add 2 i386 tests to call IFUNC functions via GOT
    
    ld/
    
    	* testsuite/ld-i386/i386.exp: Run ifunc-1a and ifunc-1b.
    	* testsuite/ld-i386/ifunc-1a.c: New file.
    	* testsuite/ld-i386/ifunc-1b.S: Likewise.
    	* testsuite/ld-i386/ifunc-1c.S: Likewise.
    	* testsuite/ld-i386/ifunc-1d.S: Likewise.
Comment 6 H.J. Lu 2016-06-14 23:04:32 UTC
Fixed for 2.26 and 2.27.
Comment 7 Dopıng 2017-02-14 15:50:04 UTC
Hi.

It looks like this change breaks the Oracle 12.1 client installation. While trying to install the 32-bit Oracle client on a 64-bit SUSE SLES 12 SP2 machine, we got

[...]/ld: [...]/product/121/lib/libnls12.a(lxecg2e.o): direct GOT relocation R_386_GOT32 against `lxecerr' without base register can not be used when making a shared object

After replacing GNU ld version "GNU ld (GNU Binutils; SUSE Linux Enterprise 12) 2.26.1" with the "GNU ld (GNU Binutils; SUSE Linux Enterprise 11) 2.21.1" version from an older SLES machine, the Oracle client installation worked fine.

Any idea what the heck Oracle is doing wrong in creating the object files for archives like libnls12.a? (These archives have file modification dates from 2014, so they’re not build during installation.)

Kind Regards,
Dopıng
Comment 8 H.J. Lu 2017-02-14 19:19:40 UTC
(In reply to Dopıng from comment #7)
> Hi.
> 
> It looks like this change breaks the Oracle 12.1 client installation. While
> trying to install the 32-bit Oracle client on a 64-bit SUSE SLES 12 SP2
> machine, we got
> 
> [...]/ld: [...]/product/121/lib/libnls12.a(lxecg2e.o): direct GOT relocation
> R_386_GOT32 against `lxecerr' without base register can not be used when
> making a shared object
> 
> After replacing GNU ld version "GNU ld (GNU Binutils; SUSE Linux Enterprise
> 12) 2.26.1" with the "GNU ld (GNU Binutils; SUSE Linux Enterprise 11)
> 2.21.1" version from an older SLES machine, the Oracle client installation
> worked fine.
> 
> Any idea what the heck Oracle is doing wrong in creating the object files
> for archives like libnls12.a? (These archives have file modification dates
> from 2014, so they’re not build during installation.)

lxecg2e.o contains something like

[hjl@gnu-17 tmp]$ cat x.s
	.globl	bar
	.type	bar, @function
bar:
	movl	foo@GOT, %ecx
	movl	(%ecx), %eax
	ret
[hjl@gnu-17 tmp]$ /usr/bin/as --32 -o x.o x.s
[hjl@gnu-17 tmp]$ ld -shared -m elf_i386 x.o
ld: x.o: direct GOT relocation R_386_GOT32 against `foo' without base register can not be used when making a shared object
ld: final link failed: Bad value
[hjl@gnu-17 tmp]$

The problem is that since linker doesn't know where the GOT base is at run-time,
it can't properly resolve R_386_GOT32.
Comment 9 Dopıng 2017-02-14 20:00:36 UTC
(In reply to H.J. Lu from comment #8)
> [...]
> The problem is that since linker doesn't know where the GOT base is at run-time,
> it can't properly resolve R_386_GOT32.

Unfortunately, my knowledge of binary file internals like the global offset table is rather limited. I know that stuff like compiling without -fpic and then trying to build a shared library is not the best of ideas, but I’ve never had any GOT-related problems before.

Do you have any idea what compiler option Oracle might have used incorrectly that caused this code?

What I don’t get is why the older GNU ld seems to link these objects files into a shared library just fine. If that’s the case, the older ld must have a way to properly deal with these assembly instructions, right?
Comment 10 H.J. Lu 2017-02-14 22:00:04 UTC
(In reply to Dopıng from comment #9)
> (In reply to H.J. Lu from comment #8)
> > [...]
> > The problem is that since linker doesn't know where the GOT base is at run-time,
> > it can't properly resolve R_386_GOT32.
> 
> Unfortunately, my knowledge of binary file internals like the global offset
> table is rather limited. I know that stuff like compiling without -fpic and
> then trying to build a shared library is not the best of ideas, but I’ve
> never had any GOT-related problems before.

Without -fpic is OK.  The problem is GOT reference without the base register.
You can use "objdump -dwr" to see what the code is doing.

> Do you have any idea what compiler option Oracle might have used incorrectly
> that caused this code?

I have no idea.

> What I don’t get is why the older GNU ld seems to link these objects files
> into a shared library just fine. If that’s the case, the older ld must have
> a way to properly deal with these assembly instructions, right?

The old linker will silently generate broken output.
Comment 11 Dopıng 2017-02-14 22:30:23 UTC
(In reply to H.J. Lu from comment #10)
> > What I don’t get is why the older GNU ld seems to link these objects files
> > into a shared library just fine. If that’s the case, the older ld must have
> > a way to properly deal with these assembly instructions, right?
> 
> The old linker will silently generate broken output.
Hmm. Broken as in ”will probably cause a SIGSEGV or SIGBUS“, or less broken? 😉
I’m wondering why nobody noticed this problem before. Is it possible that the problematic instructions belong to dead code, e.g. are part of a function that’s (almost) never called, part of a (faulty) generated exception handler, ...? Or did you find these instructions all over the place in the Oracle object files?
Comment 12 H.J. Lu 2017-02-14 23:02:49 UTC
(In reply to Dopıng from comment #11)
> (In reply to H.J. Lu from comment #10)
> > > What I don’t get is why the older GNU ld seems to link these objects files
> > > into a shared library just fine. If that’s the case, the older ld must have
> > > a way to properly deal with these assembly instructions, right?
> > 
> > The old linker will silently generate broken output.
> Hmm. Broken as in ”will probably cause a SIGSEGV or SIGBUS“, or less broken?
> 😉
> I’m wondering why nobody noticed this problem before. Is it possible that
> the problematic instructions belong to dead code, e.g. are part of a
> function that’s (almost) never called, part of a (faulty) generated
> exception handler, ...? Or did you find these instructions all over the
> place in the Oracle object files?

I don't think that piece of code is executed.
Comment 13 Dopıng 2017-02-15 11:03:55 UTC
My assembler knowledge is rusty, that much is certain...

Excerpts from the output of “objdump -dwr lxecg2e.o”:
00000010 <lxecg2e>:
  10:	66 90                	xchg   %ax,%ax
  12:	53                   	push   %ebx
  13:	8b dc                	mov    %esp,%ebx
  15:	83 e4 f0             	and    $0xfffffff0,%esp
  18:	55                   	push   %ebp
  19:	55                   	push   %ebp
  1a:	8b 6b 04             	mov    0x4(%ebx),%ebp
  1d:	89 6c 24 04          	mov    %ebp,0x4(%esp)
  21:	8b ec                	mov    %esp,%ebp
  23:	81 ec 08 04 00 00    	sub    $0x408,%esp
  29:	89 9d f8 fb ff ff    	mov    %ebx,-0x408(%ebp)
  2f:	89 7d f8             	mov    %edi,-0x8(%ebp)
  32:	e8 00 00 00 00       	call   37 <lxecg2e+0x27>
  37:	5f                   	pop    %edi
  38:	8d bf 03 00 00 00    	lea    0x3(%edi),%edi	3a: R_386_GOTPC	_GLOBAL_OFFSET_TABLE_
  3e:	8b 53 08             	mov    0x8(%ebx),%edx
  41:	89 75 fc             	mov    %esi,-0x4(%ebp)
  44:	8d 75 e0             	lea    -0x20(%ebp),%esi
  47:	89 75 f4             	mov    %esi,-0xc(%ebp)
  4a:	0f b7 42 30          	movzwl 0x30(%edx),%eax
  4e:	85 c0                	test   %eax,%eax
  50:	8b 8f 00 00 00 00    	mov    0x0(%edi),%ecx	52: R_386_GOT32	lxetbn
  56:	8b 0c 81             	mov    (%ecx,%eax,4),%ecx
  59:	89 4d f0             	mov    %ecx,-0x10(%ebp)
  5c:	75 19                	jne    77 <lxecg2e+0x67>
  [...]
  76:	c3                   	ret
  77:	8b 43 18             	mov    0x18(%ebx),%eax
  7a:	8d b5 08 fc ff ff    	lea    -0x3f8(%ebp),%esi
  80:	68 1c 02 00 00       	push   $0x21c
  85:	52                   	push   %edx
  86:	56                   	push   %esi
  87:	c7 00 ff ff ff ff    	movl   $0xffffffff,(%eax)
  8d:	8b df                	mov    %edi,%ebx
  8f:	e8 fc ff ff ff       	call   90 <lxecg2e+0x80>	90: R_386_PLT32	_intel_fast_memcpy
  94:	8b 9d f8 fb ff ff    	mov    -0x408(%ebp),%ebx
  9a:	8b 53 1c             	mov    0x1c(%ebx),%edx
  9d:	33 c0                	xor    %eax,%eax
  9f:	52                   	push   %edx
  a0:	66 89 85 38 fc ff ff 	mov    %ax,-0x3c8(%ebp)
  a7:	8d 85 2c ff ff ff    	lea    -0xd4(%ebp),%eax
  ad:	8b 0a                	mov    (%edx),%ecx
  af:	8d 15 00 00 00 00    	lea    0x0,%edx	b1: R_386_GOT32	lxecerr
  [...]

That last line is the only reference to ‘lxecerr’. If the JNE at 5c only jumps in case of an error condition, the remaining code may never get executed. But that’s quite a lot of code up to 8b4 (including six setjmp() calls, but no longjmp() call, so this code doesn’t smell like an error handler IMHO).

“ar x libnls12.a ; objdump -dwr *.o | egrep 'lea.*R_386_GOT32' | wc -l” prints 439. Does that grep make sense or would I need to grep for something else? How would the correct lea instruction look like?
Comment 14 H.J. Lu 2017-02-15 15:59:12 UTC
(In reply to Dopıng from comment #13)
> My assembler knowledge is rusty, that much is certain...

>   a0:	66 89 85 38 fc ff ff 	mov    %ax,-0x3c8(%ebp)
>   a7:	8d 85 2c ff ff ff    	lea    -0xd4(%ebp),%eax
>   ad:	8b 0a                	mov    (%edx),%ecx
>   af:	8d 15 00 00 00 00    	lea    0x0,%edx	b1: R_386_GOT32	lxecerr
>   [...]
> 

What are insns after "lea"?
Comment 15 Dopıng 2017-02-15 16:16:59 UTC
(In reply to H.J. Lu from comment #14)
> What are insns after "lea"?
  af:	8d 15 00 00 00 00    	lea    0x0,%edx	b1: R_386_GOT32	lxecerr
  b5:	ff 31                	pushl  (%ecx)
  b7:	50                   	push   %eax
  b8:	ff 34 17             	pushl  (%edi,%edx,1)
  bb:	56                   	push   %esi
  bc:	8d b5 58 fe ff ff    	lea    -0x1a8(%ebp),%esi
  c2:	56                   	push   %esi
  c3:	8b df                	mov    %edi,%ebx
  c5:	e8 fc ff ff ff       	call   c6 <lxecg2e+0xb6>	c6: R_386_PLT32	ldxnbeg
  ca:	8b 9d f8 fb ff ff    	mov    -0x408(%ebp),%ebx
  d0:	83 c4 24             	add    $0x24,%esp
  d3:	8b 43 14             	mov    0x14(%ebx),%eax
  d6:	83 f8 01             	cmp    $0x1,%eax
  d9:	0f 85 e9 02 00 00    	jne    3c8 <lxecg2e+0x3b8>
  df:	8d 85 2c ff ff ff    	lea    -0xd4(%ebp),%eax
  e5:	50                   	push   %eax
  e6:	8b df                	mov    %edi,%ebx
  e8:	e8 fc ff ff ff       	call   e9 <lxecg2e+0xd9>	e9: R_386_PLT32	_setjmp
  ed:	8b 9d f8 fb ff ff    	mov    -0x408(%ebp),%ebx
  f3:	83 c4 04             	add    $0x4,%esp
  f6:	85 c0                	test   %eax,%eax
  f8:	0f 85 d4 00 00 00    	jne    1d2 <lxecg2e+0x1c2>
  fe:	e8 00 00 00 00       	call   103 <lxecg2e+0xf3>
 103:	5e                   	pop    %esi
 104:	8d 45 d4             	lea    -0x2c(%ebp),%eax
 107:	50                   	push   %eax
 108:	ff 73 10             	pushl  0x10(%ebx)
 [...]
Comment 16 Dopıng 2017-02-15 16:27:36 UTC
Created attachment 9831 [details]
Disassembly + relocation section of lxecg2e.o

I’ve attached the complete disassembly and relocation section of lxecg2e.o.
Comment 17 H.J. Lu 2017-02-15 16:34:18 UTC
(In reply to Dopıng from comment #16)
> Created attachment 9831 [details]
> Disassembly + relocation section of lxecg2e.o
> 
> I’ve attached the complete disassembly and relocation section of lxecg2e.o.

I opened:

https://sourceware.org/bugzilla/show_bug.cgi?id=21168
Comment 18 cvs-commit@gcc.gnu.org 2017-02-15 19:12:42 UTC
The master branch has been updated by H.J. Lu <hjl@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=a5def14f1ca70e14d9433cb229c9369fa3051598

commit a5def14f1ca70e14d9433cb229c9369fa3051598
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Wed Feb 15 10:55:51 2017 -0800

    Add a test for R_386_GOT32/R_386_GOT32X IFUNC reloc error
    
    bfd/
    
    	PR ld/20244
    	* elf32-i386.c (elf_i386_relocate_section): Properly get IFUNC
    	symbol name when reporting R_386_GOT32/R_386_GOT32X relocation
    	error against local IFUNC symbol without a base register for
    	PIC.
    
    ld/
    
    	PR ld/20244
    	* testsuite/ld-i386/i386.exp: Run pr20244-4a, pr20244-4b and
    	pr20244-4c.
    	* testsuite/ld-i386/pr20244-4.s: New file.
    	* testsuite/ld-i386/pr20244-4a.d: Likewise.
    	* testsuite/ld-i386/pr20244-4b.d: Likewise.
    	* testsuite/ld-i386/pr20244-4c.d: Likewise.