This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
Re: [PATCH] Support -z initfirst for multiple shared libraries
- From: d wk <dwksrc at gmail dot com>
- To: libc-alpha at sourceware dot org
- Date: Tue, 26 Apr 2016 17:04:03 -0400
- Subject: Re: [PATCH] Support -z initfirst for multiple shared libraries
- Authentication-results: sourceware.org; auth=none
- References: <CAPESumqMqVem6VvaKXf_ko1zpM9_wOXixp1O7WGtS0RnvhMSpg at mail dot gmail dot com>
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(-)
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. */
--
1.9.1
On Tue, Apr 26, 2016 at 4:58 PM, d wk <dwksrc@gmail.com> wrote:
> Hello libc developers,
>
> In a project of mine, I needed to run some code before any constructors
> from any system libraries (such as libc or libpthread). The linker/loader
> -z initfirst feature is perfect for this, but it only supports one shared
> library. Unfortunately libpthread also uses this feature (I assume the
> feature exists because pthread needed it), so my project was incompatible
> with libpthread.
>
> So, I wrote a small patch which changes the single dl_initfirst variable
> into a linked list. This patch does not change the size of any data
> structures (it's ABI compatible), just turns dl_initfirst into a list. The
> list is not freed (the allocator wouldn't free it anyway), and insertion
> into the list is quadratic, but I expect there will never be more than
> a handful of initfirst libraries!
>
> This patch records initfirst libraries in load order, so LD_PRELOAD
> libraries will have their constructors called before libpthread. If the
> opposite behaviour is desired, the LD_PRELOAD'd library can always declare
> a dependency on libpthread. Normally LD_PRELOAD constructors are run last,
> which is very inconvenient when trying to inject new functionality, and
> I expect anyone using -z initfirst with LD_PRELOAD to really want to run
> first. The patch is written against latest glibc 2.23 (I also tested on
> glibc 2.21, and it's not quite compatible with 2.17 since the other data
> structures changed).
>
> I was not the first person to run into this problem,
> someone wanted the same thing on stack overflow two years
> ago. You can see my answer there with a complete test case.
> http://stackoverflow.com/questions/19796383/linux-ld-preload-z-initfirst/36143861
>
> Hope you will accept this patch. Comments welcome. Thanks,
>
> ~ dwk.
>
>
> ----[ 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(-)
>
> 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(*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. */
> --
> 1.9.1