From patchwork Wed Jun 17 14:00:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vivek Dasmohapatra X-Patchwork-Id: 1311236 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=libc-alpha-bounces@sourceware.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=sourceware.org Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.a=rsa-sha256 header.s=default header.b=rnZI+XRE; dkim-atps=neutral Received: from sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 49n6HB1WHRz9sRR for ; Thu, 18 Jun 2020 00:03:34 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id CB8493898531; Wed, 17 Jun 2020 14:00:47 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CB8493898531 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1592402447; bh=Zhx8S7LFpoUWwGTWNQVl+HPBcqXyvLMneOzwTlO4A30=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=rnZI+XRERLmOHlqxfNxX1KY77gXGzzBQ/xRf9acW6M0AoUokr/p3ZaW7YZ+MPgp2b NlIkH9Vl2g4G4brY+X0yclT952f2WwJhEvV27ahu/IW1aHxGCydeFPVtCc1rHxCAz3 ZLgPzPK/IMgkXV4WVht0dX5fZvn2c9UNw8BYMg6Y= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [46.235.227.227]) by sourceware.org (Postfix) with ESMTPS id 047653893668 for ; Wed, 17 Jun 2020 14:00:39 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 047653893668 Received: from noise.collabora.co.uk (unknown [IPv6:2001:4d48:ad56:3000:88ff:22ff:81f3:30c8]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: vivek) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id DB9C82A3FCB for ; Wed, 17 Jun 2020 15:00:35 +0100 (BST) To: libc-alpha@sourceware.org Subject: [RFC][PATCH v5 16/16] Add dlmopen / RTLD_SHARED tests Date: Wed, 17 Jun 2020 15:00:24 +0100 Message-Id: <20200617140024.12777-17-vivek@collabora.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20200617140024.12777-1-vivek@collabora.com> References: <20200617140024.12777-1-vivek@collabora.com> X-Spam-Status: No, score=-14.3 required=5.0 tests=BAYES_00, GIT_PATCH_0, KAM_DMARC_STATUS, RCVD_IN_DNSWL_NONE, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) 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: =?utf-8?q?Vivek_Das=C2=A0Mohapatra_via_Libc-alpha?= From: Vivek Dasmohapatra Reply-To: =?utf-8?q?Vivek_Das=C2=A0Mohapatra?= Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" --- elf/Makefile | 42 +- elf/tst-dlmopen-common.h | 15 + elf/tst-dlmopen-main.h | 899 +++++++++++++++++++++++++++++++++++++++ elf/tst-dlmopen-modules.h | 3 + elf/tst-dlmopen-rtld-shared1.c | 74 ++++ elf/tst-dlmopen-rtld-shared2.c | 76 ++++ elf/tst-dlmopen-rtld-shared3.c | 53 +++ elf/tst-dlmopen-rtld-shared4.c | 24 ++ elf/tst-dlmopen-rtld-shared5.c | 35 ++ elf/tst-dlmopen-rtld-shared6.c | 46 ++ elf/tst-dlmopen-rtld-unique1.c | 96 +++++ elf/tst-dlmopen-rtld-unique2.c | 35 ++ elf/tst-dlmopen-rtld-unique3.c | 23 + elf/tst-dlmopen-rtld-unique4.c | 24 ++ elf/tst-dlmopen-sharedmod-norm.c | 11 + elf/tst-dlmopen-sharedmod-uniq.c | 11 + elf/tst-dlmopen-std-do-test.h | 11 + 17 files changed, 1477 insertions(+), 1 deletion(-) create mode 100644 elf/tst-dlmopen-common.h create mode 100644 elf/tst-dlmopen-main.h create mode 100644 elf/tst-dlmopen-modules.h create mode 100644 elf/tst-dlmopen-rtld-shared1.c create mode 100644 elf/tst-dlmopen-rtld-shared2.c create mode 100644 elf/tst-dlmopen-rtld-shared3.c create mode 100644 elf/tst-dlmopen-rtld-shared4.c create mode 100644 elf/tst-dlmopen-rtld-shared5.c create mode 100644 elf/tst-dlmopen-rtld-shared6.c create mode 100644 elf/tst-dlmopen-rtld-unique1.c create mode 100644 elf/tst-dlmopen-rtld-unique2.c create mode 100644 elf/tst-dlmopen-rtld-unique3.c create mode 100644 elf/tst-dlmopen-rtld-unique4.c create mode 100644 elf/tst-dlmopen-sharedmod-norm.c create mode 100644 elf/tst-dlmopen-sharedmod-uniq.c create mode 100644 elf/tst-dlmopen-std-do-test.h diff --git a/elf/Makefile b/elf/Makefile index 632a4d8b0f..88e500cad4 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -177,6 +177,20 @@ tests-static += tst-tls9-static tst-tls9-static-ENV = \ LD_LIBRARY_PATH=$(objpfx):$(common-objpfx):$(common-objpfx)dlfcn +dlmopen-rtld-tests-norm := \ + tst-dlmopen-rtld-shared1 \ + tst-dlmopen-rtld-shared2 \ + tst-dlmopen-rtld-shared3 \ + tst-dlmopen-rtld-shared4 \ + tst-dlmopen-rtld-shared5 \ + tst-dlmopen-rtld-shared6 + +dlmopen-rtld-tests-uniq := \ + tst-dlmopen-rtld-unique1 \ + tst-dlmopen-rtld-unique2 \ + tst-dlmopen-rtld-unique3 \ + tst-dlmopen-rtld-unique4 + tests += restest1 preloadtest loadfail multiload origtest resolvfail \ constload1 order noload filter \ reldep reldep2 reldep3 reldep4 nodelete nodelete2 \ @@ -201,7 +215,9 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ tst-unwind-ctor tst-unwind-main tst-audit13 \ tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-tlsmodid \ tst-dlopen-self tst-auditmany tst-initfinilazyfail tst-dlopenfail \ - tst-dlopenfail-2 + tst-dlopenfail-2 \ + $(dlmopen-rtld-tests-norm) \ + $(dlmopen-rtld-tests-uniq) # reldep9 tests-internal += loadtest unload unload2 circleload1 \ neededtest neededtest2 neededtest3 neededtest4 \ @@ -262,6 +278,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ $(modules-execstack-$(have-z-execstack)) \ tst-dlopenrpathmod tst-deep1mod1 tst-deep1mod2 tst-deep1mod3 \ tst-dlmopen1mod tst-auditmod1 \ + tst-dlmopen-sharedmod-norm \ + tst-dlmopen-sharedmod-uniq \ unload3mod1 unload3mod2 unload3mod3 unload3mod4 \ unload4mod1 unload4mod2 unload4mod3 unload4mod4 \ unload6mod1 unload6mod2 unload6mod3 \ @@ -773,6 +791,8 @@ tst-nodelete-uniquemod.so-no-z-defs = yes tst-nodelete-rtldmod.so-no-z-defs = yes tst-nodelete-zmod.so-no-z-defs = yes tst-nodelete2mod.so-no-z-defs = yes +tst-dlmopen-sharedmod-norm.so-no-z-defs = yes +tst-dlmopen-sharedmod-uniq.so-no-z-defs = yes ifeq ($(build-shared),yes) # Build all the modules even when not actually running test programs. @@ -1245,6 +1265,26 @@ $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so $(objpfx)tst-dlmopen3: $(libdl) $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so +LDFLAGS-tst-dlmopen-sharedmod-uniq.so = -Wl,--unique-dso +$(objpfx)tst-dlmopen-sharedmod-norm.so: $(libdl) +$(objpfx)tst-dlmopen-sharedmod-uniq.so: $(libdl) + +dlmopen-rtld-tests-norm-executables := \ + $(foreach x,$(dlmopen-rtld-tests-norm),$(objpfx)$(x)) +dlmopen-rtld-tests-norm-out := \ + $(foreach x,$(dlmopen-rtld-tests-norm),$(objpfx)$(x).out) + +dlmopen-rtld-tests-uniq-executables := \ + $(foreach x,$(dlmopen-rtld-tests-uniq),$(objpfx)$(x)) +dlmopen-rtld-tests-uniq-out := \ + $(foreach x,$(dlmopen-rtld-tests-uniq),$(objpfx)$(x).out) + +$(dlmopen-rtld-tests-norm-executables): $(libdl) +$(dlmopen-rtld-tests-norm-out): $(objpfx)tst-dlmopen-sharedmod-norm.so + +$(dlmopen-rtld-tests-uniq-executables): $(libdl) +$(dlmopen-rtld-tests-uniq-out): $(objpfx)tst-dlmopen-sharedmod-uniq.so + $(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so diff --git a/elf/tst-dlmopen-common.h b/elf/tst-dlmopen-common.h new file mode 100644 index 0000000000..f93ceb5465 --- /dev/null +++ b/elf/tst-dlmopen-common.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include +#include + +typedef struct +{ + const char *name; + void *free; +} dlmopen_testresult; + +typedef dlmopen_testresult * (*dlmopen_testfunc) (void); + diff --git a/elf/tst-dlmopen-main.h b/elf/tst-dlmopen-main.h new file mode 100644 index 0000000000..80bf3a6d8b --- /dev/null +++ b/elf/tst-dlmopen-main.h @@ -0,0 +1,899 @@ +#pragma once + +#include "tst-dlmopen-common.h" +#include +#include + +#define DSO_DUMMY "$ORIGIN/tst-dlmopen-sharedmod-noop.so" +#define DSO_NORMAL "$ORIGIN/tst-dlmopen-sharedmod-norm.so" +#define DSO_UNIQUE "$ORIGIN/tst-dlmopen-sharedmod-uniq.so" +#define DSO_TESTFN "rtld_shared_testfunc" +#define DSO_NAMESTUB "tst-dlmopen-sharedmod-" +#define MAX_NS 16 +#define POISON 0xabad1dea + +#define LM_ID_NONE -2 + +#define END_TESTS { .name = NULL }, + +#define ERROR(test, fmt, ...) \ + ({ if (last_test != test) \ + printf ("FAILED: %s (%s):\n", test->name, test->desc); \ + printf ("%s @ %d - " fmt, __FILE__, __LINE__, ##__VA_ARGS__); \ + last_test = test; }) + +static void *last_test; + +typedef enum + { + NONE = 0, + DSO = 1, + PROXY = 2, + NEW = 4, + } dso_type; + +typedef struct +{ + const char *name; + const char *desc; + int is_prep_stage; + const char *dso_name; + int failure; + + struct + { + const char *dso_path; + Lmid_t ns; + int flags; + } args; + + dso_type preloaded[MAX_NS]; + dso_type loaded[MAX_NS]; + dso_type handle_type; + Lmid_t handle_ns; +} dlmopen_test_spec; + +struct r_scope_elem +{ + struct tst_lm **r_list; + unsigned int r_nlist; +}; + +/* This is a copy of the first part of the internal definition of + struct link_map from . We use it to check some + expected internal state that's not readily accessible via public APIs. + */ +struct tst_lm + { + /* These first few members are part of the protocol with the debugger. + This is the same format used in SVR4. */ + + ElfW(Addr) l_addr; /* Difference between the address in the ELF + file and the addresses in memory. */ + char *l_name; /* Absolute file name object was found in. */ + ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. */ + struct tst_lm *l_next, *l_prev; /* Chain of loaded objects. */ + + /* All following members are internal to the dynamic linker. + They may change without notice. */ + + /* This is an element which is only ever different from a pointer to + the very same copy of this type when: + - A shallow copy of ld.so is placed in namespaces other than LM_ID_BASE. + - An object is proxied into a namespace by dlmopen with RTLD_SHARED. */ + struct tst_lm *l_real; + + /* Number of the namespace this link map belongs to. */ + Lmid_t l_ns; + + struct libname_list *l_libname; + /* Indexed pointers to dynamic section. + [0,DT_NUM) are indexed by the processor-independent tags. + [DT_NUM,DT_NUM+DT_THISPROCNUM) are indexed by the tag minus DT_LOPROC. + [DT_NUM+DT_THISPROCNUM,DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM) are + indexed by DT_VERSIONTAGIDX(tagvalue). + [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM, + DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM) are indexed by + DT_EXTRATAGIDX(tagvalue). + [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM, + DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM) are + indexed by DT_VALTAGIDX(tagvalue) and + [DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM, + DT_NUM+DT_THISPROCNUM+DT_VERSIONTAGNUM+DT_EXTRANUM+DT_VALNUM+DT_ADDRNUM) + are indexed by DT_ADDRTAGIDX(tagvalue), see . */ + + ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGNUM + + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM]; + const ElfW(Phdr) *l_phdr; /* Pointer to program header table in core. */ + ElfW(Addr) l_entry; /* Entry point location. */ + ElfW(Half) l_phnum; /* Number of program header entries. */ + ElfW(Half) l_ldnum; /* Number of dynamic segment entries. */ + + /* Array of DT_NEEDED dependencies and their dependencies, in + dependency order for symbol lookup (with and without + duplicates). There is no entry before the dependencies have + been loaded. */ + struct r_scope_elem l_searchlist; + + /* We need a special searchlist to process objects marked with + DT_SYMBOLIC. */ + struct r_scope_elem l_symbolic_searchlist; + + /* Dependent object that first caused this object to be loaded. */ + struct link_map *l_loader; + + /* Array with version names. */ + struct r_found_version *l_versions; + unsigned int l_nversions; + + /* Symbol hash table. */ + Elf_Symndx l_nbuckets; + Elf32_Word l_gnu_bitmask_idxbits; + Elf32_Word l_gnu_shift; + const ElfW(Addr) *l_gnu_bitmask; + union + { + const Elf32_Word *l_gnu_buckets; + const Elf_Symndx *l_chain; + }; + union + { + const Elf32_Word *l_gnu_chain_zero; + const Elf_Symndx *l_buckets; + }; + + unsigned int l_direct_opencount; /* Reference count for dlopen/dlclose. */ + enum /* Where this object came from. */ + { + lt_executable, /* The main executable program. */ + lt_library, /* Library needed by main executable. */ + lt_loaded /* Extra run-time loaded shared object. */ + } l_type:2; + unsigned int l_relocated:1; /* Nonzero if object's relocations done. */ + unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */ + unsigned int l_global:1; /* Nonzero if object in _dl_global_scope. */ + unsigned int l_proxy:1; /* Nonzero if object is a shallow copy. */ + unsigned int l_reserved:2; /* Reserved for internal use. */ + unsigned int l_phdr_allocated:1; /* Nonzero if the data structure pointed + to by `l_phdr' is allocated. */ + unsigned int l_soname_added:1; /* Nonzero if the SONAME is for sure in + the l_libname list. */ + unsigned int l_faked:1; /* Nonzero if this is a faked descriptor + without associated file. */ + unsigned int l_need_tls_init:1; /* Nonzero if GL(dl_init_static_tls) + should be called on this link map + when relocation finishes. */ + unsigned int l_auditing:1; /* Nonzero if the DSO is used in auditing. */ + unsigned int l_audit_any_plt:1; /* Nonzero if at least one audit module + is interested in the PLT interception.*/ + unsigned int l_removed:1; /* Nozero if the object cannot be used anymore + since it is removed. */ + unsigned int l_contiguous:1; /* Nonzero if inter-segment holes are + mprotected or if no holes are present at + all. */ + unsigned int l_symbolic_in_local_scope:1; /* Nonzero if l_local_scope + during LD_TRACE_PRELINKING=1 + contains any DT_SYMBOLIC + libraries. */ + unsigned int l_free_initfini:1; /* Nonzero if l_initfini can be + freed, ie. not allocated with + the dummy malloc in ld.so. */ + }; + +typedef struct { void *handle; Lmid_t ns; } test_handle; +static test_handle cached_handles[32]; + +static void +cache_test_handle (void *handle, Lmid_t ns) +{ + int i; + + for (i = 0; i < sizeof(cached_handles)/sizeof(test_handle); i++) + if (cached_handles[i].handle == NULL) + { + cached_handles[i].handle = handle; + cached_handles[i].ns = ns; + break; + } +} + +__attribute__((unused)) +static struct tst_lm * +link_map_of_dl_handle (void *handle) +{ + struct tst_lm *lm = NULL; + + if (dlinfo (handle, RTLD_DI_LINKMAP, &lm) == 0) + return lm; + + printf ("dlinfo (LINKMAP) for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + + return NULL; +} + +__attribute__((unused)) +static Lmid_t +ns_of_dl_handle (void *handle) +{ + Lmid_t ns = 0; + + if (dlinfo (handle, RTLD_DI_LMID, &ns) == 0) + return ns; + + printf ("dlinfo (LMID) for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + + return -1; +} + +__attribute__((unused)) +static Lmid_t real_ns_of_dl_handle (void *handle) +{ + Lmid_t ns = 0; + struct tst_lm *lm = link_map_of_dl_handle (handle); + + if (lm == NULL) + return -1; + + // printf ("DEBUG: handle %p; LM %p; proxy: %d / %p\n", + // handle, lm, lm->l_proxy, lm->l_real); + + if (lm->l_proxy) + { + // printf ("DEBUG: proxy %p vs real %p\n", lm, lm->l_real); + ns = ns_of_dl_handle ((void *) lm->l_real); + } + else + ns = ns_of_dl_handle (handle); + + return ns; +} + +__attribute__((unused)) +static const char *str_soname (const char *name) +{ + char *slash = NULL; + + if (name == NULL) + return NULL; + + if ((slash = strrchr (name, '/'))) + return ++slash; + else + return name; +} + +__attribute__((unused)) +static const char *lm_name (struct tst_lm *lm) +{ + if (lm) + return lm->l_name; + + return NULL; +} + + +static int dlm_dso_is_loaded (void *handle) +{ + if (handle != RTLD_DEFAULT) + { +#ifdef DEBUG_DSO_LOADCHECK + Dl_info sinfo = {}; +#endif + + if (((struct tst_lm *) handle)->l_type != lt_loaded) + return 0; + +#ifdef DEBUG_DSO_LOADCHECK + printf ("checking %p %s for %s\n", + handle, ((struct tst_lm *)handle)->l_name, DSO_TESTFN); +#endif + + void *symbol = dlsym (handle, DSO_TESTFN); + +#ifdef DEBUG_DSO_LOADCHECK + dladdr (symbol, &sinfo); + printf (" -> %s (in %s (%p))\n", + sinfo.dli_fname, + sinfo.dli_sname, + sinfo.dli_saddr); +#endif + + return symbol ? 1 : 0; + } + + for (int i = 0; i < sizeof(cached_handles)/sizeof(test_handle); i++) + { + if (cached_handles[i].handle == NULL) + break; + + if (((struct tst_lm *) cached_handles[i].handle)->l_type != lt_loaded) + continue; + +#ifdef DEBUG_DSO_LOADCHECK + printf ("checking %p %s for %s\n", + cached_handles[i].handle, + ((struct tst_lm *)cached_handles[i].handle)->l_name, + DSO_TESTFN); +#endif + + if (dlsym (cached_handles[i].handle, DSO_TESTFN) != NULL) + return 1; + } + + return 0; +} + +static int call_testfunc (dlmopen_test_spec *test, void *handle, Lmid_t expect_ns) +{ + Dl_info dli = {}; + struct tst_lm *lm = NULL; + dlmopen_testfunc func = NULL; + dlmopen_testresult *result = NULL; + + if (handle != RTLD_DEFAULT) + func = dlsym (handle, DSO_TESTFN); + + if (func == NULL) + { + ERROR (test, "test function %s not found\n", DSO_TESTFN); + return 0; + } + + result = (func)(); + + if (result == NULL) + { + ERROR (test, "test function %s returned NULL\n", DSO_TESTFN); + return 0; + } + + dladdr1 (result->free, &dli, (void **)&lm, RTLD_DL_LINKMAP); + + if (lm == NULL) + { + ERROR (test, "free() implementation from test module is invalid\n"); + return 0; + } + + if (lm->l_ns != expect_ns) + { + ERROR (test, + "free() function from test module was from ns %d, expected %d\n", + (int)lm->l_ns, (int)expect_ns); + return 0; + } + + printf ("%s: %s: %s in ns %d using free() from ns %d: OK\n", + test->name, test->args.dso_path, DSO_TESTFN, + (int)test->handle_ns, (int)lm->l_ns); + + return 1; +} + +static void *link_map_snapshot_array (void *handle, void *func, Lmid_t ns, size_t *len) +{ + struct tst_lm *lm = NULL; + struct tst_lm *start = NULL; + + if (len) + *len = 0; + + if (handle != NULL) + { + dlinfo (handle, RTLD_DI_LINKMAP, &lm); + } + else if (func != NULL) + { + Dl_info dli = {}; + + dladdr1 (func, &dli, (void **)&lm, RTLD_DL_LINKMAP); + } + else if (ns >= LM_ID_BASE) + { + for (int i = 0; i < sizeof(cached_handles)/sizeof(test_handle); i++) + { + if (cached_handles[i].handle == NULL) + break; + + if (cached_handles[i].ns != ns) + continue; + + dlinfo (cached_handles[i].handle, RTLD_DI_LINKMAP, &lm); + break; + } + } + + if (lm == NULL) + return NULL; + + start = lm; + + while (start->l_prev) + start = start->l_prev; + + size_t lm_size = 0; + + for (lm = start; lm; lm = lm->l_next) + lm_size++; + + struct tst_lm **lm_list = calloc (lm_size + 1, sizeof (struct tst_lm *)); + + if (len) + *len = lm_size; + + int i = 0; + + for (lm = start; lm; lm = lm->l_next) + lm_list[i++] = lm; + lm_list[i] = NULL; + + return lm_list; +} + +__attribute__((unused)) +static int search_link_map_array (struct tst_lm **lma, size_t len, void *handle) +{ + if (lma == NULL) + return 0; + + if (len == 0) + return 0; + + struct tst_lm *target = link_map_of_dl_handle (handle); + + for (int i = 0; i < len; i++) + { + //printf ("searching for %p in <%p>[%d] == %p\n", + // handle, lma, i, lma[i]); + if (handle == (struct tst_lm *)(lma[i])) + return 1; + + if (target->l_proxy) + if (target->l_real == (struct tst_lm *)(lma[i])) + return 1; + } + + return 0; +} + +#ifdef DEBUG_DSO_SEARCH +#define TRACE2(fmt, ...) \ + printf (" find-test-dso (%s @ %d): " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#else +#define TRACE2(fmt, ...) +#endif + +static struct tst_lm * +find_test_dso_in_link_map_array (struct tst_lm **lma, size_t len) +{ + if (lma == NULL) + return NULL; + + if (len == 0) + return NULL; + + for (int i = 0; i < len; i++) + { + if (!lma[i] || !lma[i]->l_name) + continue; + + TRACE2 ("%p [%d/%d] %s", lma, i, (int)len - 1, + lma[i] ? (lma[i]->l_name ?: "???.so") : "NULL" ); + + if (lma[i] && lma[i]->l_name) + if (strstr (lma[i]->l_name, DSO_NAMESTUB)) + if (dlsym (lma[i], DSO_TESTFN) != NULL) + return (struct tst_lm *)lma[i]; + } + + return NULL; +} + +__attribute__((unused)) +static void *link_map_list (void *handle, void *func, int *len, Lmid_t *ns) +{ + struct tst_lm *lm = NULL; + struct tst_lm *start = NULL; + + if (len) + *len = 0; + + if (handle != NULL) + { + dlinfo (handle, RTLD_DI_LINKMAP, &lm); + } + else if (func != NULL) + { + Dl_info dli = {}; + + dladdr1 (func, &dli, (void **)&lm, RTLD_DL_LINKMAP); + } + + if (lm == NULL) + return NULL; + + if (ns) + *ns = lm->l_ns; + + // rewind to start of link map list: + start = lm; + + while (start->l_prev) + start = start->l_prev; + + size_t lm_size = 0; + + for (lm = start; lm; lm = lm->l_next) + lm_size++; + + if (len) + *len = lm_size; + + return start; +} + +#ifdef DEBUG_DLMOPEN_TEST_WRAPPER +#define TRACE(fmt, ...) \ + printf ("%s (%s @ %d): " fmt "\n", test->name, __FILE__, __LINE__, ##__VA_ARGS__) +#else +#define TRACE(fmt, ...) +#endif + +static int process_test_spec (dlmopen_test_spec *test) +{ + void *handle = NULL; + size_t lm_before_len[MAX_NS] = { 0 }; + size_t lm_after_len[MAX_NS] = { 0 }; + struct tst_lm **lm_before[MAX_NS] = { NULL }; + struct tst_lm **lm_after[MAX_NS] = { NULL }; + struct tst_lm *preloads[MAX_NS] = { NULL }; + int want_preload = 0; + int test_status = 0; + + memset (&lm_after[0], 0, sizeof (lm_after)); + memset (&lm_before[0], 0, sizeof (lm_before)); + memset (&preloads[0], 0, sizeof (preloads)); + + TRACE("BEFORE SNAPSHOTS: %p", &lm_before[0]); + TRACE("AFTER SNAPSHOTS: %p", &lm_after[0]); + TRACE("preloads : %p", preloads); + TRACE("setup done"); + + if (test->args.dso_path && *test->args.dso_path && !test->dso_name) + test->dso_name = str_soname (test->args.dso_path); + + TRACE("DSO short name: %s", test->dso_name); + + // get the existing link map contents before the test runs: + lm_before[0] = link_map_snapshot_array (NULL, process_test_spec, + LM_ID_BASE, &lm_before_len[0]); + for (int i = 1; i < MAX_NS; i++) + lm_before[i] = link_map_snapshot_array (NULL, NULL, i, &lm_before_len[i]); + + TRACE("link map snapshots cached"); + + for (int i = 0; i < MAX_NS; i++) + { + if (test->preloaded[i] & PROXY) + { + struct tst_lm **lm = lm_before[i]; + want_preload++; + + if (lm != NULL) + for (int j = 0; !preloads[i] && (j < lm_before_len[i]); j++) + if (dlm_dso_is_loaded (lm[j]) && lm[j]->l_proxy) + preloads[i] = lm[j]; + + if (!preloads[i]) + { + ERROR (test, + "needed proxy for %s preloaded in NS %d, not found\n", + test->dso_name, i); + goto cleanup; + } + } + else if (test->preloaded[i] & DSO) + { + struct tst_lm **lm = lm_before[i]; + int lm_max = lm_before_len[i]; + want_preload++; + + if (lm != NULL) + for (int j = 0; !preloads[i] && (j < lm_max); j++) + { + if (dlm_dso_is_loaded (lm[j]) && !lm[j]->l_proxy) + preloads[i] = lm[j]; + } + if (!preloads[i]) + { + ERROR (test, + "needed %s preloaded in NS %d, not found\n", + test->dso_name, i); + goto cleanup; + } + } + } + TRACE("preload checks (A)"); + + if (dlm_dso_is_loaded (RTLD_DEFAULT)) + { + // test DSO module must _not_ be preloaded, and is: + if (!want_preload) + { + ERROR (test, "DSO %s unexpectedly loaded before test\n", test->dso_name); + goto cleanup; + } + } + else + { + // DSO is not loaded, and must be: + // In theory we can never see this error as it + // should be caught by the preceding preload loop: + if (want_preload) + { + ERROR (test, "DSO %s must be preloaded (and is not)\n", test->args.dso_path); + goto cleanup; + } + } + TRACE("preload checks (B)"); + + if (!(test->args.flags & (RTLD_NOW|RTLD_LAZY))) + test->args.flags |= RTLD_NOW; + + handle = dlmopen (test->args.ns, test->args.dso_path, test->args.flags); + TRACE("dlmopen returned %p", handle); + + if (handle == NULL) + { + const char *status = "failed"; + + if (test->failure) + { + status = "failed (EXPECTED)"; + test_status = 1; + + printf ("%s: dlmopen(%s, %d, 0x%0x) failed: OK\n", + test->name, test->args.dso_path, + (int)test->args.ns, (int)test->args.flags); + printf ("Returned: %p\n\n", handle); + + goto cleanup; + } + + ERROR (test, ""); + + if (test->is_prep_stage) + printf ("(during setup of preconditions): "); + else + printf (": "); + + if (test->args.ns == LM_ID_BASE) + printf ("dlmopen (LM_ID_BASE, \"%s\", 0x%x) %s: %s\n", + test->args.dso_path, test->args.flags, status, dlerror ()); + else + printf ("dlmopen (%d, \"%s\", 0x%x) %s: %s\n", + (int)test->args.ns, test->args.dso_path, test->args.flags, status, dlerror ()); + + goto cleanup; + } + else if (test->failure) + { + ERROR (test, "dlmopen() call should have failed, but did not\n"); + goto cleanup; + } + + TRACE("return status checked"); + + if (!dlm_dso_is_loaded (handle)) + { + ERROR (test, "DSO %s (%p) missing function (%s)\n", + test->args.dso_path, handle, DSO_TESTFN); + goto cleanup; + } + + TRACE ("loaded DSO sanity checked"); + + Lmid_t hns = ns_of_dl_handle (handle); + Lmid_t real_hns = real_ns_of_dl_handle (handle); + Lmid_t proxy_ns = 0; + + call_testfunc (test, handle, LM_ID_BASE); + TRACE (DSO_TESTFN "called"); + + cache_test_handle (handle, hns); + TRACE ("handle %p cached (ns %d)", handle, (int)hns); + + // if the real ns was different to the apparent one + // then we have a proxy and we need to shuffle the values, + // else leave the proxy ns as 0 as an expect.proxy_ns of 0 + // means we weren't expecting a proxy: + if (real_hns != hns) + { + proxy_ns = hns; + hns = real_hns; + } + + if (proxy_ns) + printf ("Returned: proxy ns:%d (real ns: %d)\n\n", (int)proxy_ns, (int)hns); + else + printf ("Returned: dso ns:%d\n\n", (int)hns); + + Lmid_t expected; + if (test->handle_type & PROXY) + expected = proxy_ns; + else + expected = hns; + + TRACE("check expected ns %d", (int)expected); + + if (test->args.ns == LM_ID_NEWLM) + { + if (expected <= LM_ID_BASE) + { + ERROR (test, "DSO should have been in NS > %d, was in %d\n", + LM_ID_BASE, (int)expected); + goto cleanup; + } + + // for any cases where we can't predict + // the namespace in advance: + if (test->handle_ns == LM_ID_NEWLM) + test->handle_ns = expected; + } + else + { + if (test->args.ns != expected) + { + ERROR (test, "DSO should have been in NS %d, was in %d\n", + (int)test->args.ns, (int)expected); + goto cleanup; + } + } + + TRACE("ns %d Ok", (int)expected); + + if (test->handle_type & PROXY) // expecting a proxy + { + if (proxy_ns != 0) // got a proxy + { + if (test->handle_ns != proxy_ns) // but not in the right place + { + ERROR (test, "DSO proxy should have been in ns %d, was in %d\n", + (int)test->handle_ns, (int)proxy_ns); + goto cleanup; + } + } + else // didn't get a proxy + { + ERROR (test, + "DSO should have been a proxy in ns %d," + " was a non-proxy in ns %d\n", + (int)test->handle_ns, (int)hns); + goto cleanup; + } + } + else // not expecting a proxy + { + if (proxy_ns > 0) + { + ERROR (test, + "DSO should NOT have been a proxy," + " was a proxy in ns %d (real ns %d)\n", + (int)proxy_ns, (int)hns); + goto cleanup; + } + + if (test->handle_ns != hns) + { + ERROR (test, + "DSO should have been in ns %d," + " was in ns %d\n", + (int)test->handle_ns, (int)hns); + goto cleanup; + } + } + TRACE ("proxy status Ok"); + + // get the new link map contents after the test has run: + lm_after[0] = link_map_snapshot_array (NULL, process_test_spec, + LM_ID_BASE, &lm_after_len[0]); + for (int i = 1; i < MAX_NS; i++) + lm_after[i] = link_map_snapshot_array (NULL, NULL, i, &lm_after_len[i]); + + for (int i = 0; i < MAX_NS; i++) + { + TRACE("checking status of NS %d", i); + void *old_handle = + find_test_dso_in_link_map_array (lm_before[i], lm_before_len[i]); + TRACE ("old handle is %p", old_handle); + + void *new_handle = + find_test_dso_in_link_map_array (lm_after[i], lm_after_len[i]); + TRACE ("new handle is %p", new_handle); + + if (test->loaded[i] == NONE) + { + TRACE ("ns %d requirement is NONE", i); + if (old_handle != NULL) + { + ERROR (test, + "Unexpected preload DSO %s in ns %d\n", + lm_name (old_handle), i); + goto cleanup; + } + if (new_handle != NULL) + { + ERROR (test, "Unexpected new DSO %s in ns %d\n", + lm_name (new_handle), i); + goto cleanup; + } + continue; + } + + if (test->loaded[i] & NEW) + { + TRACE("ns %d requirement is NEW", i); + if (old_handle != NULL) + { + ERROR (test, + "DSO in ns %d should have been a new load," + " found to have been preloaded\n", i); + goto cleanup; + } + if (new_handle == NULL) + { + ERROR (test, "Expected DSO in ns %d, not found\n", i); + goto cleanup; + } + } + else + { + TRACE("ns %d requirement is OLD", i); + if (new_handle == NULL) + { + ERROR (test, "Expected new DSO in ns %d, not found\n", i); + goto cleanup; + } + + if (old_handle != new_handle) + { + ERROR (test, "DSO in ns %d changed. This should be impossible, " + "sanity check the test code in %s\n", i, __FILE__); + goto cleanup; + } + } + + if (test->loaded[i] & PROXY) + { + TRACE ("rechecking DSO status in ns %d", i); + if (!((struct tst_lm *)new_handle)->l_proxy) + { + ERROR (test, "DSO in ns %d should be a proxy but is not\n", i); + goto cleanup; + } + } + else + { + TRACE ("rechecking proxy status in ns %d", i); + if (((struct tst_lm *)new_handle)->l_proxy) + { + ERROR (test, "DSO in ns %d should NOT be a proxy but is\n", i); + goto cleanup; + } + } + } + + test_status = 1; + + cleanup: + for (int i = 0; i < MAX_NS; i++) + free (lm_after[i]); + for (int i = 0; i < MAX_NS; i++) + free (lm_before[i]); + + return test_status; +} + + diff --git a/elf/tst-dlmopen-modules.h b/elf/tst-dlmopen-modules.h new file mode 100644 index 0000000000..796d8855fe --- /dev/null +++ b/elf/tst-dlmopen-modules.h @@ -0,0 +1,3 @@ +#pragma once + +#include "tst-dlmopen-common.h" diff --git a/elf/tst-dlmopen-rtld-shared1.c b/elf/tst-dlmopen-rtld-shared1.c new file mode 100644 index 0000000000..152eaf933c --- /dev/null +++ b/elf/tst-dlmopen-rtld-shared1.c @@ -0,0 +1,74 @@ +#include +#include "tst-dlmopen-main.h" + +static dlmopen_test_spec dltest[] = + { + { + .name = "dlmopen:0:none--ns0", + .desc = "dlmopen as dlopen", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_BASE, + .loaded = { [0] = DSO|NEW }, + .handle_type = DSO, + .handle_ns = LM_ID_BASE, + }, + { + .name = "dlmopen:0:ns0--ns0", + .desc = "dlmopen a preloaded DSO in the base NS", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_BASE, + .preloaded = { [0] = DSO }, + .loaded = { [0] = DSO }, + .handle_type = DSO, + .handle_ns = LM_ID_BASE, + }, + { + .name = "dlmopen-shared:0:ns0--ns0", + .desc = "dlmopen a preloaded DSO in the base NS with RTLD_SHARED", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_BASE, + .args.flags = RTLD_SHARED, + .preloaded = { [0] = DSO }, + .loaded = { [0] = DSO }, + .handle_type = DSO, + .handle_ns = LM_ID_BASE, + }, + { + .name = "dlmopen:0:ns0--ns1", + .desc = "dlmopen a preloaded DSO in the base NS into a new NS", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_NEWLM, + .preloaded = { [0] = DSO }, + .loaded = { [0] = DSO, [1] = DSO|NEW }, + .handle_type = DSO, + .handle_ns = 1, + }, + { + .name = "dlmopen:0:ns0-ns1--ns0-ns1", + .desc = "dlmopen a preloaded DSO in the base & secondary NS into the base NS", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_BASE, + .preloaded = { [0] = DSO, [1] = DSO }, + .loaded = { [0] = DSO, [1] = DSO }, + .handle_type = DSO, + .handle_ns = LM_ID_BASE, + }, + { + .name = "dlmopen-shared:0:ns0-ns1--ns0-ns1", + .desc = "dlmopen a preloaded DSO in the base & secondary NS into the base NS", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_BASE, + .args.flags = RTLD_SHARED, + .preloaded = { [0] = DSO, [1] = DSO }, + .loaded = { [0] = DSO, [1] = DSO }, + .handle_type = DSO, + .handle_ns = LM_ID_BASE, + }, + END_TESTS + }; + +#include "tst-dlmopen-std-do-test.h" + +DEFINE_DLMOPEN_TEST(dltest) + +#include diff --git a/elf/tst-dlmopen-rtld-shared2.c b/elf/tst-dlmopen-rtld-shared2.c new file mode 100644 index 0000000000..ffa5779e34 --- /dev/null +++ b/elf/tst-dlmopen-rtld-shared2.c @@ -0,0 +1,76 @@ +#include +#include "tst-dlmopen-main.h" + +static dlmopen_test_spec dltest[] = + { + { + .name = "dlmopen-shared:0:none--ns0", + .desc = "dlmopen as dlopen with RTLD_SHARED", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_BASE, + .args.flags = RTLD_SHARED, + .loaded = { [0] = DSO|NEW }, + .handle_ns = LM_ID_BASE, + .handle_type = DSO, + }, + { + .name = "dlmopen-shared:1:ns0--ns0-ns1p", + .desc = "dlmopen into a new namespace with the target already in the base NS", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_NEWLM, + .args.flags = RTLD_SHARED, + .handle_ns = 1, + .handle_type = PROXY, + .preloaded = { [0] = DSO }, + .loaded = { [0] = DSO, [1] = PROXY|NEW } + }, + { + .name = "dlmopen:0:ns0-ns1p--ns0-ns1p", + .desc = "dlmopen into base NS while proxy already in ns1", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_BASE, + .handle_ns = 0, + .handle_type = DSO, + .preloaded = { [0] = DSO, [1] = PROXY }, + .loaded = { [0] = DSO, [1] = PROXY }, + }, + { + .name = "dlmopen-shared:0:ns0-ns1p--ns0-ns1p", + .desc = "dlmopen with RTLD_SHARED into base NS while proxy already in ns1", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_BASE, + .args.flags = RTLD_SHARED, + .handle_ns = 0, + .handle_type = DSO, + .preloaded = { [0] = DSO, [1] = PROXY }, + .loaded = { [0] = DSO, [1] = PROXY }, + }, + { + .name = "dlmopen:1:ns0-ns1p--ns0-ns1p", + .desc = "dlmopen into NS 1 while proxy already in ns1", + .args.dso_path = DSO_NORMAL, + .args.ns = 1, + .handle_ns = 1, + .handle_type = PROXY, + .preloaded = { [0] = DSO, [1] = PROXY }, + .loaded = { [0] = DSO, [1] = PROXY }, + }, + { + .name = "dlmopen-shared:1:ns0-ns1p--ns0-ns1p", + .desc = "dlmopen with RTLD_SHARED into NS 1 while proxy already in ns1", + .args.dso_path = DSO_NORMAL, + .args.ns = 1, + .args.flags = RTLD_SHARED, + .handle_ns = 1, + .handle_type = PROXY, + .preloaded = { [0] = DSO, [1] = PROXY }, + .loaded = { [0] = DSO, [1] = PROXY }, + }, + END_TESTS + }; + +#include "tst-dlmopen-std-do-test.h" + +DEFINE_DLMOPEN_TEST(dltest) + +#include diff --git a/elf/tst-dlmopen-rtld-shared3.c b/elf/tst-dlmopen-rtld-shared3.c new file mode 100644 index 0000000000..cc2d411b5f --- /dev/null +++ b/elf/tst-dlmopen-rtld-shared3.c @@ -0,0 +1,53 @@ +#include +#include "tst-dlmopen-main.h" + +static dlmopen_test_spec dltest[] = + { + { + .name = "dlmopen:1:none--ns1", + .desc = "dlmopen into ns1, no copies preloaded", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_NEWLM, + .handle_ns = 1, + .handle_type = DSO, + .preloaded = { }, + .loaded = { [1] = DSO|NEW }, + }, + { + .name = "dlmopen:0:ns1--ns0-ns1", + .desc = "dlmopen into ns 0, copy already loaded in ns 1", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_BASE, + .handle_ns = 0, + .handle_type = DSO, + .preloaded = { [1] = DSO }, + .loaded = { [0] = DSO|NEW, [1] = DSO }, + }, + { + .name = "dlmopen:1:ns0-ns1--ns1", + .desc = "dlmopen into ns 1, copies already in ns 0 and ns 1", + .args.dso_path = DSO_NORMAL, + .args.ns = 1, + .handle_ns = 1, + .handle_type = DSO, + .preloaded = { [0] = DSO, [1] = DSO }, + .loaded = { [0] = DSO, [1] = DSO }, + }, + { + .name = "dlmopen-shared:1:ns0-ns1--ns1", + .desc = "dlmopen RTLD_SHARED into ns1 with a DSO already in NS0 and NS1", + .args.dso_path = DSO_NORMAL, + .args.ns = 1, + .args.flags = RTLD_SHARED, + .failure = 1, + .preloaded = { [0] = DSO, [1] = DSO }, + .loaded = { [0] = DSO, [1] = DSO }, + }, + END_TESTS + }; + +#include "tst-dlmopen-std-do-test.h" + +DEFINE_DLMOPEN_TEST(dltest) + +#include diff --git a/elf/tst-dlmopen-rtld-shared4.c b/elf/tst-dlmopen-rtld-shared4.c new file mode 100644 index 0000000000..72c2d08506 --- /dev/null +++ b/elf/tst-dlmopen-rtld-shared4.c @@ -0,0 +1,24 @@ +#include +#include "tst-dlmopen-main.h" + +static dlmopen_test_spec dltest[] = + { + { + .name = "dlmopen-shared:1:none--ns0-ns1", + .desc = "dlmopen a new proxy in ns1 with no preexisting dso in ns0", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_NEWLM, + .args.flags = RTLD_SHARED, + .handle_ns = 1, + .handle_type = PROXY, + .preloaded = { }, + .loaded = { [0] = DSO|NEW, [1] = PROXY|NEW }, + }, + END_TESTS + }; + +#include "tst-dlmopen-std-do-test.h" + +DEFINE_DLMOPEN_TEST(dltest) + +#include diff --git a/elf/tst-dlmopen-rtld-shared5.c b/elf/tst-dlmopen-rtld-shared5.c new file mode 100644 index 0000000000..d824214402 --- /dev/null +++ b/elf/tst-dlmopen-rtld-shared5.c @@ -0,0 +1,35 @@ +#include +#include "tst-dlmopen-main.h" + +static dlmopen_test_spec dltest[] = + { + { + .name = "dlmopen-preload:1:none--ns1", + .desc = "preload a DSO into ns1 to prepare for other tests", + .is_prep_stage = 1, + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_NEWLM, + .handle_ns = 1, + .handle_type = DSO, + .preloaded = { }, + .loaded = { [1] = DSO|NEW }, + }, + { + .name = "dlmopen-shared:0:ns1--ns1-ns0", + .desc = "dlmopen RTLD_SHARED into ns0 when preloaded into ns1", + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_BASE, + .args.flags = RTLD_SHARED, + .handle_ns = 0, + .handle_type = DSO, + .preloaded = { [1] = DSO }, + .loaded = { [0] = DSO|NEW, [1] = DSO }, + }, + END_TESTS + }; + +#include "tst-dlmopen-std-do-test.h" + +DEFINE_DLMOPEN_TEST(dltest) + +#include diff --git a/elf/tst-dlmopen-rtld-shared6.c b/elf/tst-dlmopen-rtld-shared6.c new file mode 100644 index 0000000000..886f03f8e0 --- /dev/null +++ b/elf/tst-dlmopen-rtld-shared6.c @@ -0,0 +1,46 @@ +#include +#include "tst-dlmopen-main.h" + +static dlmopen_test_spec dltest[] = + { + { + .name = "dlmopen-preload:1:none--ns1", + .desc = "preload a DSO into ns1 to prepare for other tests", + .is_prep_stage = 1, + .args.dso_path = DSO_NORMAL, + .args.ns = LM_ID_NEWLM, + .handle_ns = 1, + .handle_type = DSO, + .preloaded = { }, + .loaded = { [1] = DSO|NEW }, + }, + { + .name = "dlmopen:1:ns1--ns1", + .desc = "dlmopen a dso in ns1 while already loaded there", + .args.dso_path = DSO_NORMAL, + .args.ns = 1, + .handle_ns = 1, + .handle_type = DSO, + .preloaded = { [1] = DSO }, + .loaded = { [1] = DSO }, + }, + { + .name = "dlmopen-shared:1:ns1--ns1", + .desc = "dlmopen RTLD_SHARED a dso in ns1 while already loaded there", + .failure = 1, + .args.dso_path = DSO_NORMAL, + .args.ns = 1, + .args.flags = RTLD_SHARED, + .handle_ns = 1, + .handle_type = DSO, + .preloaded = { [1] = DSO }, + .loaded = { [1] = DSO }, + }, + END_TESTS + }; + +#include "tst-dlmopen-std-do-test.h" + +DEFINE_DLMOPEN_TEST(dltest) + +#include diff --git a/elf/tst-dlmopen-rtld-unique1.c b/elf/tst-dlmopen-rtld-unique1.c new file mode 100644 index 0000000000..6fc6d12721 --- /dev/null +++ b/elf/tst-dlmopen-rtld-unique1.c @@ -0,0 +1,96 @@ +#include +#include "tst-dlmopen-main.h" + +static dlmopen_test_spec dltest[] = + { + { + .name = "dlmopen-unique:0:none--ns0", + .desc = "dlmopen a DF_1_UNIQUE dso into ns0", + .args.dso_path = DSO_UNIQUE, + .args.ns = LM_ID_BASE, + .handle_ns = 0, + .handle_type = DSO, + .preloaded = { }, + .loaded = { [0] = DSO|NEW }, + }, + { + .name = "dlmopen-unique:0:ns0--ns0", + .desc = "dlmopen a DF_1_UNIQUE dso into ns0 while already present", + .args.dso_path = DSO_UNIQUE, + .args.ns = LM_ID_BASE, + .handle_ns = 0, + .handle_type = DSO, + .preloaded = { [0] = DSO }, + .loaded = { [0] = DSO }, + }, + { + .name = "dlmopen-unique-shared:0:ns0--ns0", + .desc = "dlmopen RTLD_SHARED a DF_1_UNIQUE dso into ns0 while already present", + .args.dso_path = DSO_UNIQUE, + .args.ns = LM_ID_BASE, + .args.flags = RTLD_SHARED, + .handle_ns = 0, + .handle_type = DSO, + .preloaded = { [0] = DSO }, + .loaded = { [0] = DSO }, + }, + { + .name = "dlmopen-unique:1:ns0--ns1", + .desc = "dlmopen a DF_1_UNIQUE dso into ns1 while present in ns0", + .args.dso_path = DSO_UNIQUE, + .args.ns = LM_ID_NEWLM, + .handle_ns = 1, + .handle_type = PROXY, + .preloaded = { [0] = DSO }, + .loaded = { [0] = DSO, [1] = PROXY|NEW }, + }, + { + .name = "dlmopen-unique:0:ns0-ns1p--ns0--ns1p", + .desc = "dlmopen a DF_1_UNIQUE dso already in ns0 proxied in ns1", + .args.dso_path = DSO_UNIQUE, + .args.ns = LM_ID_BASE, + .handle_ns = 0, + .handle_type = DSO, + .preloaded = { [0] = DSO, [1] = PROXY }, + .loaded = { [0] = DSO, [1] = PROXY }, + }, + { + .name = "dlmopen-shared-unique:0:ns0-ns1p--ns0-ns1p", + .desc = "dlmopen RTLD_SHARED a DF_1_UNIQUE dso into ns0 already in ns0 and proxied in ns1", + .args.dso_path = DSO_UNIQUE, + .args.ns = LM_ID_BASE, + .args.flags = RTLD_SHARED, + .handle_ns = 0, + .handle_type = DSO, + .preloaded = { [0] = DSO, [1] = PROXY }, + .loaded = { [0] = DSO, [1] = PROXY }, + }, + { + .name = "dlmopen-unique:0:ns0-ns1p--ns0-ns1p", + .desc = "dlmopen a DF_1_UNIQUE dso into ns0 already in ns0 and proxied in ns1", + .args.dso_path = DSO_UNIQUE, + .args.ns = 1, + .handle_ns = 1, + .handle_type = PROXY, + .preloaded = { [0] = DSO, [1] = PROXY }, + .loaded = { [0] = DSO, [1] = PROXY }, + }, + { + .name = "dlmopen-unique:0:ns0-ns1p--ns0-ns1p", + .desc = "dlmopen RTLD_SHARED a DF_1_UNIQUE dso into ns0 already in ns0 and proxied in ns1", + .args.dso_path = DSO_UNIQUE, + .args.ns = 1, + .args.flags = RTLD_SHARED, + .handle_ns = 1, + .handle_type = PROXY, + .preloaded = { [0] = DSO, [1] = PROXY }, + .loaded = { [0] = DSO, [1] = PROXY }, + }, + END_TESTS + }; + +#include "tst-dlmopen-std-do-test.h" + +DEFINE_DLMOPEN_TEST(dltest) + +#include diff --git a/elf/tst-dlmopen-rtld-unique2.c b/elf/tst-dlmopen-rtld-unique2.c new file mode 100644 index 0000000000..865c1532c4 --- /dev/null +++ b/elf/tst-dlmopen-rtld-unique2.c @@ -0,0 +1,35 @@ +#include +#include "tst-dlmopen-main.h" + +static dlmopen_test_spec dltest[] = + { + { + .name = "dlmopen-shared-unique:0:none--ns0", + .desc = "dlmopen RTLD_SHARED a DF_1_UNIQUE dso in the base ns", + .args.dso_path = DSO_UNIQUE, + .args.ns = LM_ID_BASE, + .args.flags = RTLD_SHARED, + .handle_ns = 0, + .handle_type = DSO, + .preloaded = { }, + .loaded = { [0] = DSO|NEW }, + }, + { + .name = "dlmopen-shared-unique:1:ns0--ns0-ns1p", + .desc = "dlmopen RTLD_SHARED a DF_1_UNIQUE dso into ns1 while present in ns0", + .args.dso_path = DSO_UNIQUE, + .args.ns = LM_ID_NEWLM, + .args.flags = RTLD_SHARED, + .handle_ns = 1, + .handle_type = PROXY, + .preloaded = { [0] = DSO }, + .loaded = { [0] = DSO, [1] = PROXY|NEW }, + }, + END_TESTS + }; + +#include "tst-dlmopen-std-do-test.h" + +DEFINE_DLMOPEN_TEST(dltest) + +#include diff --git a/elf/tst-dlmopen-rtld-unique3.c b/elf/tst-dlmopen-rtld-unique3.c new file mode 100644 index 0000000000..f05b9dd733 --- /dev/null +++ b/elf/tst-dlmopen-rtld-unique3.c @@ -0,0 +1,23 @@ +#include +#include "tst-dlmopen-main.h" + +static dlmopen_test_spec dltest[] = + { + { + .name = "dlmopen-unique:1:none--ns0-ns1p", + .desc = "dlmopen a DF_1_UNIQUE dso into ns1", + .args.dso_path = DSO_UNIQUE, + .args.ns = LM_ID_NEWLM, + .handle_ns = 1, + .handle_type = PROXY, + .preloaded = { }, + .loaded = { [0] = DSO|NEW, [1] = PROXY|NEW }, + }, + END_TESTS + }; + +#include "tst-dlmopen-std-do-test.h" + +DEFINE_DLMOPEN_TEST(dltest) + +#include diff --git a/elf/tst-dlmopen-rtld-unique4.c b/elf/tst-dlmopen-rtld-unique4.c new file mode 100644 index 0000000000..247085f5d7 --- /dev/null +++ b/elf/tst-dlmopen-rtld-unique4.c @@ -0,0 +1,24 @@ +#include +#include "tst-dlmopen-main.h" + +static dlmopen_test_spec dltest[] = + { + { + .name = "dlmopen-shared-unique:1:none--ns0-ns1p", + .desc = "dlmopen a DF_1_UNIQUE dso", + .args.dso_path = DSO_UNIQUE, + .args.ns = LM_ID_NEWLM, + .args.flags = RTLD_SHARED, + .handle_ns = 1, + .handle_type = PROXY, + .preloaded = { }, + .loaded = { [0] = DSO|NEW, [1] = PROXY|NEW }, + }, + END_TESTS + }; + +#include "tst-dlmopen-std-do-test.h" + +DEFINE_DLMOPEN_TEST(dltest) + +#include diff --git a/elf/tst-dlmopen-sharedmod-norm.c b/elf/tst-dlmopen-sharedmod-norm.c new file mode 100644 index 0000000000..fd049903f4 --- /dev/null +++ b/elf/tst-dlmopen-sharedmod-norm.c @@ -0,0 +1,11 @@ +#include "tst-dlmopen-modules.h" + +dlmopen_testresult *rtld_shared_testfunc (void) +{ + static dlmopen_testresult result; + + result.name = "norm"; + result.free = free; + + return &result; +} diff --git a/elf/tst-dlmopen-sharedmod-uniq.c b/elf/tst-dlmopen-sharedmod-uniq.c new file mode 100644 index 0000000000..5c9701da41 --- /dev/null +++ b/elf/tst-dlmopen-sharedmod-uniq.c @@ -0,0 +1,11 @@ +#include "tst-dlmopen-modules.h" + +dlmopen_testresult *rtld_shared_testfunc (void) +{ + static dlmopen_testresult result; + + result.name = "noop"; + result.free = free; + + return &result; +} diff --git a/elf/tst-dlmopen-std-do-test.h b/elf/tst-dlmopen-std-do-test.h new file mode 100644 index 0000000000..595f6f764d --- /dev/null +++ b/elf/tst-dlmopen-std-do-test.h @@ -0,0 +1,11 @@ +#pragma once + +#define DEFINE_DLMOPEN_TEST(x) \ + static int \ + do_test (void) \ + { \ + for (int i = 0; x[i].name; i++) \ + if (!process_test_spec (&x[i])) \ + return 1; \ + return 0; \ + }