Support -z initfirst for multiple shared libraries

Message ID CAPESumoJ4wOGbq8JsttTwr+spbqWet6cNQvRViTOTbvzuQ07cw@mail.gmail.com
State New, archived
Headers

Commit Message

d wk April 26, 2016, 9:04 p.m. UTC
  Apologies, I renamed `node' to `new_node' at the last moment and broke
the patch. It should be easy to figure out, but here's a fixed version.

----[ cut here ]----
Support -z initfirst for multiple shared libraries (run in load order).

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 --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..5288b8f 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.  */