Control constructor order with -z initfirst for multiple libraries
Commit Message
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(-)
@@ -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;
}
@@ -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;
@@ -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;
@@ -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. */