Control constructor order with -z initfirst for multiple libraries
diff mbox

Message ID CAPESumqQs8sKAKkV8vBNTO1PwM=xPXktRmUMUsuUYqWofuWxVQ@mail.gmail.com
State New
Headers show

Commit Message

d wk Sept. 29, 2016, 2:09 p.m. UTC
Hello libc developers,

I wanted a way to run constructors before any other code in shared libraries.
libpthread achieves this with the loader feature -z initfirst. But as written,
the mechanism only supports one shared library, and I wanted compatibility with
libpthread. Hence, I wrote a patch for libc that extends -z initfirst to
support multiple shared libraries.

I sent the same patch to this mailing list several months ago:
https://sourceware.org/ml/libc-alpha/2016-04/msg00666.html
My copyright assignment has been completed and so I am posting it again.

To summarize some points from the earlier discussion: this patch does not
affect symbol resolution or dependencies, only constructor order. It could be
extended to call destructors in reverse order, though I don't believe libc
currently does that. It is compatible with LD_PRELOAD, and Solaris's initfirst
definition.

Comment welcome. Thanks,

~ dwk.


----[ cut here ]----
Control constructor order with -z initfirst for multiple libraries.

This is particularly useful when combined with LD_PRELOAD, as it is then
possible to run constructors before any code in other libraries runs.
---
 elf/dl-init.c              |  9 ++++++++-
 elf/dl-load.c              | 19 ++++++++++++++++++-
 elf/dl-support.c           |  4 ++--
 sysdeps/generic/ldsodefs.h |  7 +++++--
 4 files changed, 33 insertions(+), 6 deletions(-)

Patch
diff mbox

diff --git a/elf/dl-init.c b/elf/dl-init.c
index 818c3aa..da59d1f 100644
--- a/elf/dl-init.c
+++ b/elf/dl-init.c
@@ -84,7 +84,14 @@  _dl_init (struct link_map *main_map, int argc, char
**argv, char **env)

   if (__glibc_unlikely (GL(dl_initfirst) != NULL))
     {
-      call_init (GL(dl_initfirst), argc, argv, env);
+      struct initfirst_list *initfirst;
+      for(initfirst = GL(dl_initfirst); initfirst; initfirst = initfirst->next)
+        {
+          call_init (initfirst->which, argc, argv, env);
+        }
+
+      /* We do not try to free this list, as the memory will not be reclaimed
+         by the allocator unless there were no intervening malloc()'s.  */
       GL(dl_initfirst) = NULL;
     }

diff --git a/elf/dl-load.c b/elf/dl-load.c
index c0d6249..1efabbf 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1388,7 +1388,24 @@  cannot enable executable stack as shared object
requires");

   /* Remember whether this object must be initialized first.  */
   if (l->l_flags_1 & DF_1_INITFIRST)
-    GL(dl_initfirst) = l;
+    {
+      struct initfirst_list *new_node = malloc(sizeof(*new_node));
+      struct initfirst_list *it = GL(dl_initfirst);
+      new_node->which = l;
+      new_node->next = NULL;
+
+      /* We append to the end of the linked list. Whichever library was loaded
+         first has higher initfirst priority. This means that LD_PRELOAD
+         initfirst overrides initfirst in libraries linked normally.  */
+      if (!it)
+        GL(dl_initfirst) = new_node;
+      else
+        {
+          while (it->next)
+            it = it->next;
+          it->next = new_node;
+        }
+    }

   /* Finally the file information.  */
   l->l_file_id = id;
diff --git a/elf/dl-support.c b/elf/dl-support.c
index c30194c..d8b8acc 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -147,8 +147,8 @@  struct r_search_path_elem *_dl_all_dirs;
 /* All directories after startup.  */
 struct r_search_path_elem *_dl_init_all_dirs;

-/* The object to be initialized first.  */
-struct link_map *_dl_initfirst;
+/* The list of objects to be initialized first.  */
+struct initfirst_list *_dl_initfirst;

 /* Descriptor to write debug messages to.  */
 int _dl_debug_fd = STDERR_FILENO;
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
index ddec0be..198c089 100644
--- a/sysdeps/generic/ldsodefs.h
+++ b/sysdeps/generic/ldsodefs.h
@@ -326,8 +326,11 @@  struct rtld_global
   /* Incremented whenever something may have been added to dl_loaded.  */
   EXTERN unsigned long long _dl_load_adds;

-  /* The object to be initialized first.  */
-  EXTERN struct link_map *_dl_initfirst;
+  /* The list of objects to be initialized first.  */
+  EXTERN struct initfirst_list {
+    struct link_map *which;
+    struct initfirst_list *next;
+  } *_dl_initfirst;

 #if HP_SMALL_TIMING_AVAIL
   /* Start time on CPU clock.  */