From patchwork Fri Jul 9 13:50:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 44299 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 72290398A4B3 for ; Fri, 9 Jul 2021 13:50:31 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 72290398A4B3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1625838631; bh=qHxAyb+R2WL3/6BohIdFnLnm63WdOGHWrCkiuU/21JE=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=GKGaaEEXtDsYlpeW1jdqrcbotQ4jHPPQ0+TkkZNNCfKB9/oW08W//5ADLDFiWqlMa hz9r9Ory5ormYXmwgySFQC68qbAO+vsNBmX/ewHx7+BSqhRYVA+PXgpZ/J36SWOOqx tPf4hnznUExs9o4GwgaZjvKqDI2NlIk008915etI= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pg1-x530.google.com (mail-pg1-x530.google.com [IPv6:2607:f8b0:4864:20::530]) by sourceware.org (Postfix) with ESMTPS id 9009C384842D for ; Fri, 9 Jul 2021 13:50:07 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 9009C384842D Received: by mail-pg1-x530.google.com with SMTP id w15so9992188pgk.13 for ; Fri, 09 Jul 2021 06:50:07 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=qHxAyb+R2WL3/6BohIdFnLnm63WdOGHWrCkiuU/21JE=; b=VAvRqPPntcM3TB7RwddmftuDx+BFUpxEFakskMLATSNoB2UsrjZG8cfhXeBG+U9PIW 6p2ti8A467qtnn8491clGRiu698c5WBEeN11Y3/VyOOpyDm3lkf0IUqrBVjOMd1bmx3e LVU4Hv7GsdI9474/WrihZ7/YQrcaKfOo3PqdhDcnUlYsbgAZdSM/m/cTPbbtG93fhVoa DKn1l5uL9Yyba4T96JUGXxebQ2Zv/Hbo0KoB4pgBbwXzCJp2WJNvOHa0PaH37oWyR/D0 hmIxFCuTMLesoIaCw5P9Cb9BLvioR1EzVKmOLvbKKOjiHxUv3CFcSJzAu+QNOmvfhVFK uobA== X-Gm-Message-State: AOAM533W/FPAKhix83D46ivYtKwgAAmV2/sXO/pfV6HOUteU37oolDQn Jt5PNjHBwXetADlnJM1Ju5oXUCHSMXFjPg== X-Google-Smtp-Source: ABdhPJwBt1NMiTKakTRWPaQBszE10joOWwnp9L/H0Cjv1rjOuF9OcbXjNReqYldPLbmSH8/C8vxaig== X-Received: by 2002:a63:2d46:: with SMTP id t67mr38119156pgt.307.1625838606219; Fri, 09 Jul 2021 06:50:06 -0700 (PDT) Received: from birita.. ([177.194.59.218]) by smtp.gmail.com with ESMTPSA id v6sm7726936pgk.33.2021.07.09.06.50.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 09 Jul 2021 06:50:05 -0700 (PDT) To: libc-alpha@sourceware.org Subject: [PATCH v3] elf: Fix DTV gap reuse logic (BZ #27135) Date: Fri, 9 Jul 2021 10:50:01 -0300 Message-Id: <20210709135001.505521-1-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.30.2 MIME-Version: 1.0 X-Spam-Status: No, score=-12.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Adhemerval Zanella via Libc-alpha From: Adhemerval Zanella Netto Reply-To: Adhemerval Zanella Cc: Szabolcs Nagy Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" Changes from previous version: - Fix commit message and add a line about the bug fixes. - Use atomic operation while setting the slotinfo. - Use test_verbose on tst-tls20.c. Reviewed-by: Szabolcs Nagy --- This is updated version of the 572bd547d57a (reverted by 40ebfd016ad2) that fixes the _dl_next_tls_modid issues. This issue with 572bd547d57a patch is the DTV entry will be only update on dl_open_worker() with the update_tls_slotinfo() call after all dependencies are being processed by _dl_map_object_deps(). However _dl_map_object_deps() itself might call _dl_next_tls_modid(), and since the _dl_tls_dtv_slotinfo_list::map is not yet set the entry will be wrongly reused. This patch fixes by renaming the _dl_next_tls_modid() function to _dl_assign_tls_modid() and by passing the link_map so it can set the slotinfo value so a so subsequente _dl_next_tls_modid() call will see the entry as allocated. The intermediary value is cleared up on remove_slotinfo() for the case a library fails to load with RTLD_NOW. This patch fixes BZ #27135. Checked on x86_64-linux-gnu. --- elf/Makefile | 64 ++++++++- elf/dl-close.c | 8 +- elf/dl-load.c | 2 +- elf/dl-open.c | 10 -- elf/dl-tls.c | 17 +-- elf/rtld.c | 2 +- elf/tst-tls20.c | 275 ++++++++++++++++++++++++++++++++++++- sysdeps/generic/ldsodefs.h | 4 +- 8 files changed, 349 insertions(+), 33 deletions(-) diff --git a/elf/Makefile b/elf/Makefile index 4b320e8b3a..bdd5cc9e1a 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -253,6 +253,13 @@ one-hundred = $(foreach x,0 1 2 3 4 5 6 7 8 9, \ 0$x 1$x 2$x 3$x 4$x 5$x 6$x 7$x 8$x 9$x) tst-tls-many-dynamic-modules := \ $(foreach n,$(one-hundred),tst-tls-manydynamic$(n)mod) +tst-tls-many-dynamic-modules-dep-suffixes = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 \ + 14 15 16 17 18 19 +tst-tls-many-dynamic-modules-dep = \ + $(foreach n,$(tst-tls-many-dynamic-modules-dep-suffixes),tst-tls-manydynamic$(n)mod-dep) +tst-tls-many-dynamic-modules-dep-bad-suffixes = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 +tst-tls-many-dynamic-modules-dep-bad = \ + $(foreach n,$(tst-tls-many-dynamic-modules-dep-bad-suffixes),tst-tls-manydynamic$(n)mod-dep-bad) extra-test-objs += $(tlsmod17a-modules:=.os) $(tlsmod18a-modules:=.os) \ tst-tlsalign-vars.o test-extras += tst-tlsmod17a tst-tlsmod18a tst-tlsalign-vars @@ -325,6 +332,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ tst-audit11mod1 tst-audit11mod2 tst-auditmod11 \ tst-audit12mod1 tst-audit12mod2 tst-audit12mod3 tst-auditmod12 \ tst-latepthreadmod $(tst-tls-many-dynamic-modules) \ + $(tst-tls-many-dynamic-modules-dep) \ + $(tst-tls-many-dynamic-modules-dep-bad) \ tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin \ tst-main1mod tst-absolute-sym-lib \ tst-absolute-zero-lib tst-big-note-lib tst-unwind-ctor-lib \ @@ -1812,10 +1821,63 @@ $(objpfx)tst-rtld-help.out: $(objpfx)ld.so $(evaluate-test) # Reuses tst-tls-many-dynamic-modules +$(patsubst %,$(objpfx)%.os,$(tst-tls-many-dynamic-modules-dep)): \ + $(objpfx)tst-tls-manydynamic%mod-dep.os : tst-tls-manydynamicmod.c + $(compile-command.c) \ + -DNAME=tls_global_$* -DSETTER=set_value_$* -DGETTER=get_value_$* +$(patsubst %,$(objpfx)%.os,$(tst-tls-many-dynamic-modules-dep-bad)): \ + $(objpfx)tst-tls-manydynamic%mod-dep-bad.os : tst-tls-manydynamicmod.c + $(compile-command.c) \ + -DNAME=tls_global_$* -DSETTER=set_value_$* -DGETTER=get_value_$* tst-tls20mod-bad.so-no-z-defs = yes +# Single dependency. +$(objpfx)tst-tls-manydynamic0mod-dep.so: $(objpfx)tst-tls-manydynamic1mod-dep.so +# Double dependencies. +$(objpfx)tst-tls-manydynamic2mod-dep.so: $(objpfx)tst-tls-manydynamic3mod-dep.so \ + $(objpfx)tst-tls-manydynamic4mod-dep.so +# Double dependencies with each dependency depent of another module. +$(objpfx)tst-tls-manydynamic5mod-dep.so: $(objpfx)tst-tls-manydynamic6mod-dep.so \ + $(objpfx)tst-tls-manydynamic7mod-dep.so +$(objpfx)tst-tls-manydynamic6mod-dep.so: $(objpfx)tst-tls-manydynamic8mod-dep.so +$(objpfx)tst-tls-manydynamic7mod-dep.so: $(objpfx)tst-tls-manydynamic8mod-dep.so +# Long chain with one double dependency in the middle +$(objpfx)tst-tls-manydynamic9mod-dep.so: $(objpfx)tst-tls-manydynamic10mod-dep.so \ + $(objpfx)tst-tls-manydynamic11mod-dep.so +$(objpfx)tst-tls-manydynamic10mod-dep.so: $(objpfx)tst-tls-manydynamic12mod-dep.so +$(objpfx)tst-tls-manydynamic12mod-dep.so: $(objpfx)tst-tls-manydynamic13mod-dep.so +# Long chain with two double depedencies in the middle +$(objpfx)tst-tls-manydynamic14mod-dep.so: $(objpfx)tst-tls-manydynamic15mod-dep.so +$(objpfx)tst-tls-manydynamic15mod-dep.so: $(objpfx)tst-tls-manydynamic16mod-dep.so \ + $(objpfx)tst-tls-manydynamic17mod-dep.so +$(objpfx)tst-tls-manydynamic16mod-dep.so: $(objpfx)tst-tls-manydynamic18mod-dep.so \ + $(objpfx)tst-tls-manydynamic19mod-dep.so +# Same but with an invalid module. +# Single dependency. +$(objpfx)tst-tls-manydynamic0mod-dep-bad.so: $(objpfx)tst-tls20mod-bad.so +# Double dependencies. +$(objpfx)tst-tls-manydynamic1mod-dep-bad.so: $(objpfx)tst-tls-manydynamic2mod-dep-bad.so \ + $(objpfx)tst-tls20mod-bad.so +# Double dependencies with each dependency depent of another module. +$(objpfx)tst-tls-manydynamic3mod-dep-bad.so: $(objpfx)tst-tls-manydynamic4mod-dep-bad.so \ + $(objpfx)tst-tls-manydynamic5mod-dep-bad.so +$(objpfx)tst-tls-manydynamic4mod-dep-bad.so: $(objpfx)tst-tls20mod-bad.so +$(objpfx)tst-tls-manydynamic5mod-dep-bad.so: $(objpfx)tst-tls20mod-bad.so +# Long chain with one double dependency in the middle +$(objpfx)tst-tls-manydynamic6mod-dep-bad.so: $(objpfx)tst-tls-manydynamic7mod-dep-bad.so \ + $(objpfx)tst-tls-manydynamic8mod-dep-bad.so +$(objpfx)tst-tls-manydynamic7mod-dep-bad.so: $(objpfx)tst-tls-manydynamic9mod-dep-bad.so +$(objpfx)tst-tls-manydynamic9mod-dep-bad.so: $(objpfx)tst-tls20mod-bad.so +# Long chain with two double depedencies in the middle +$(objpfx)tst-tls-manydynamic10mod-dep-bad.so: $(objpfx)tst-tls-manydynamic11mod-dep-bad.so +$(objpfx)tst-tls-manydynamic11mod-dep-bad.so: $(objpfx)tst-tls-manydynamic12mod-dep-bad.so \ + $(objpfx)tst-tls-manydynamic13mod-dep-bad.so +$(objpfx)tst-tls-manydynamic12mod-dep-bad.so: $(objpfx)tst-tls-manydynamic14mod-dep-bad.so \ + $(objpfx)tst-tls20mod-bad.so $(objpfx)tst-tls20: $(shared-thread-library) $(objpfx)tst-tls20.out: $(objpfx)tst-tls20mod-bad.so \ - $(tst-tls-many-dynamic-modules:%=$(objpfx)%.so) + $(tst-tls-many-dynamic-modules:%=$(objpfx)%.so) \ + $(tst-tls-many-dynamic-modules-dep:%=$(objpfx)%.so) \ + $(tst-tls-many-dynamic-modules-dep-bad:%=$(objpfx)%.so) \ # Reuses tst-tls-many-dynamic-modules $(objpfx)tst-tls21: $(shared-thread-library) diff --git a/elf/dl-close.c b/elf/dl-close.c index 3720e47dd1..f39001cab9 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -77,8 +77,6 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, object that wasn't fully set up. */ if (__glibc_likely (old_map != NULL)) { - assert (old_map->l_tls_modid == idx); - /* Mark the entry as unused. These can be read concurrently. */ atomic_store_relaxed (&listp->slotinfo[idx - disp].gen, GL(dl_tls_generation) + 1); @@ -88,7 +86,11 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, /* If this is not the last currently used entry no need to look further. */ if (idx != GL(dl_tls_max_dtv_idx)) - return true; + { + /* There is an unused dtv entry in the middle. */ + GL(dl_tls_dtv_gaps) = true; + return true; + } } while (idx - disp > (disp == 0 ? 1 + GL(dl_tls_static_nelem) : 0)) diff --git a/elf/dl-load.c b/elf/dl-load.c index a08df001af..650e4edc35 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1498,7 +1498,7 @@ cannot enable executable stack as shared object requires"); not set up TLS data structures, so don't use them now. */ || __glibc_likely (GL(dl_tls_dtv_slotinfo_list) != NULL))) /* Assign the next available module ID. */ - l->l_tls_modid = _dl_next_tls_modid (); + _dl_assign_tls_modid (l); #ifdef DL_AFTER_LOAD DL_AFTER_LOAD (l); diff --git a/elf/dl-open.c b/elf/dl-open.c index a066f39bd0..d2240d8747 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -899,16 +899,6 @@ no more namespaces available for dlmopen()")); state if relocation failed, for example. */ if (args.map) { - /* Maybe some of the modules which were loaded use TLS. - Since it will be removed in the following _dl_close call - we have to mark the dtv array as having gaps to fill the - holes. This is a pessimistic assumption which won't hurt - if not true. There is no need to do this when we are - loading the auditing DSOs since TLS has not yet been set - up. */ - if ((mode & __RTLD_AUDIT) == 0) - GL(dl_tls_dtv_gaps) = true; - _dl_close_worker (args.map, true); /* All l_nodelete_pending objects should have been deleted diff --git a/elf/dl-tls.c b/elf/dl-tls.c index 2b5161d10a..423e380f7c 100644 --- a/elf/dl-tls.c +++ b/elf/dl-tls.c @@ -126,8 +126,8 @@ oom (void) } -size_t -_dl_next_tls_modid (void) +void +_dl_assign_tls_modid (struct link_map *l) { size_t result; @@ -157,7 +157,11 @@ _dl_next_tls_modid (void) } if (result - disp < runp->len) - break; + { + /* Mark the entry as used, so any dependency see it. */ + atomic_store_relaxed (&runp->slotinfo[result - disp].map, l); + break; + } disp += runp->len; } @@ -184,17 +188,14 @@ _dl_next_tls_modid (void) atomic_store_relaxed (&GL(dl_tls_max_dtv_idx), result); } - return result; + l->l_tls_modid = result; } size_t _dl_count_modids (void) { - /* It is rare that we have gaps; see elf/dl-open.c (_dl_open) where - we fail to load a module and unload it leaving a gap. If we don't - have gaps then the number of modids is the current maximum so - return that. */ + /* The count is the max unless dlclose or failed dlopen created gaps. */ if (__glibc_likely (!GL(dl_tls_dtv_gaps))) return GL(dl_tls_max_dtv_idx); diff --git a/elf/rtld.c b/elf/rtld.c index fbbd60b446..160faaf5ab 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -1722,7 +1722,7 @@ dl_main (const ElfW(Phdr) *phdr, /* Add the dynamic linker to the TLS list if it also uses TLS. */ if (GL(dl_rtld_map).l_tls_blocksize != 0) /* Assign a module ID. Do this before loading any audit modules. */ - GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid (); + _dl_assign_tls_modid (&GL(dl_rtld_map)); audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_AUDIT); audit_list_add_dynamic_tag (&state.audit_list, main_map, DT_DEPAUDIT); diff --git a/elf/tst-tls20.c b/elf/tst-tls20.c index 9977ec8032..f0db2d1a27 100644 --- a/elf/tst-tls20.c +++ b/elf/tst-tls20.c @@ -16,12 +16,14 @@ License along with the GNU C Library; if not, see . */ +#include #include #include #include #include #include #include +#include #include #include @@ -59,28 +61,75 @@ access (int i) char *buf = xasprintf ("tls_global_%02d", i); dlerror (); int *p = dlsym (mod[i], buf); - printf ("mod[%d]: &tls = %p\n", i, p); + if (test_verbose) + printf ("mod[%d]: &tls = %p\n", i, p); if (p == NULL) FAIL_EXIT1 ("dlsym failed: %s\n", dlerror ()); + TEST_COMPARE (*p, 0); ++*p; free (buf); } +static void +access_mod (const char *modname, void *mod, int i) +{ + char *modsym = xasprintf ("tls_global_%d", i); + dlerror (); + int *p = dlsym (mod, modsym); + if (test_verbose) + printf ("%s: &tls = %p\n", modname, p); + if (p == NULL) + FAIL_EXIT1 ("dlsym failed: %s\n", dlerror ()); + TEST_COMPARE (*p, 0); + ++*p; + free (modsym); +} + +static void +access_dep (int i) +{ + char *modname = xasprintf ("tst-tls-manydynamic%dmod-dep.so", i); + void *moddep = xdlopen (modname, RTLD_LAZY); + access_mod (modname, moddep, i); + free (modname); + xdlclose (moddep); +} + +struct start_args +{ + const char *modname; + void *mod; + int modi; + int ndeps; + const int *deps; +}; + static void * start (void *a) { + struct start_args *args = a; + for (int i = 0; i < NMOD; i++) if (mod[i] != NULL) access (i); + + if (args != NULL) + { + access_mod (args->modname, args->mod, args->modi); + for (int n = 0; n < args->ndeps; n++) + access_dep (args->deps[n]); + } + return 0; } -static int -do_test (void) +/* This test gaps with shared libraries with dynamic TLS that has no + dependencies. The DTV gap is set with by trying to load an invalid + module, the entry should be used on the dlopen. */ +static void +do_test_no_depedency (void) { - int i; - - for (i = 0; i < NMOD; i++) + for (int i = 0; i < NMOD; i++) { load_mod (i); /* Bump the generation of mod[0] without using new dtv slot. */ @@ -91,8 +140,220 @@ do_test (void) pthread_t t = xpthread_create (0, start, 0); xpthread_join (t); } - for (i = 0; i < NMOD; i++) + for (int i = 0; i < NMOD; i++) unload_mod (i); +} + +/* The following test check DTV gaps handling with shared libraries that has + dependencies. It defines 5 different sets: + + 1. Single dependency: + mod0 -> mod1 + 2. Double dependency: + mod2 -> [mod3,mod4] + 3. Double dependency with each dependency depent of another module: + mod5 -> [mod6,mod7] -> mod8 + 4. Long chain with one double dependency in the middle: + mod9 -> [mod10, mod11] -> mod12 -> mod13 + 5. Long chain with two double depedencies in the middle: + mod15 -> mod15 -> [mod16, mod17] + mod15 -> [mod18, mod19] + + This does not cover all the possible gaps and configuration, but it + should check if different dynamic shared sets are placed correctly in + different gaps configurations. */ + +static int +nmodules (uint32_t v) +{ + unsigned int r = 0; + while (v >>= 1) + r++; + return r + 1; +} + +static inline bool +is_mod_set (uint32_t g, uint32_t n) +{ + return (1U << (n - 1)) & g; +} + +static void +print_gap (uint32_t g) +{ + if (!test_verbose) + return; + printf ("gap: "); + int nmods = nmodules (g); + for (int n = 1; n <= nmods; n++) + printf ("%c", ((1 << (n - 1)) & g) == 0 ? 'G' : 'M'); + printf ("\n"); +} + +static void +do_test_dependency (void) +{ + /* Maps the module and its dependencies, use thread to access the TLS on + each loaded module. */ + static const int tlsmanydeps0[] = { 1 }; + static const int tlsmanydeps1[] = { 3, 4 }; + static const int tlsmanydeps2[] = { 6, 7, 8 }; + static const int tlsmanydeps3[] = { 10, 11, 12 }; + static const int tlsmanydeps4[] = { 15, 16, 17, 18, 19 }; + static const struct tlsmanydeps_t + { + int modi; + int ndeps; + const int *deps; + } tlsmanydeps[] = + { + { 0, array_length (tlsmanydeps0), tlsmanydeps0 }, + { 2, array_length (tlsmanydeps1), tlsmanydeps1 }, + { 5, array_length (tlsmanydeps2), tlsmanydeps2 }, + { 9, array_length (tlsmanydeps3), tlsmanydeps3 }, + { 14, array_length (tlsmanydeps4), tlsmanydeps4 }, + }; + + /* The gap configuration is defined as a bitmap: the bit set represents a + loaded module prior the tests execution, while a bit unsed is a module + unloaded. Not all permtation will show gaps, but it is simpler than + define each one independently. */ + for (uint32_t g = 0; g < 64; g++) + { + print_gap (g); + int nmods = nmodules (g); + + int mods[nmods]; + /* We use '0' as indication for a gap, to avoid the dlclose on iteration + cleanup. */ + for (int n = 1; n <= nmods; n++) + { + load_mod (n); + mods[n] = n; + } + for (int n = 1; n <= nmods; n++) + { + if (!is_mod_set (g, n)) + { + unload_mod (n); + mods[n] = 0; + } + } + + for (int t = 0; t < array_length (tlsmanydeps); t++) + { + char *moddepname = xasprintf ("tst-tls-manydynamic%dmod-dep.so", + tlsmanydeps[t].modi); + void *moddep = xdlopen (moddepname, RTLD_LAZY); + + /* Access TLS in all loaded modules. */ + struct start_args args = + { + moddepname, + moddep, + tlsmanydeps[t].modi, + tlsmanydeps[t].ndeps, + tlsmanydeps[t].deps + }; + pthread_t t = xpthread_create (0, start, &args); + xpthread_join (t); + + free (moddepname); + xdlclose (moddep); + } + + for (int n = 1; n <= nmods; n++) + if (mods[n] != 0) + unload_mod (n); + } +} + +/* The following test check DTV gaps handling with shared libraries that has + invalid dependencies. It defines 5 different sets: + + 1. Single dependency: + mod0 -> invalid + 2. Double dependency: + mod1 -> [mod2,invalid] + 3. Double dependency with each dependency depent of another module: + mod3 -> [mod4,mod5] -> invalid + 4. Long chain with one double dependency in the middle: + mod6 -> [mod7, mod8] -> mod12 -> invalid + 5. Long chain with two double depedencies in the middle: + mod10 -> mod11 -> [mod12, mod13] + mod12 -> [mod14, invalid] + + This does not cover all the possible gaps and configuration, but it + should check if different dynamic shared sets are placed correctly in + different gaps configurations. */ + +static void +do_test_invalid_dependency (bool bind_now) +{ + static const int tlsmanydeps[] = { 0, 1, 3, 6, 10 }; + + /* The gap configuration is defined as a bitmap: the bit set represents a + loaded module prior the tests execution, while a bit unsed is a module + unloaded. Not all permtation will show gaps, but it is simpler than + define each one independently. */ + for (uint32_t g = 0; g < 64; g++) + { + print_gap (g); + int nmods = nmodules (g); + + int mods[nmods]; + /* We use '0' as indication for a gap, to avoid the dlclose on iteration + cleanup. */ + for (int n = 1; n <= nmods; n++) + { + load_mod (n); + mods[n] = n; + } + for (int n = 1; n <= nmods; n++) + { + if (!is_mod_set (g, n)) + { + unload_mod (n); + mods[n] = 0; + } + } + + for (int t = 0; t < array_length (tlsmanydeps); t++) + { + char *moddepname = xasprintf ("tst-tls-manydynamic%dmod-dep-bad.so", + tlsmanydeps[t]); + void *moddep; + if (bind_now) + { + moddep = dlopen (moddepname, RTLD_NOW); + TEST_VERIFY (moddep == 0); + } + else + moddep = dlopen (moddepname, RTLD_LAZY); + + /* Access TLS in all loaded modules. */ + pthread_t t = xpthread_create (0, start, NULL); + xpthread_join (t); + + free (moddepname); + if (!bind_now) + xdlclose (moddep); + } + + for (int n = 1; n <= nmods; n++) + if (mods[n] != 0) + unload_mod (n); + } +} + +static int +do_test (void) +{ + do_test_no_depedency (); + do_test_dependency (); + do_test_invalid_dependency (true); + do_test_invalid_dependency (false); + return 0; } diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index 176394de4d..9c15259236 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -1171,8 +1171,8 @@ extern ElfW(Addr) _dl_sysdep_start (void **start_argptr, extern void _dl_sysdep_start_cleanup (void) attribute_hidden; -/* Determine next available module ID. */ -extern size_t _dl_next_tls_modid (void) attribute_hidden; +/* Determine next available module ID and set the L l_tls_modid. */ +extern void _dl_assign_tls_modid (struct link_map *l) attribute_hidden; /* Count the modules with TLS segments. */ extern size_t _dl_count_modids (void) attribute_hidden;