From patchwork Thu Feb 21 12:57:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vincent Whitchurch X-Patchwork-Id: 1046144 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=sourceware.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=libc-alpha-return-100196-incoming=patchwork.ozlabs.org@sourceware.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=axis.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b="EyqT/Bd1"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 444vdF4dMQz9s6w for ; Thu, 21 Feb 2019 23:57:20 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id; q=dns; s= default; b=IBFDX+OFD3P7uEkB158P5nLUVVgs7BZcEgQO5tvgZok79Xu0jSvUG rocuTiapTIfN40k7oPzoSr5i3+SBWUfsgpey8kR0P0tslLQuPXmo/NGyhfpWpcgr dZ7rBV4/1qGzXWm7FYduJ+pPmjvo+ova+bz0j0+V0R4n7XWI/0cD1w= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id; s=default; bh=mL1WSG6uOUW+J1jVT2PeHtVuzvU=; b=EyqT/Bd1+lGt4uDSkUsWzgw0Kmrh R8aKM6Ut8i+n/3quSqPtOW4K3r5wokgArZQT0ZwtDmDU3agxzYKgX6M+ERL/zyIU 3M8/tpRz6Or+6kMgfOjgOzOjnvH8tlnbOnJrn7mXscp9/i8mkCwKy5ZHK45EmfVK doOrpQIJRAfqVk0= Received: (qmail 12747 invoked by alias); 21 Feb 2019 12:57:13 -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 12034 invoked by uid 89); 21 Feb 2019 12:57:13 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=initializers, linkedin, Filters, accounting X-HELO: bastet.se.axis.com X-Axis-User: NO X-Axis-NonUser: YES From: Vincent Whitchurch To: libc-alpha@sourceware.org Cc: Vincent Whitchurch Subject: [PATCH v2] Initialize preloaded DSOs earlier [BZ #14379] Date: Thu, 21 Feb 2019 13:57:02 +0100 Message-Id: <20190221125702.1084-1-vincent.whitchurch@axis.com> Currently, preloaded DSOs are initialized after linked-in DSOs. However, in many cases it is desirable that preloaded DSO are initialized before linked-in DSOs (unless dependencies require otherwise). For example, when malloc is overloaded using a preloaded DSO for the purpose of heap profiling, we ideally want the preloaded DSO to be initialized before other DSOs so that it has a chance to set up its accounting code before their initializers are called. So ensure that preloaded libraries are initialized as early as possible and finalized as late as possible. Dependencies are still taken into account: DSOs which the preloaded DSO depends on are correctly initialized before it and finalized after it. If multiple DSOs are preloaded, DSOs earlier on the preload list will be initialized earlier and finalized later than DSOs present later on the list, analogous to how DSOs earlier on the preload list are searched for symbols before DSOs later on the preload list. Add a test case for this. Test suite run on x86-64. 2019-02-21 Vincent Whitchurch [BZ #14379] * elf/Makefile (tests): Add tst-preload-initorder and related rules. * elf/dl-deps.c (_dl_map_object_deps): Initialize preloaded DSOs earlier. * elf/dl-fini.c (_dl_fini): Finalize preloaded DSOs later. * elf/rtld.c (do_preload): Set l_preload flag on preloaded DSOs. * elf/tst-preload-initorder.c: New file. * elf/tst-preload-initorder.exp: Likewise. * include/link.h (struct link_map): Add new field l_preload. diff --git a/NEWS b/NEWS index 0a3b6c7a5a1..da3a0d7d200 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,9 @@ Deprecated and removed features, and other changes affecting compatibility: definitions in libc will be used automatically, which have been available since glibc 2.17. +* Preloaded shared objects are now initialized as early as dependencies allow, + and finalized as late as dependencies allow. + Changes to build and runtime requirements: * GCC 6.2 or later is required to build the GNU C Library. diff --git a/elf/Makefile b/elf/Makefile index 5c625b89fa3..c80645224ff 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -183,6 +183,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ tst-unique1 tst-unique2 $(if $(CXX),tst-unique3 tst-unique4 \ tst-nodelete) \ tst-initorder tst-initorder2 tst-relsort1 tst-null-argv \ + tst-preload-initorder \ tst-tlsalign tst-tlsalign-extern tst-nodelete-opened \ tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \ tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \ @@ -266,7 +267,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ tst-initordera2 tst-initorderb2 \ tst-initordera3 tst-initordera4 \ tst-initorder2a tst-initorder2b tst-initorder2c \ - tst-initorder2d \ + tst-initorder2d tst-initorder2x tst-initorder2y \ tst-relsort1mod1 tst-relsort1mod2 tst-array2dep \ tst-array5dep tst-null-argv-lib \ tst-tlsalign-lib tst-nodelete-opened-lib tst-nodelete2mod \ @@ -369,6 +370,7 @@ tests-special += $(objpfx)order-cmp.out $(objpfx)tst-array1-cmp.out \ $(objpfx)tst-array4-cmp.out $(objpfx)tst-array5-cmp.out \ $(objpfx)tst-array5-static-cmp.out $(objpfx)order2-cmp.out \ $(objpfx)tst-initorder-cmp.out \ + $(objpfx)tst-preload-initorder-cmp.out \ $(objpfx)tst-initorder2-cmp.out $(objpfx)tst-unused-dep.out \ $(objpfx)tst-unused-dep-cmp.out endif @@ -773,6 +775,13 @@ $(objpfx)preloadtest.out: $(preloadtest-preloads:%=$(objpfx)%.so) preloadtest-ENV = \ LD_PRELOAD=$(subst $(empty) ,:,$(strip $(preloadtest-preloads:=.so))) +tst-preload-initorder-preloads = tst-initorder2x tst-initorder2y +$(objpfx)tst-preload-initorder: $(objpfx)tst-initorder2c.so +$(objpfx)tst-preload-initorder.out: \ + $(tst-preload-initorder-preloads:%=$(objpfx)%.so) +tst-preload-initorder-ENV = \ + LD_PRELOAD=$(subst $(empty) ,:,$(strip $(tst-preload-initorder-preloads:=.so))) + $(objpfx)loadfail: $(libdl) LDFLAGS-loadfail = -rdynamic @@ -1343,19 +1352,28 @@ $(objpfx)tst-initorder-cmp.out: tst-initorder.exp $(objpfx)tst-initorder.out cmp $^ > $@; \ $(evaluate-test) +$(objpfx)tst-preload-initorder-cmp.out: \ + tst-preload-initorder.exp $(objpfx)tst-preload-initorder.out + cmp $^ > $@; \ + $(evaluate-test) + $(objpfx)tst-initorder2: $(objpfx)tst-initorder2a.so $(objpfx)tst-initorder2d.so $(objpfx)tst-initorder2c.so $(objpfx)tst-initorder2a.so: $(objpfx)tst-initorder2b.so $(objpfx)tst-initorder2b.so: $(objpfx)tst-initorder2c.so $(objpfx)tst-initorder2c.so: $(objpfx)tst-initorder2d.so +$(objpfx)tst-initorder2x.so: $(objpfx)tst-initorder2d.so +$(objpfx)tst-initorder2y.so: $(objpfx)tst-initorder2d.so LDFLAGS-tst-initorder2 = $(no-as-needed) LDFLAGS-tst-initorder2a.so = $(no-as-needed) LDFLAGS-tst-initorder2b.so = $(no-as-needed) LDFLAGS-tst-initorder2c.so = $(no-as-needed) +LDFLAGS-tst-initorder2x.so = $(no-as-needed) +LDFLAGS-tst-initorder2y.so = $(no-as-needed) define o-iterator-doit $(objpfx)tst-initorder2$o.os: tst-initorder2.c; \ $$(compile-command.c) -DNAME=\"$o\" endef -object-suffixes-left := a b c d +object-suffixes-left := a b c d x y include $(o-iterator) $(objpfx)tst-initorder2-cmp.out: tst-initorder2.exp $(objpfx)tst-initorder2.out diff --git a/elf/dl-deps.c b/elf/dl-deps.c index e12c353158a..5cc9b2d3646 100644 --- a/elf/dl-deps.c +++ b/elf/dl-deps.c @@ -587,8 +587,36 @@ Filters not supported with LD_TRACE_PRELINKING")); /* Sort the initializer list to take dependencies into account. The binary itself will always be initialize last. */ - memcpy (l_initfini, map->l_searchlist.r_list, - nlist * sizeof (struct link_map *)); + if (__glibc_unlikely (preloads > 0 && nlist > npreloads + 1)) + { + unsigned int src, dest = 0; + + /* The binary itself and non-preloaded DSOs will be initialized after + preloaded DSOs. */ + for (src = 0; src < nlist; src++) + { + struct link_map *dso = map->l_searchlist.r_list[src]; + + if (!dso->l_preload) + l_initfini[dest++] = dso; + } + + /* Insert preloads in reverse order so that preloads earlier on the + list are initialized earlier. */ + for (src = nlist - 1; src > 0; src--) + { + struct link_map *dso = map->l_searchlist.r_list[src]; + + if (dso->l_preload) + l_initfini[dest++] = dso; + } + } + else + { + memcpy (l_initfini, map->l_searchlist.r_list, + nlist * sizeof (struct link_map *)); + } + /* We can skip looking for the binary itself which is at the front of the search list. */ _dl_sort_maps (&l_initfini[1], nlist - 1, NULL, false); diff --git a/elf/dl-fini.c b/elf/dl-fini.c index 1e55d398149..b4cb3887110 100644 --- a/elf/dl-fini.c +++ b/elf/dl-fini.c @@ -68,22 +68,45 @@ _dl_fini (void) struct link_map *maps[nloaded]; unsigned int i; - struct link_map *l; + bool have_preloads = false; + struct link_map *l, *last = NULL; assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL); for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next) - /* Do not handle ld.so in secondary namespaces. */ - if (l == l->l_real) - { - assert (i < nloaded); - - maps[i] = l; - l->l_idx = i; - ++i; - - /* Bump l_direct_opencount of all objects so that they - are not dlclose()ed from underneath us. */ - ++l->l_direct_opencount; - } + { + last = l; + if (__glibc_unlikely (l->l_preload)) + have_preloads = true; + /* Do not handle ld.so in secondary namespaces. */ + if (l == l->l_real && !l->l_preload) + { + assert (i < nloaded); + + maps[i] = l; + l->l_idx = i; + ++i; + + /* Bump l_direct_opencount of all objects so that they + are not dlclose()ed from underneath us. */ + ++l->l_direct_opencount; + } + } + + /* Preloaded DSOs should be finalized late and in reverse order. */ + if (__glibc_unlikely (have_preloads)) + for (l = last; l != NULL; l = l->l_prev) + if (l == l->l_real && l->l_preload) + { + assert (i < nloaded); + + maps[i] = l; + l->l_idx = i; + ++i; + + /* Bump l_direct_opencount of all objects so that they + are not dlclose()ed from underneath us. */ + ++l->l_direct_opencount; + } + assert (ns != LM_ID_BASE || i == nloaded); assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1); unsigned int nmaps = i; diff --git a/elf/rtld.c b/elf/rtld.c index c1cc1b01f2d..ce7a1ed234b 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -770,8 +770,12 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded (%s): ignored.\n", the libc's malloc is used. */ } else if (GL(dl_ns)[LM_ID_BASE]._ns_nloaded != old_nloaded) - /* It is no duplicate. */ - return 1; + { + args.map->l_preload = 1; + + /* It is no duplicate. */ + return 1; + } /* Nothing loaded. */ return 0; diff --git a/elf/tst-preload-initorder.c b/elf/tst-preload-initorder.c new file mode 100644 index 00000000000..4305ba79763 --- /dev/null +++ b/elf/tst-preload-initorder.c @@ -0,0 +1 @@ +#include "tst-initorder.c" diff --git a/elf/tst-preload-initorder.exp b/elf/tst-preload-initorder.exp new file mode 100644 index 00000000000..a163dc3ec09 --- /dev/null +++ b/elf/tst-preload-initorder.exp @@ -0,0 +1,9 @@ +init: d +init: x +init: y +init: c +main +fini: c +fini: y +fini: x +fini: d diff --git a/include/link.h b/include/link.h index 736e1d72aec..3ac11277801 100644 --- a/include/link.h +++ b/include/link.h @@ -202,6 +202,7 @@ struct link_map unsigned int l_free_initfini:1; /* Nonzero if l_initfini can be freed, ie. not allocated with the dummy malloc in ld.so. */ + unsigned int l_preload:1; /* Nonzero if DSO has been preloaded. */ #include