Bug 16381

Summary: Explicit loader invocation "ld.so ./a.out" on a PIE binary calls global ctors twice
Product: glibc Reporter: Paul Pluzhnikov <ppluzhnikov>
Component: dynamic-linkAssignee: Paul Pluzhnikov <ppluzhnikov>
Severity: normal CC: corsix, stijnv
Priority: P2 Flags: fweimer: security-
Version: unspecified   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:

Description Paul Pluzhnikov 2013-12-30 22:31:31 UTC
Explicit loader invocation "ld.so ./a.out" on a PIE binary calls global ctors twice.
Confirmed with current trunk (e646a161cef3069fe1a6c92b750a87350630c62d).


#include <assert.h>

int g;

void init_g () __attribute__((constructor));

init_g ()
  assert (g == 0);
  g = 1;

main ()
  return 0;

gcc -g t.c -fPIE -pie
./a.out && echo ok

/tmp/build/elf/ld.so --library-path /tmp/build ./a.out
a.out: t.c:10: init_g: Assertion `g == 0' failed.
Aborted (core dumped)

The first call to init_g is here:

Breakpoint 1, init_g () at t.c:10
10        assert (g == 0);
(gdb) bt
#0  init_g () at t.c:10
#1  0x00005555555631ca in call_init (l=<optimized out>, argc=argc@entry=1, argv=argv@entry=0x7fffffffe370, env=env@entry=0x7fffffffe380) at dl-init.c:78
#2  0x00005555555632db in call_init (env=0x7fffffffe380, argv=0x7fffffffe370, argc=1, l=<optimized out>) at dl-init.c:36
#3  _dl_init (main_map=0x555555777190, argc=1, argv=0x7fffffffe370, env=0x7fffffffe380) at dl-init.c:126
#4  0x0000555555554dba in _dl_start_user () from /tmp/build/elf/ld.so

The second:

(gdb) bt
#0  init_g () at t.c:10
#1  0x00007ffff7dfc82d in __libc_csu_init ()
#2  0x00007ffff7a6804f in __libc_start_main (main=0x7ffff7dfc7bb <main>, argc=1, argv=0x7fffffffe370, init=0x7ffff7dfc7d0 <__libc_csu_init>, 
    fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe368) at libc-start.c:244
#3  0x00007ffff7dfc6a9 in _start ()
Comment 1 Paul Pluzhnikov 2014-01-12 03:51:02 UTC
Patch: http://cygwin.com/ml/libc-alpha/2014-01/msg00240.html
Comment 2 Sourceware Commits 2014-03-12 23:00:32 UTC
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, master has been updated
       via  798212a01311491d5e14fcda687460b75f8ca286 (commit)
      from  abe6d90cc8c1c212dab7cde4468f9ed895d6ba86 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------

commit 798212a01311491d5e14fcda687460b75f8ca286
Author: Paul Pluzhnikov <ppluzhnikov@google.com>
Date:   Wed Mar 12 15:58:39 2014 -0700

    2014-03-12  Paul Pluzhnikov  <ppluzhnikov@google.com>
    	[BZ #16381]
    	* elf/Makefile (tests): Add tst-pie2.
            (tests-pie): Add tst-pie2.
    	* elf/tst-pie2.c: New file.
    	* elf/dl-load.c (_dl_map_object_from_fd): Assert correct l_type
    	for ET_EXEC.
    	* elf/rtld.c (map_doit): Load executable as lt_executable.
    	(dl_main): Likewise.


Summary of changes:
 ChangeLog     |   12 ++++++++++++
 NEWS          |    6 +++---
 elf/Makefile  |    5 +++--
 elf/dl-load.c |    4 ++--
 elf/rtld.c    |    5 +++--
 5 files changed, 23 insertions(+), 9 deletions(-)
Comment 3 Paul Pluzhnikov 2014-03-13 00:37:01 UTC
Should be fixed now.
Comment 4 Florian Weimer 2021-12-04 15:25:44 UTC
*** Bug 17769 has been marked as a duplicate of this bug. ***
Comment 5 Peter Cawley 2024-05-31 16:51:45 UTC
I appreciate that this issue is more than ten years old, but for the benefit of anyone repeating the archaeological dig that I just did: I think that the fix for this issue also fixed another issue at the same time - explicit loader invocation "ld.so ./a.out" on a PIE binary didn't handle copy relocations properly.

Test for that:

#include <stdio.h>

int main(int argc, const char** argv) {
  fputs("Hello World!\n", stdout);

gcc -g t.c -fPIE -pie

With glibc 2.19 (i.e. before this fix):
$ ld-linux-x86-64.so.2 ./a.out
Segmentation fault (core dumped)

Versus correct behaviour with this fix:
$ ld-linux-x86-64.so.2 ./a.out
Hello World!