Bug 25384 - Copy relocations and BIND_NOW on POWER ELFv1 results in crashes
Summary: Copy relocations and BIND_NOW on POWER ELFv1 results in crashes
Status: RESOLVED FIXED
Alias: None
Product: binutils
Classification: Unclassified
Component: ld (show other bugs)
Version: 2.34
: P2 normal
Target Milestone: 2.34
Assignee: Alan Modra
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-01-14 13:50 UTC by Florian Weimer
Modified: 2020-01-15 13:07 UTC (History)
0 users

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


Attachments
libshared.so (2.38 KB, application/octet-stream)
2020-01-14 13:50 UTC, Florian Weimer
Details
liblike-dl.so (2.31 KB, application/octet-stream)
2020-01-14 13:50 UTC, Florian Weimer
Details
liblike-pthread.so (2.25 KB, application/octet-stream)
2020-01-14 13:51 UTC, Florian Weimer
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Florian Weimer 2020-01-14 13:50:25 UTC
Created attachment 12203 [details]
libshared.so

This script produces a ./main executable which crashes when run:

cat >like-pthread.c <<EOF
void
implementation (void)
{
}
EOF
gcc -fPIC -shared -o liblike-pthread.so like-pthread.c -Wl,-z,now

cat >like-dl.c <<EOF
extern void implementation (void) __attribute__ ((weak));
void
use_implementation (void)
{
  if (implementation != 0)
    implementation ();
}
EOF
gcc -fPIC -shared -o liblike-dl.so like-dl.c -Wl,-z,now

cat >shared.c <<EOF
extern void use_implementation (void);
extern void _exit (int);

void
call_dlopen (void)
{
  use_implementation ();
  _exit (0);
}
EOF

cat >main.s <<EOF
	.section .rodata
ptr:
	.quad alias
	.weakref alias, implementation
        .section        ".opd","aw"
        .align 3
	.globl main
main:
        .quad   .L.main,.TOC.@tocbase
        .text
        .type   main, @function
.L.main:
        bl call_dlopen
	nop
EOF

gcc -fPIC -shared -o libshared.so shared.c -Wl,--no-as-needed -L. -llike-dl -llike-pthread
gcc -Wl,-rpath,. -o main main.s -Wl,--no-as-needed -L. -lshared


It reproduces with binutils 2.27 and the current master branch in Git (at commit 7a6bf3becbe3e0ce47d2681edcfe7adcb67fe4e2).

GDB says this:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x00003fffb7d008c8 in use_implementation () from ./liblike-dl.so
#2  0x00003fffb7f50918 in call_dlopen () from ./libshared.so
#3  0x0000000010000784 in main ()

The construct in use_implementation was previously discussed here:

  <https://www.sourceware.org/ml/gnu-gabi/2016-q1/msg00004.html>

This crash arises when current glibc is built with --enable-bind-now (see the downstream report; our 2.17 build includes the --enable-bind-now changes in glibc master).
Comment 1 Florian Weimer 2020-01-14 13:50:57 UTC
Created attachment 12204 [details]
liblike-dl.so
Comment 2 Florian Weimer 2020-01-14 13:51:16 UTC
Created attachment 12205 [details]
liblike-pthread.so
Comment 3 Alan Modra 2020-01-14 23:25:16 UTC
The trigger for this problem is an initialized function pointer in a read-only section, the function being defined in a shared library.

	.section .rodata
ptr:
	.quad alias
	.weakref alias, implementation

That generates a .dynbss copy and copy reloc via linker code supposed to work around bugs in very old versions of gcc.  The work-around should have reported an error

  if (h->plt.plist != NULL)
    {
      /* We should never get here, but unfortunately there are versions
	 of gcc out there that improperly (for this ABI) put initialized
	 function pointers, vtable refs and suchlike in read-only
	 sections.  Allow them to proceed, but warn that this might
	 break at runtime.  */
      info->callbacks->einfo
	(_("%P: copy reloc against `%T' requires lazy plt linking; "
	   "avoid setting LD_BIND_NOW=1 or upgrade gcc\n"),
	 h->root.root.string);
    }

but doesn't due to a lack of a call to "implementation" in the executable.  (plt.plist is NULL).  That's a linker bug.  The warning is relevant even when there is no call in the executable.

So we have this .dynbss copy of the "implementation" function descriptor in the executable:

000000001001fc08  0000000800000013 R_PPC64_COPY           000000001001fc08 implementation + 0

     8: 000000001001fc08    40 FUNC    GLOBAL DEFAULT   18 implementation

Notice that the size (40) is incorrect for a function descriptor.  That's because   gcc puts the text size of the function on the symbol.  But that means the .dynbss copy is the wrong size too, a linker and glibc bug if we are to support .dynbss copies of function descriptors with versions of gcc since 2004 when dot-symbols were removed.  (With dot-symbols, the size of the "implementation symbol is the size of the function descriptor with the size of ".implementation" being the text size.)

Anyway, a size larger than 16 is OK for copying the descriptor and "works" just as well for this testcase as if we copied the descriptor.  We just get some more of .opd copied from the library defining "implementation".

The segfault happens because the PLT slot in liblike-dl.so for "implementation" is initialized from the function descriptor for "implementation", which is now the copy in main.  Unfortunately that initialization happens before "main" is processed to initialize the copy, resulting in the PLT slot being set to zeros.
Comment 4 cvs-commit@gcc.gnu.org 2020-01-15 02:44:17 UTC
The master branch has been updated by Alan Modra <amodra@sourceware.org>:

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

commit e1c6cf618cbeebbafd34afc5ee921fcbf7061bfa
Author: Alan Modra <amodra@gmail.com>
Date:   Tue Jan 14 20:45:53 2020 +1030

    PR25384, PowerPC64 ELFv1 copy relocs against function symbols
    
    Function symbols of course don't normally want .dynbss copies but
    with some old versions of gcc they are needed to copy the function
    descriptor.  This patch restricts the cases where they are useful to
    compilers using dot-symbols, and enables the warning regardless of
    whether a PLT entry is emitted in the executable.  PLTs in shared
    libraries are affected by a .dynbss copy in the executable.
    
    bfd/
    	PR 25384
    	* elf64-ppc.c (ELIMINATE_COPY_RELOCS): Update comment.
    	(ppc64_elf_adjust_dynamic_symbol): Don't allow .dynbss copies
    	of function symbols unless dot symbols are present.  Do warn
    	whenever one is created, regardles of whether a PLT entry is
    	also emitted for the function symbol.
    ld/
    	* testsuite/ld-powerpc/ambiguousv1b.d: Adjust expected output.
    	* testsuite/ld-powerpc/funref.s: Align func_tab.
    	* testsuite/ld-powerpc/funref2.s: Likewise.
    	* testsuite/ld-powerpc/funv1.s: Add dot symbols.
Comment 5 Alan Modra 2020-01-15 02:48:02 UTC
Fixed.  You'll now get DT_TEXTREL and a dynamic reloc instead of a .dynbss copy.
Comment 6 cvs-commit@gcc.gnu.org 2020-01-15 03:20:34 UTC
The binutils-2_33-branch branch has been updated by Alan Modra <amodra@sourceware.org>:

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

commit 02efd171de60180efa8f77e311cd1382a52553c6
Author: Alan Modra <amodra@gmail.com>
Date:   Tue Jan 14 20:45:53 2020 +1030

    PR25384, PowerPC64 ELFv1 copy relocs against function symbols
    
    Function symbols of course don't normally want .dynbss copies but
    with some old versions of gcc they are needed to copy the function
    descriptor.  This patch restricts the cases where they are useful to
    compilers using dot-symbols, and enables the warning regardless of
    whether a PLT entry is emitted in the executable.  PLTs in shared
    libraries are affected by a .dynbss copy in the executable.
    
    bfd/
    	PR 25384
    	* elf64-ppc.c (ELIMINATE_COPY_RELOCS): Update comment.
    	(ppc64_elf_adjust_dynamic_symbol): Don't allow .dynbss copies
    	of function symbols unless dot symbols are present.  Do warn
    	whenever one is created, regardles of whether a PLT entry is
    	also emitted for the function symbol.
    ld/
    	* testsuite/ld-powerpc/ambiguousv1b.d: Adjust expected output.
    	* testsuite/ld-powerpc/funref.s: Align func_tab.
    	* testsuite/ld-powerpc/funref2.s: Likewise.
    	* testsuite/ld-powerpc/funv1.s: Add dot symbols.
    
    (cherry picked from commit e1c6cf618cbeebbafd34afc5ee921fcbf7061bfa)
Comment 7 cvs-commit@gcc.gnu.org 2020-01-15 03:20:49 UTC
The binutils-2_32-branch branch has been updated by Alan Modra <amodra@sourceware.org>:

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

commit f5deb4103e0cec3912c304d7c08277aadab09e5c
Author: Alan Modra <amodra@gmail.com>
Date:   Tue Jan 14 20:45:53 2020 +1030

    PR25384, PowerPC64 ELFv1 copy relocs against function symbols
    
    Function symbols of course don't normally want .dynbss copies but
    with some old versions of gcc they are needed to copy the function
    descriptor.  This patch restricts the cases where they are useful to
    compilers using dot-symbols, and enables the warning regardless of
    whether a PLT entry is emitted in the executable.  PLTs in shared
    libraries are affected by a .dynbss copy in the executable.
    
    bfd/
    	PR 25384
    	* elf64-ppc.c (ELIMINATE_COPY_RELOCS): Update comment.
    	(ppc64_elf_adjust_dynamic_symbol): Don't allow .dynbss copies
    	of function symbols unless dot symbols are present.  Do warn
    	whenever one is created, regardles of whether a PLT entry is
    	also emitted for the function symbol.
    ld/
    	* testsuite/ld-powerpc/ambiguousv1b.d: Adjust expected output.
    	* testsuite/ld-powerpc/funref.s: Align func_tab.
    	* testsuite/ld-powerpc/funref2.s: Likewise.
    	* testsuite/ld-powerpc/funv1.s: Add dot symbols.
    
    (cherry picked from commit e1c6cf618cbeebbafd34afc5ee921fcbf7061bfa)
Comment 8 Florian Weimer 2020-01-15 13:07:49 UTC
I believe later GCC (I tried 7) uses .data.relro with -O0 and the __pthread_key_create-based single thread detection, not .rodata. This means that the current toolchain is consistent and does not produce text relocations.