From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-qv1-xf35.google.com (mail-qv1-xf35.google.com [IPv6:2607:f8b0:4864:20::f35]) by sourceware.org (Postfix) with ESMTPS id 5862E3844023 for ; Fri, 28 May 2021 17:32:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 5862E3844023 Received: by mail-qv1-xf35.google.com with SMTP id 5so2317710qvk.0 for ; Fri, 28 May 2021 10:32:08 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=j+Xxtz1zdpyLlXyJCoQPB9g2ZsHMhERUWRGWp7pB0fk=; b=qemzdLN+k47nR2sMqzoNfpUIuCoy79x2rVh26uOTeuZNiGkN50sxooJmCzjdj7Cir/ /+MQ+INwD4QXutHDqA4FCd4QVy0WJUAECMADgrp8VfH3l28kxH2xxvLy/GQLCy8vftp6 n6H21uet6y3IQJie6+tAAVbMyNc95aZI1JQXgrohehX6g0daYsNpZGD8cmtAyMskSgOI 6P/6ltbz6cSwyZvtb+JYcBE+vIfS0qSmw4LH6dnrSjANzV4HZfcpmhgXUyUW6SvOpbYe 50ACr57aNxmA8gxEPLbWIJp5Hjh8kY2wPLP5hdCqCzSvHMNlVI9hNZkmRQPBrILwleY+ kZuA== X-Gm-Message-State: AOAM530XhM+cz2KWRWRnbAXw2w8vISUZ0wC8keOHeH9FNTyMLnxN589l dXk+sWciJ9/fQSTDxB9Jk7/Ic9AaMqHZdQ== X-Google-Smtp-Source: ABdhPJyV/UykteAWTGraJrrMXAtI8/qdIGvMQwTie0W0YqChWCI4Xt7Z6e+cvSiusf8Emrny/823Ew== X-Received: by 2002:a0c:e250:: with SMTP id x16mr4984401qvl.56.1622223126848; Fri, 28 May 2021 10:32:06 -0700 (PDT) Received: from [192.168.1.4] ([177.194.59.218]) by smtp.gmail.com with ESMTPSA id l14sm3783116qtj.26.2021.05.28.10.32.05 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 28 May 2021 10:32:06 -0700 (PDT) Subject: Re: [RFC][PATCH v10 5/7] Implement dlmopen RTLD_SHARED flag (bug 22745) To: Vivek Das Mohapatra , libc-alpha@sourceware.org References: <20210322154111.24798-1-vivek@collabora.com> <20210322154111.24798-6-vivek@collabora.com> From: Adhemerval Zanella Message-ID: Date: Fri, 28 May 2021 14:32:03 -0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.8.1 MIME-Version: 1.0 In-Reply-To: <20210322154111.24798-6-vivek@collabora.com> Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, NICE_REPLY_A, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, URIBL_BLACK 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-List-Received-Date: Fri, 28 May 2021 17:32:12 -0000 On 22/03/2021 12:41, Vivek Das Mohapatra via Libc-alpha wrote: > This flag will instruct dlmopen to create a shared object present > in the main namespace and accessible from the selected namespace > when supplied in the MODE argument. > > include/link.h: Update the link_map struct to allow proxies > > We already have an l_real pointer, used for a similar purpose by > the linker for copies of ld.so in secondary namespaces. Update its > documentation and add a bitfield to indicate when link_map entry > is a proxy. > > elf/dl-object.c: Implement a helper function to proxy link_map entries > > Provides the minimal functionality needed to take an existing > link_map entry and create a proxy for it in the specified namespace. > > elf/dl-load.c, elf/dl-open.c: Implement RTLD_SHARED dlmopen proxying > > This uses the new infrastructure to implement RTLD_SHARED object > proxying via dlmopen: Instead of opening the specified object in > the requested namespace we open it in the main namespace (if it > is not already present there) and proxy it to the destination. > > The following rules apply: > > If a proxy of the object is already present in the requested namespace, > we simply return it (with an incremented direct-open count). > > If the object is already present in the requested namespace, a dl > error is signalled, since we cannot satisfy the user's request. > > Proxies are never created in the main namespace: RTLD_SHARED has no > effect when the requested namespace is LM_ID_BASE. > > elf/dl-fini.c: Handle proxy link_maps in the shutdown path (bug 22745) > > When cleaning up before exit we should not call destructors or > otherwise free [most of] the contents of proxied link_map entries > since they share [most of] their contents with the LM_ID_BASE > objects to which they point. > > elf/dl-init.c: Skip proxy link_map entries in dl init path > > Proxies should not trigger calls to DT_INIT constructors since they're > just shims that point to the real, already loaded and initialised, objects. > > elf/dl-open.c: Skip libc init if namespace has no libc map > > Secondary namespaces which share their libc mapping with the main > namespace cannot (and should not) have _dl_call_libc_early_init > called for them by dl_open_worker. > > elf/dl-open.c: When creating a proxy check NS 0 libc map > > The libc_already_loaded check normally considers the libc_map entry > in GL(dl_ns)[args->nsid].libc_map. > > This is not correct for proxies, which use the libc_map from > the default namespace (as proxies are dummy entries that point > to the base namespace via their l_real members). > > elf/dl-load.c, dl-open.c: Compare DSOs by file ID & check DF_GNU_1_UNIQUE > > If _dl_map_object_from_fd finds that a DSO it was asked to > load into a non-base namespace is already loaded (into the > main namespace) and is flagged DF_GNU_1_UNIQUE then it should > return that DSO's link map entry. > > In such cases _dl_open_worker must notice that this has > happened and continue down the link map proxy generation > path instead of normal link map entry preparation. > > elf/dl-load.c: error if RTLD_SHARED = DF_GNU_1_UNIQUE semantics violated > > elf/dl-open.c: Use search helper to find preloaded DT_GNU_UNIQUE DSOs > > If a DSO already exists (with the same name) in the base namespace > and it is flagged DT_GNU_UNIQUE then we should behave as if a proxy > had been requested. > > elf/dl-load.c: When loading DSOs in other namespaces check DT_GNU_UNIQUE > > If a DSO has not already been loaded and the target is not the main > namespace then we must check to see if it's been DT_GNU_UNIQUE tagged > and load it into the main namespace instead. > > dl_open_worker has alread been modified to notice the discrepancy > between the request and the result in such cases, and will set up > a proxy in the target namespace. > > elf/dl-load.c: Suppress audit calls when a (new) namespace is empty > > When preparing an RTLD_SHARED proxy in a new namespace > it is possible for the target namespace to be empty: > > This can happen for RTLD_SHARED + LM_ID_NEWLM. > > The audit infrastructure should not be invoked at this > point (as there's nothing there to audit yet). > > bits/dlfcn.h, elf/dl-load.c, elf/dl-open.c, elf/rtld.c, > sysdeps/mips/bits/dlfcn.h: > Suppress inter-namespace DSO sharing for audit libraries > > Audit libraries should not participate in DSO sharing: In > particular libraries tagged with DF_GNU_1_UNIQUE should not > be shared between the audit namespace and any others - they > should get their own copy. > > This is signalled to the loader code by passing the RTLD_ISOLATE > flag from the relevant entry point in the dl modes argument. > > elf/dl-sym.c: dlsym, dlvsym must be able to resolve symbols via proxies With this patch I am seeing some regression with binutils with and without -z unique support: FAIL: elf/tst-dlmopen-dlerror FAIL: elf/tst-tls-ie-dlmopen $ ./debugglibc.sh -- elf/tst-dlmopen-dlerror --direct [...] Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7fdf44f in _dl_open (file=, mode=, caller_dlopen=0x7ffff7dd1383, nsid=-2, argc=2, argv=0x7fffffffd5c0, env=0x7fffffffd5d8) at dl-open.c:995 995 GL(dl_ns)[nsid].libc_map = NULL; (gdb) bt #0 0x00007ffff7fdf44f in _dl_open (file=, mode=, caller_dlopen=0x7ffff7dd1383, nsid=-2, argc=2, argv=0x7fffffffd5c0, env=0x7fffffffd5d8) at dl-open.c:995 #1 0x00007ffff7fbf288 in dlopen_doit (a=a@entry=0x7fffffffcb70) at dlopen.c:66 #2 0x00007ffff7f0f769 in __GI__dl_catch_exception (exception=exception@entry=0x7fffffffcad0, operate=, args=) at dl-error-skeleton.c:208 #3 0x00007ffff7f0f80f in __GI__dl_catch_error (objname=0x7fffffffcb30, errstring=0x7fffffffcb38, mallocedp=0x7fffffffcb2f, operate=, args=) at dl-error-skeleton.c:227 #4 0x00007ffff7fbf776 in _dlerror_run (operate=operate@entry=0x7ffff7fbf230 , args=args@entry=0x7fffffffcb70) at dlerror.c:142 #5 0x00007ffff7fbf306 in __dlopen (file=, mode=) at dlopen.c:87 #6 0x00007ffff7dd1383 in ?? () #7 0x000000000000002b in ?? () #8 0x00007fffffffcbd0 in ?? () #9 0x00007ffff7dd1370 in ?? () #10 0x0000000000402595 in do_test () at tst-dlmopen-dlerror.c:43 #11 0x0000000000402d75 in support_test_main (argc=1, argc@entry=2, argv=0x7fffffffd5c8, argv@entry=0x7fffffffd5c0, config=config@entry=0x7fffffffd450) at support_test_main.c:403 #12 0x0000000000402385 in main (argc=argc@entry=2, argv=argv@entry=0x7fffffffd5c0) at ../support/test-driver.c:168 #13 0x00007ffff7dff407 in __libc_start_call_main (main=main@entry=0x402350
, argc=argc@entry=2, argv=argv@entry=0x7fffffffd5c0) at ../sysdeps/nptl/libc_start_call_main.h:58 #14 0x00007ffff7dff4b8 in __libc_start_main_impl (main=0x402350
, argc=2, argv=0x7fffffffd5c0, init=, fini=, rtld_fini=, stack_end=0x7fffffffd5b8) at ../csu/libc-start.c:411 #15 0x0000000000402411 in _start () at ../sysdeps/x86_64/start.S:116 $ ./debugglibc.sh -- elf/tst-dlmopen-dlerror --direct [...] Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7fdf44f in _dl_open (file=, mode=, caller_dlopen=0x7ffff7dd1383, nsid=-2, argc=2, argv=0x7fffffffd5c0, env=0x7fffffffd5d8) at dl-open.c:995 995 GL(dl_ns)[nsid].libc_map = NULL; (gdb) bt #0 0x00007ffff7fdf44f in _dl_open (file=, mode=, caller_dlopen=0x7ffff7dd1383, nsid=-2, argc=2, argv=0x7fffffffd5c0, env=0x7fffffffd5d8) at dl-open.c:995 #1 0x00007ffff7fbf288 in dlopen_doit (a=a@entry=0x7fffffffcb70) at dlopen.c:66 #2 0x00007ffff7f0f769 in __GI__dl_catch_exception (exception=exception@entry=0x7fffffffcad0, operate=, args=) at dl-error-skeleton.c:208 #3 0x00007ffff7f0f80f in __GI__dl_catch_error (objname=0x7fffffffcb30, errstring=0x7fffffffcb38, mallocedp=0x7fffffffcb2f, operate=, args=) at dl-error-skeleton.c:227 #4 0x00007ffff7fbf776 in _dlerror_run (operate=operate@entry=0x7ffff7fbf230 , args=args@entry=0x7fffffffcb70) at dlerror.c:142 #5 0x00007ffff7fbf306 in __dlopen (file=, mode=) at dlopen.c:87 #6 0x00007ffff7dd1383 in ?? () #7 0x000000000000002b in ?? () #8 0x00007fffffffcbd0 in ?? () #9 0x00007ffff7dd1370 in ?? () #10 0x0000000000402595 in do_test () at tst-dlmopen-dlerror.c:43 #11 0x0000000000402d75 in support_test_main (argc=1, argc@entry=2, argv=0x7fffffffd5c8, argv@entry=0x7fffffffd5c0, config=config@entry=0x7fffffffd450) at support_test_main.c:403 #12 0x0000000000402385 in main (argc=argc@entry=2, argv=argv@entry=0x7fffffffd5c0) at ../support/test-driver.c:168 #13 0x00007ffff7dff407 in __libc_start_call_main (main=main@entry=0x402350
, argc=argc@entry=2, argv=argv@entry=0x7fffffffd5c0) at ../sysdeps/nptl/libc_start_call_main.h:58 #14 0x00007ffff7dff4b8 in __libc_start_main_impl (main=0x402350
, argc=2, argv=0x7fffffffd5c0, init=, fini=, rtld_fini=, stack_end=0x7fffffffd5b8) at ../csu/libc-start.c:411 #15 0x0000000000402411 in _start () at ../sysdeps/x86_64/start.S:116 Could you check if this is something related to recent changes? I will stop the review process until we sort these out. > --- > bits/dlfcn.h | 10 ++ > elf/dl-close.c | 43 +++++---- > elf/dl-fini.c | 6 +- > elf/dl-init.c | 4 +- > elf/dl-load.c | 181 +++++++++++++++++++++++++++++++++---- > elf/dl-object.c | 78 ++++++++++++++++ > elf/dl-open.c | 109 +++++++++++++++++++++- > elf/dl-sym.c | 14 +++ > elf/rtld.c | 2 +- > include/link.h | 6 +- > sysdeps/generic/ldsodefs.h | 5 + > sysdeps/mips/bits/dlfcn.h | 10 ++ > 12 files changed, 423 insertions(+), 45 deletions(-) > > diff --git a/bits/dlfcn.h b/bits/dlfcn.h > index f3bc63e958..1366cb3546 100644 > --- a/bits/dlfcn.h > +++ b/bits/dlfcn.h > @@ -32,6 +32,16 @@ > visible as if the object were linked directly into the program. */ > #define RTLD_GLOBAL 0x00100 > > +/* If the following bit is set in the MODE argument to dlmopen > + then the target object is loaded into the main namespace (if > + it is not already there) and a shallow copy (proxy) is placed > + in the target namespace: This allows multiple namespaces to > + share a single instance of a DSO. */ > +#define RTLD_SHARED 0x00080 > + > +/* Suppress RTLD_SHARED and/or DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE */ > +#define RTLD_ISOLATE 0x00040 > + > /* Unix98 demands the following flag which is the inverse to RTLD_GLOBAL. > The implementation does this by default and so we can define the > value to zero. */ > diff --git a/elf/dl-close.c b/elf/dl-close.c > index c51becd06b..a0432b884d 100644 > --- a/elf/dl-close.c > +++ b/elf/dl-close.c > @@ -283,8 +283,9 @@ _dl_close_worker (struct link_map *map, bool force) > > /* Call its termination function. Do not do it for > half-cooked objects. Temporarily disable exception > - handling, so that errors are fatal. */ > - if (imap->l_init_called) > + handling, so that errors are fatal. > + Proxies should never have this flag set, but we double check. */ > + if (imap->l_init_called && !imap->l_proxy) > { > /* When debugging print a message first. */ > if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, > @@ -360,7 +361,9 @@ _dl_close_worker (struct link_map *map, bool force) > one for the terminating NULL pointer. */ > size_t remain = (new_list != NULL) + 1; > bool removed_any = false; > - for (size_t cnt = 0; imap->l_scope[cnt] != NULL; ++cnt) > + for (size_t cnt = 0; > + imap->l_scope && imap->l_scope[cnt] != NULL; > + ++cnt) > /* This relies on l_scope[] entries being always set either > to its own l_symbolic_searchlist address, or some map's > l_searchlist address. */ > @@ -686,8 +689,10 @@ _dl_close_worker (struct link_map *map, bool force) > > /* We can unmap all the maps at once. We determined the > start address and length when we loaded the object and > - the `munmap' call does the rest. */ > - DL_UNMAP (imap); > + the `munmap' call does the rest. Proxies do not have > + any segments of their own to unmap. */ > + if (!imap->l_proxy) > + DL_UNMAP (imap); > > /* Finally, unlink the data structure and free it. */ > #if DL_NNS == 1 > @@ -727,19 +732,23 @@ _dl_close_worker (struct link_map *map, bool force) > _dl_debug_printf ("\nfile=%s [%lu]; destroying link map\n", > imap->l_name, imap->l_ns); > > - /* This name always is allocated. */ > - free (imap->l_name); > - /* Remove the list with all the names of the shared object. */ > + /* Skip structures borrowed by proxies from the real map. */ > + if (!imap->l_proxy) > + { > + /* This name always is allocated. */ > + free (imap->l_name); > + /* Remove the list with all the names of the shared object. */ > > - struct libname_list *lnp = imap->l_libname; > - do > - { > - struct libname_list *this = lnp; > - lnp = lnp->next; > - if (!this->dont_free) > - free (this); > - } > - while (lnp != NULL); > + struct libname_list *lnp = imap->l_libname; > + do > + { > + struct libname_list *this = lnp; > + lnp = lnp->next; > + if (!this->dont_free) > + free (this); > + } > + while (lnp != NULL); > + } > > /* Remove the searchlists. */ > free (imap->l_initfini); > diff --git a/elf/dl-fini.c b/elf/dl-fini.c > index 6dbdfe4b3e..10194488bb 100644 > --- a/elf/dl-fini.c > +++ b/elf/dl-fini.c > @@ -73,7 +73,7 @@ _dl_fini (void) > 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) > + if (l == l->l_real || l->l_proxy) > { > assert (i < nloaded); > > @@ -111,7 +111,9 @@ _dl_fini (void) > { > struct link_map *l = maps[i]; > > - if (l->l_init_called) > + /* Do not call fini functions via proxies, or for > + objects which are not marked as initialised. */ > + if (l->l_init_called && !l->l_proxy) > { > /* Make sure nothing happens if we are called twice. */ > l->l_init_called = 0; > diff --git a/elf/dl-init.c b/elf/dl-init.c > index f924d26642..00ce8bb9d6 100644 > --- a/elf/dl-init.c > +++ b/elf/dl-init.c > @@ -30,8 +30,8 @@ call_init (struct link_map *l, int argc, char **argv, char **env) > need relocation, and neither do proxy objects.) */ > assert (l->l_real->l_relocated || l->l_real->l_type == lt_executable); > > - if (l->l_init_called) > - /* This object is all done. */ > + if (l->l_init_called || l->l_proxy) > + /* This object is all done, or a proxy (and therefore initless). */ > return; > > /* Avoid handling this constructor again in case we have a circular > diff --git a/elf/dl-load.c b/elf/dl-load.c > index f2dbe45703..11513e1992 100644 > --- a/elf/dl-load.c > +++ b/elf/dl-load.c > @@ -838,6 +838,53 @@ _dl_init_paths (const char *llp, const char *source, > __rtld_env_path_list.dirs = (void *) -1; > } > > +static bool > +has_gnu_unique (int fd, const ElfW(Ehdr) *header, const ElfW(Phdr) *phdr) > +{ > + bool unique = false; > + const ElfW(Phdr) *ph; > + > + for (ph = phdr; ph < &phdr[header->e_phnum]; ++ph) > + { > + off64_t end; > + off64_t pos; > + ssize_t bytes = -1; > + ElfW(Dyn) entry = { .d_tag = 0, .d_un.d_val = 0 }; > + > + switch (ph->p_type) > + { > + case PT_DYNAMIC: > + pos = ph->p_offset; > + end = pos + ph->p_filesz; > + > + while (pos < end) > + { > + bytes = __pread64_nocancel (fd, &entry, sizeof (ElfW(Dyn)), pos); > + > + if (__glibc_unlikely (bytes != sizeof (ElfW(Dyn)))) > + goto done; > + > + pos += bytes; > + > + switch (entry.d_tag) > + { > + case DT_GNU_FLAGS_1: > + unique = (entry.d_un.d_val & DF_GNU_1_UNIQUE); > + goto done; > + break; > + > + case DT_NULL: > + goto done; > + break; > + } > + } > + break; > + } > + } > + > + done: > + return unique; > +} > > /* Process PT_GNU_PROPERTY program header PH in module L after > PT_LOAD segments are mapped. Only one NT_GNU_PROPERTY_TYPE_0 > @@ -1021,6 +1068,32 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, > } > #endif > > + /* DSOs in the main namespace which are flagged DF_GNU_1_UNIQUE should only > + be opened into the main namespace. Other namespaces should only get > + proxies. */ > + if (__glibc_unlikely ((nsid != LM_ID_BASE) && !(mode & RTLD_ISOLATE))) > + { > + /* Check base ns to see if the name matched another already loaded. */ > + for (l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l != NULL; l = l->l_next) > + if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id)) > + { > + if (!(l->l_gnu_flags_1 & DF_GNU_1_UNIQUE)) > + continue; > + > + /* Already loaded. Bump its reference count and return it. */ > + __close_nocancel (fd); > + > + /* If the name is not listed for this object add it. */ > + free (realname); > + add_name_to_object (l, name); > + > + /* NOTE: It is important that our caller picks up on the fact > + that we have NOT returned an object in the requested namespace > + and handles the proxying correctly. */ > + return l; > + } > + } > + > if (mode & RTLD_NOLOAD) > { > /* We are not supposed to load the object unless it is already > @@ -1046,8 +1119,11 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, > && __glibc_unlikely (GLRO(dl_naudit) > 0)) > { > struct link_map *head = GL(dl_ns)[nsid]._ns_loaded; > - /* Do not call the functions for any auditing object. */ > - if (head->l_auditing == 0) > + /* Do not call the functions for any auditing object. > + Do not try to call auditing functions if the namespace > + is currently empty. This can hapen when opening the first > + DSO in a new namespace. */ > + if ((head != NULL) && (head->l_auditing == 0)) > { > struct audit_ifaces *afct = GLRO(dl_audit); > for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) > @@ -1073,6 +1149,32 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, > else > assert (r->r_state == RT_ADD); > > + /* Load the ELF header using preallocated struct space if it's big enough. */ > + maplength = header->e_phnum * sizeof (ElfW(Phdr)); > + if (header->e_phoff + maplength <= (size_t) fbp->len) > + phdr = (void *) (fbp->buf + header->e_phoff); > + else > + { > + phdr = alloca (maplength); > + if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength, > + header->e_phoff) != maplength) > + { > + errstring = N_("cannot read file data"); > + goto lose_errno; > + } > + } > + > + /* We need to check for DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE before we start > + initialising any namespace dependent metatada. */ > + if (__glibc_unlikely ((nsid != LM_ID_BASE) && !(mode & RTLD_ISOLATE))) > + { > + /* Target DSO is flagged as unique: Make sure it gets loaded into > + the base namespace. It is up to our caller to generate a proxy > + in the target nsid. */ > + if (has_gnu_unique (fd, header, phdr)) > + nsid = LM_ID_BASE; > + } > + > /* Enter the new object in the list of loaded objects. */ > l = _dl_new_object (realname, name, l_type, loader, mode, nsid); > if (__glibc_unlikely (l == NULL)) > @@ -1090,20 +1192,6 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, > type = header->e_type; > l->l_phnum = header->e_phnum; > > - maplength = header->e_phnum * sizeof (ElfW(Phdr)); > - if (header->e_phoff + maplength <= (size_t) fbp->len) > - phdr = (void *) (fbp->buf + header->e_phoff); > - else > - { > - phdr = alloca (maplength); > - if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength, > - header->e_phoff) != maplength) > - { > - errstring = N_("cannot read file data"); > - goto lose_errno; > - } > - } > - > /* On most platforms presume that PT_GNU_STACK is absent and the stack is > * executable. Other platforms default to a nonexecutable stack and don't > * need PT_GNU_STACK to do so. */ > @@ -2007,6 +2095,37 @@ open_path (const char *name, size_t namelen, int mode, > return -1; > } > > +/* Search for a link map proxy in the given namespace by name. > + Consider it to be an error if the found object is not a proxy. */ > +struct link_map * > +_dl_find_proxy (Lmid_t nsid, const char *name) > +{ > + struct link_map *l; > + > + for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next) > + { > + if (__glibc_unlikely ((l->l_faked | l->l_removed) != 0)) > + continue; > + > + if (!_dl_name_match_p (name, l)) > + continue; > + > + /* We have a match - stop searching. */ > + break; > + } > + > + if (l != NULL) > + { > + if (l->l_proxy) > + return l; > + > + _dl_signal_error (EEXIST, name, NULL, > + N_("object cannot be demoted to a proxy")); > + } > + > + return NULL; > +} > + > /* Search for a shared object in a given namespace. */ > struct link_map * > _dl_find_dso (const char *name, Lmid_t nsid) > @@ -2060,12 +2179,42 @@ _dl_map_object (struct link_map *loader, const char *name, > > assert (nsid >= 0); > assert (nsid < GL(dl_nns)); > + assert (!((mode & RTLD_ISOLATE) && (mode & RTLD_SHARED))); > + > +#ifdef SHARED > + /* Only need to do proxy checks if 'nsid' is not LM_ID_BASE. */ > + if (__glibc_unlikely ((mode & RTLD_SHARED) && (nsid != LM_ID_BASE))) > + { > + /* Search the namespace in case the object is already proxied. */ > + l = _dl_find_proxy (nsid, name); > + if (l != NULL) > + return l; > + > + /* Further searches should be in the base ns: We will proxy the > + resulting object in dl_open_worker *after* it is initialised. */ > + nsid = LM_ID_BASE; > + } > +#endif > > /* Look for this name among those already loaded. */ > l = _dl_find_dso (name, nsid); > > if (l != NULL) > { > +#ifdef SHARED > + /* If we are trying to load a DF_GNU_1_UNIQUE flagged DSO which WAS > + already opened in the target NS but with RTLD_ISOLATE so it WAS NOT > + created as a proxy we need to error out since we cannot satisfy the > + DF_GNU_1_UNIQUE is-equivalent-to RTLD_SHARED semantics. */ > + if (!(mode & RTLD_ISOLATE) && > + (l->l_ns != LM_ID_BASE) && > + (l->l_gnu_flags_1 & DF_GNU_1_UNIQUE) && > + !l->l_proxy) > + { > + _dl_signal_error (EEXIST, name, NULL, > + N_("object cannot be demoted to a proxy")); > + } > +#endif > return l; > } > > diff --git a/elf/dl-object.c b/elf/dl-object.c > index 1875599eb2..30a05b086f 100644 > --- a/elf/dl-object.c > +++ b/elf/dl-object.c > @@ -21,6 +21,7 @@ > #include > #include > #include > +#include > > #include > > @@ -50,6 +51,83 @@ _dl_add_to_namespace_list (struct link_map *new, Lmid_t nsid) > __rtld_lock_unlock_recursive (GL(dl_load_write_lock)); > } > > +/* Proxy an existing link map entry into a new link map: > + This is based on _dl_new_object, skipping the steps we know we won't need > + because this is mostly just a shell for the l_real pointer holding the real > + link map entry (normally l == l->l_real, but not for ld.so in non-main > + link maps or RTLD_SHARED proxies). > + It also flags the proxy by setting l_proxy, and sets the the no-delete > + flag in the original if it is an lt_loaded. */ > +struct link_map * > +_dl_new_proxy (struct link_map *old, int mode, Lmid_t nsid) > +{ > + const char *name; > + struct link_map *new; > + struct libname_list *newname; > +#ifdef SHARED > + unsigned int na = GLRO(dl_naudit); > + > + if ((mode & __RTLD_OPENEXEC) != 0) > + na = DL_NNS; > + > + size_t audit_space = na * sizeof (struct auditstate); > +#else > +# define audit_space 0 > +#endif > + > + name = old->l_name; > + > + /* Find the original link map entry if 'old' is itself a proxy. */ > + while (old != NULL && old->l_proxy) > + old = old->l_real; > + > + if (old == NULL) > + _dl_signal_error (EINVAL, name, NULL, N_("cannot proxy NULL link_map")); > + > + /* Object already exists in the target namespace. This should get handled > + by dl_open_worker but just in case we get this far, handle it: */ > + if (__glibc_unlikely (old->l_ns == nsid)) > + _dl_signal_error (EEXIST, name, NULL, > + N_("existing object cannot be demoted to a proxy")); > + > + /* Now duplicate as little of _dl_new_object as possible to get a > + working proxied object in the target link map. */ > + new = (struct link_map *) calloc (sizeof (*new) + audit_space > + + sizeof (struct link_map *) > + + sizeof (*newname) + PATH_MAX, 1); > + > + if (new == NULL) > + _dl_signal_error (ENOMEM, name, NULL, > + N_("cannot create shared object descriptor")); > + > + /* Specific to the proxy. */ > + new->l_real = old; > + new->l_proxy = 1; > + new->l_ns = nsid; > + > + /* Copied from the origin. */ > + new->l_libname = old->l_libname; > + new->l_name = old->l_name; > + /* Proxies are considered lt_loaded if the real entry type is lt_library. */ > + new->l_type = (old->l_type == lt_library) ? lt_loaded : old->l_type; > + > + if (__glibc_unlikely (mode & RTLD_NODELETE)) > + new->l_flags_1 |= DF_1_NODELETE; > + > + /* Specific to the origin. Ideally we'd do some accounting here but > + for now it's easier to pin the original so the proxy remains valid. */ > + if (old->l_type == lt_loaded) > + old->l_flags_1 |= DF_1_NODELETE; > + > + /* Fix up the searchlist so that relocations work. */ > + _dl_map_object_deps (new, NULL, 0, 0, > + mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT)); > + > + /* And finally put the proxy in the target namespace. */ > + _dl_add_to_namespace_list (new, nsid); > + > + return new; > +} > > /* Allocate a `struct link_map' for a new object being loaded, > and enter it into the _dl_loaded list. */ > diff --git a/elf/dl-open.c b/elf/dl-open.c > index ab7aaa345e..083e6b2c3f 100644 > --- a/elf/dl-open.c > +++ b/elf/dl-open.c > @@ -484,6 +484,16 @@ dl_open_worker (void *a) > const char *file = args->file; > int mode = args->mode; > struct link_map *call_map = NULL; > + struct link_map *preloaded = NULL; > + bool want_proxy = false; > + bool dl_isolate = mode & RTLD_ISOLATE; > + Lmid_t proxy_ns = LM_ID_BASE; > + > + /* Isolation means we should suppress all inter-namespace sharing. */ > + if (dl_isolate) > + mode &= ~RTLD_SHARED; > + else > + want_proxy = mode & RTLD_SHARED; > > /* Determine the caller's map if necessary. This is needed in case > we have a DST, when we don't know the namespace ID we have to put > @@ -508,6 +518,15 @@ dl_open_worker (void *a) > args->nsid = call_map->l_ns; > } > > + /* Now that we know the NS for sure, sanity check the mode. */ > + if (__glibc_likely (args->nsid == LM_ID_BASE) && > + __glibc_unlikely (mode & RTLD_SHARED)) > + { > + args->mode &= ~RTLD_SHARED; > + mode &= ~RTLD_SHARED; > + want_proxy = 0; > + } > + > /* The namespace ID is now known. Keep track of whether libc.so was > already loaded, to determine whether it is necessary to call the > early initialization routine (or clear libc_map on error). */ > @@ -521,6 +540,24 @@ dl_open_worker (void *a) > may not be true if this is a recursive call to dlopen. */ > _dl_debug_initialize (0, args->nsid); > > + /* Target Lmid is not the base and we haven't explicitly asked for a proxy: > + We need to check for a matching DSO in the base Lmid in case it is flagged > + DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE in which case we add RTLD_SHARED to the > + mode and set want_proxy. > + NOTE: RTLD_ISOLATE in the mode suppresses this behaviour. */ > + if (__glibc_unlikely (args->nsid != LM_ID_BASE) && > + __glibc_likely (!dl_isolate) && > + __glibc_likely (!want_proxy)) > + { > + preloaded = _dl_find_dso (file, LM_ID_BASE); > + > + if ((preloaded != NULL) && (preloaded->l_gnu_flags_1 & DF_GNU_1_UNIQUE)) > + { > + want_proxy = true; > + mode |= RTLD_SHARED; > + } > + } > + > /* Load the named object. */ > struct link_map *new; > args->map = new = _dl_map_object (call_map, file, lt_loaded, 0, > @@ -541,6 +578,35 @@ dl_open_worker (void *a) > /* This object is directly loaded. */ > ++new->l_direct_opencount; > > + /* Proxy already existed in the target ns, nothing left to do. */ > + if (__glibc_unlikely (new->l_proxy)) > + { > + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) > + _dl_debug_printf ("proxied file=%s [%lu]; direct_opencount=%u\n\n", > + new->l_name, new->l_ns, new->l_direct_opencount); > + return; > + } > + > + /* If we are trying to load a DF_GNU_1_UNIQUE flagged DSO which was > + NOT ALREADY LOADED (or not loaded with the name we are using) then > + _dl_map_object will have returned an instance from the main namespace. > + We need to detect this and set up the RTLD_SHARED flags. */ > + if (__glibc_unlikely (args->nsid != LM_ID_BASE && new->l_ns == LM_ID_BASE)) > + { > + want_proxy = RTLD_SHARED; > + mode |= RTLD_SHARED; > + } > + > + /* If we want proxy and we get this far then the entry in 'new' will > + be in the main namespace: we should revert to the main namespace code > + path(s), but remember the namespace we want the proxy to be in. */ > + if (__glibc_unlikely (want_proxy)) > + { > + proxy_ns = args->nsid; > + args->nsid = LM_ID_BASE; > + args->libc_already_loaded = GL(dl_ns)[LM_ID_BASE].libc_map != NULL; > + } > + > /* It was already open. */ > if (__glibc_unlikely (new->l_searchlist.r_list != NULL)) > { > @@ -572,6 +638,16 @@ dl_open_worker (void *a) > > assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); > > + if (__glibc_unlikely (want_proxy)) > + { > + args->map = new = _dl_new_proxy (new, mode, proxy_ns); > + ++new->l_direct_opencount; > + > + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) > + _dl_debug_printf ("proxying file=%s [%lu]; direct_opencount=%u\n\n", > + new->l_name, new->l_ns, new->l_direct_opencount); > + } > + > return; > } > > @@ -582,7 +658,8 @@ dl_open_worker (void *a) > > /* Load that object's dependencies. */ > _dl_map_object_deps (new, NULL, 0, 0, > - mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT)); > + mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT | > + RTLD_ISOLATE)); > > /* So far, so good. Now check the versions. */ > for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) > @@ -753,16 +830,21 @@ dl_open_worker (void *a) > namespace. */ > if (!args->libc_already_loaded) > { > + /* If this is a secondary (nsid != LM_ID_BASE) namespace then > + it is POSSIBLE there's no libc_map at all - We use the one > + shared with LM_ID_BASE instead (which MUST already be > + initialised for us to even reach here). */ > struct link_map *libc_map = GL(dl_ns)[args->nsid].libc_map; > #ifdef SHARED > - bool initial = libc_map->l_ns == LM_ID_BASE; > + bool initial = (libc_map != NULL) && (libc_map->l_real->l_ns == LM_ID_BASE); > #else > /* In the static case, there is only one namespace, but it > contains a secondary libc (the primary libc is statically > linked). */ > bool initial = false; > #endif > - _dl_call_libc_early_init (libc_map, initial); > + if (libc_map != NULL) > + _dl_call_libc_early_init (libc_map, initial); > } > > #ifndef SHARED > @@ -787,10 +869,27 @@ dl_open_worker (void *a) > if (mode & RTLD_GLOBAL) > add_to_global_update (new); > > + if (__glibc_unlikely (want_proxy)) > + { > + /* args->map is the return slot which the caller sees, but keep > + the original value of new hanging around so we can see the > + real link map entry (for logging etc). */ > + args->map = _dl_new_proxy (new, mode, proxy_ns); > + ++args->map->l_direct_opencount; > + } > + > /* Let the user know about the opencount. */ > if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) > - _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", > - new->l_name, new->l_ns, new->l_direct_opencount); > + { > + _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", > + new->l_name, new->l_ns, new->l_direct_opencount); > + > + if (args->map->l_proxy) > + _dl_debug_printf ("proxying file=%s [%lu]; direct_opencount=%u\n\n", > + args->map->l_name, > + args->map->l_ns, > + args->map->l_direct_opencount); > + } > } > > void * > diff --git a/elf/dl-sym.c b/elf/dl-sym.c > index dfd6169e12..b7252804ad 100644 > --- a/elf/dl-sym.c > +++ b/elf/dl-sym.c > @@ -96,6 +96,10 @@ do_sym (void *handle, const char *name, void *who, > { > match = _dl_sym_find_caller_link_map (caller); > > + /* Proxies don't contain any symbols: Need to look at the real DSO. */ > + if (__glibc_unlikely (match->l_proxy)) > + match = match->l_real; > + > /* Search the global scope. We have the simple case where > we look up in the scope of an object which was part of > the initial binary. And then the more complex part > @@ -140,6 +144,11 @@ RTLD_NEXT used in code not dynamically loaded")); > } > > struct link_map *l = match; > + > + /* Proxies don't contain any symbols: Need to look at the real DSO. */ > + if (__glibc_unlikely (l->l_proxy)) > + l = l->l_real; > + > while (l->l_loader != NULL) > l = l->l_loader; > > @@ -150,6 +159,11 @@ RTLD_NEXT used in code not dynamically loaded")); > { > /* Search the scope of the given object. */ > struct link_map *map = handle; > + > + /* Proxies don't contain any symbols: Need to look at the real DSO. */ > + if (__glibc_unlikely (map->l_proxy)) > + map = map->l_real; > + > result = GLRO(dl_lookup_symbol_x) (name, map, &ref, map->l_local_scope, > vers, 0, flags, NULL); > } > diff --git a/elf/rtld.c b/elf/rtld.c > index 94a00e2049..e02ae18c1d 100644 > --- a/elf/rtld.c > +++ b/elf/rtld.c > @@ -660,7 +660,7 @@ dlmopen_doit (void *a) > struct dlmopen_args *args = (struct dlmopen_args *) a; > args->map = _dl_open (args->fname, > (RTLD_LAZY | __RTLD_DLOPEN | __RTLD_AUDIT > - | __RTLD_SECURE), > + | __RTLD_SECURE | RTLD_ISOLATE), > dl_main, LM_ID_NEWLM, _dl_argc, _dl_argv, > __environ); > } > diff --git a/include/link.h b/include/link.h > index 6ed35eb808..55e0cad71d 100644 > --- a/include/link.h > +++ b/include/link.h > @@ -107,8 +107,9 @@ struct link_map > 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 for ld.so when it is used in more > - than one namespace. */ > + 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 link_map *l_real; > > /* Number of the namespace this link map belongs to. */ > @@ -180,6 +181,7 @@ struct link_map > 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. */ > diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h > index 08791fbc0f..641176e7d9 100644 > --- a/sysdeps/generic/ldsodefs.h > +++ b/sysdeps/generic/ldsodefs.h > @@ -978,6 +978,11 @@ extern lookup_t _dl_lookup_symbol_x (const char *undef, > struct link_map *skip_map) > attribute_hidden; > > +/* Proxy an existing link map entry into a new link map */ > +extern struct link_map *_dl_new_proxy (struct link_map *old, > + int mode, > + Lmid_t nsid) > + attribute_hidden; > > /* Restricted version of _dl_lookup_symbol_x. Searches MAP (and only > MAP) for the symbol UNDEF_NAME, with GNU hash NEW_HASH (computed > diff --git a/sysdeps/mips/bits/dlfcn.h b/sysdeps/mips/bits/dlfcn.h > index 5cec898de3..898797f863 100644 > --- a/sysdeps/mips/bits/dlfcn.h > +++ b/sysdeps/mips/bits/dlfcn.h > @@ -32,6 +32,16 @@ > visible as if the object were linked directly into the program. */ > #define RTLD_GLOBAL 0x0004 > > +/* If the following bit is set in the MODE argument to dlmopen > + then the target object is loaded into the main namespace (if > + it is not already there) and a shallow copy (proxy) is placed > + in the target namespace: This allows multiple namespaces to > + share a single instance of a DSO. */ > +#define RTLD_SHARED 0x00020 > + > +/* Suppress RTLD_SHARED and/or DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE */ > +#define RTLD_ISOLATE 0x00040 > + > /* Unix98 demands the following flag which is the inverse to RTLD_GLOBAL. > The implementation does this by default and so we can define the > value to zero. */ >