From patchwork Thu Sep 29 14:09:58 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: d wk X-Patchwork-Id: 16146 Received: (qmail 120207 invoked by alias); 29 Sep 2016 14:10:20 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 120088 invoked by uid 89); 29 Sep 2016 14:10:11 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.4 required=5.0 tests=BAYES_00, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, RCVD_IN_SORBS_SPAM, SPF_PASS autolearn=no version=3.3.2 spammy=8414 X-HELO: mail-wm0-f66.google.com X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:from:date:message-id:subject:to:cc; bh=tIgfLY/00Q//oh9oq7aNgZPMXc37A367T549EYeghBg=; b=g0NaxfAQUwV9Zqd2GkGytoctiw32wp1fmbw3XLv8DyAW/tr6mUAXU2TH1NsSY6TofW 3Rk0naVLZYZlnaaE2G6pFCKrMQwH9gnALF2th4MA1CBTfVa4nl1Mk/m5F8/L0CS7kOb9 wkUzTLk88aFxgOGPLreC8dZ0Z3shIKnqmAwAJLV5/R0rU05RzO/K4RIUygd+H6Ba3aYI qzkajoYxyFTzT+j3ySsx0W4bFWlQEtGVL7B0Qdsi0uND6sfN3q6npgxbtrfosOe9iM0l qfDIqpL8VGrlsGIuQ28g0kMan7YnzM1GguYNaGwfnCw/UcFYkFxzWzoSd4SvnN9+F0/D gJQA== X-Gm-Message-State: AA6/9RnteQjvtdLkR4lYhyUUgNhVq6N4wkDuUPjsX05mb5GohIwE7H3bf08UchEs2duzabuzacHvcmZkPYZGGA== X-Received: by 10.28.4.72 with SMTP id 69mr13206865wme.91.1475158199120; Thu, 29 Sep 2016 07:09:59 -0700 (PDT) MIME-Version: 1.0 From: d wk Date: Thu, 29 Sep 2016 10:09:58 -0400 Message-ID: Subject: [PATCH] Control constructor order with -z initfirst for multiple libraries To: libc-alpha@sourceware.org Cc: "Carlos O'Donell" 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(-) 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. */