This is the mail archive of the glibc-bugs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug dynamic-link/17769] New: The program interpreter should not call static initializers in PIE binaries


https://sourceware.org/bugzilla/show_bug.cgi?id=17769

            Bug ID: 17769
           Summary: The program interpreter should not call static
                    initializers in PIE binaries
           Product: glibc
           Version: 2.19
            Status: NEW
          Keywords: glibc_2.19
          Severity: normal
          Priority: P2
         Component: dynamic-link
          Assignee: unassigned at sourceware dot org
          Reporter: stijnv at gmail dot com

Hello,

I've been seeing the following problem for a while now. It has been posted on
other bugtrackers, e.g.
https://code.google.com/p/chromium/issues/detail?id=394508 , but even though
this particular bugtracker says the problem is fixed, it is not fixed for me.

Whenever gcc compiles a program as PIE, its ELF type is set to ET_DYN. While
this isn't a problem on its own, it does make ld-linux.so falsely believe that
the program is a shared library. Observe for example the following program:

  #include <stdio.h>
  void __attribute__((constructor)) pie_init() {
    printf("I am a PIE binary and this is my constructor!\n");
  }
  int main(int argc, char** argv) {
    printf("Hello!\n");
    return 0;
  }

When compiled as PIE and invoked directly I get:

  $ gcc -ggdb -o i_like_pie -fPIE -pie i_like_pie.c
  $ ./i_like_pie 
  I am a PIE binary and this is my constructor!
  Hello!

This is the expected output. However, when invoked through the interpreter, I
get:

  $ /lib/ld-linux.so.2 ./i_like_pie
  I am a PIE binary and this is my constructor!
  I am a PIE binary and this is my constructor!
  Hello!

This is certainly not the correct output. If the program is not compiled as
PIE, I get the same output in both cases (direct invocation and invocation
through the program interpreter). This is hardly surprising, as the call_init
function, which invokes the initializers checks:

  /* Check for object which constructors we do not run here.  */
  if (__builtin_expect (l->l_name[0], 'a') == '\0'
      && l->l_type == lt_executable)
    return;

For non-PIE binaries, the l_type is indeed lt_executable. For PIE binaries, the
l_type is lt_library. The root cause for this problem lies in elf/rtld.c. If
the program is loaded directly, the interpreter simply creates the main map as
follows:

  /* Create a link_map for the executable itself. 
  This will be what dlopen on "" returns.  */
  main_map = _dl_new_object ((char *) "", "", lt_executable, NULL, 
    __RTLD_OPENEXEC, LM_ID_BASE);

However, if the program is interpreter loads the program instead, it does:

  _dl_map_object (NULL, rtld_progname, lt_library, 0, 
    __RTLD_OPENEXEC, LM_ID_BASE);

  /* ... */
  /* Now the map for the main executable is available.  */
  main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;

The problem here is that _dl_map_object will actually read l_type from the ELF
header, wheras dl_new_object will use whatever l_type you pass to it. I see no
reason why l_type shouldn't be set to lt_executable if a program is loaded
indirecty, even if the program you're attempting to load is in fact a shared
library.

The following patch fixes the problem for me:

$ diff -up glibc-2.19.orig/elf/rtld.c glibc-2.19/elf/rtld.c 
--- glibc-2.19.orig/elf/rtld.c    2014-12-09 16:38:51.929980899 +0100
+++ glibc-2.19/elf/rtld.c    2014-12-28 13:20:18.613821869 +0100
@@ -1085,6 +1085,11 @@ of this helper program; chances are you
       /* Now the map for the main executable is available.  */
       main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;

+      /* Make sure that the type for the main binary is lt_executable, 
+     even if it says ET_DYN in the header. This way we won't call the
+     static initializers twice for PIE binaries */
+      main_map->l_type = lt_executable;
+
       if (__builtin_expect (mode, normal) == normal
       && GL(dl_rtld_map).l_info[DT_SONAME] != NULL
       && main_map->l_info[DT_SONAME] != NULL

Regards,
Stijn

-- 
You are receiving this mail because:
You are on the CC list for the bug.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]