From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS17314 8.43.84.0/22 X-Spam-Status: No, score=-3.2 required=3.0 tests=AWL,BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,MAILING_LIST_MULTI,NICE_REPLY_A, RCVD_IN_DNSWL_MED,SPF_HELO_PASS,SPF_PASS,URIBL_BLACK shortcircuit=no autolearn=no autolearn_force=no version=3.4.2 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 dcvr.yhbt.net (Postfix) with ESMTPS id 2F8C41F8C6 for ; Mon, 9 Aug 2021 19:08:19 +0000 (UTC) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2D7AF385AC25 for ; Mon, 9 Aug 2021 19:08:18 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2D7AF385AC25 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1628536098; bh=fxEcRRXjXbNIrbzyDO2qaj41bE+BBKZlyVhlkYx+kyw=; h=Subject:To:References:Date:In-Reply-To:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=PIaAdzKODWJunmpVxoatqMzlq2R4rPKnIURUrZXnzJLV8E2hVCqTN9+mfKdaICTUf rzou+/nwjIkN9jFkOK9Cw7gFq/dIQDSW7wS9A8F2Cl7+bLV2+RroLr3Oyr0F+QzJoD UvhwZ9/n1YaVA+Ia2yf8fDKMQJ55BJencQlHGylk= Received: from mail-pj1-x102c.google.com (mail-pj1-x102c.google.com [IPv6:2607:f8b0:4864:20::102c]) by sourceware.org (Postfix) with ESMTPS id AE883385800E for ; Mon, 9 Aug 2021 19:07:45 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org AE883385800E Received: by mail-pj1-x102c.google.com with SMTP id cp15-20020a17090afb8fb029017891959dcbso444962pjb.2 for ; Mon, 09 Aug 2021 12:07:45 -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=fxEcRRXjXbNIrbzyDO2qaj41bE+BBKZlyVhlkYx+kyw=; b=A4q6Oa5TfgK72JjNSid7ORQw8mS5jgCvr8GuJ0H7orAdtobRoSrT+FYtt5cMlagr8j jGbMK3WnHYBG7D0x9XtQgoukLV3cIlzEgWjS2h/G4vimhMUjv4RENwKmLpdAahvi2P+S iUyC97eAZ366uBQ96SluR2CqQYsDg1q+UC6wUNkXeTQ/UCyApA2cTC6tA8nr8RmLR3TD BNEMw1Olb2EvQUaC1UgDXZGdrsJOESi7DqEBCWe4SMhwjIiiq9ka1OmvhcTicIjMfV0s pyXkz9Aea4kExri7/a5nbFzXCwLfCxyq1pHOC+Ru28AnHcHB7r9+5q0wCn/XcPiiYnPu zYPw== X-Gm-Message-State: AOAM5331ucH78Sr7Oa5rpmAN+VA7FpsEKKX4JIj86xjZwM5MmuT827h4 EBFVX6JazWSI6bLe+oNKN4IsYJinl1a5xw== X-Google-Smtp-Source: ABdhPJxtMltGZCeO0ttK2WM2dFLrFEftZihjtIJrajLThQH3f3oHcj4CZijdZFZAa7vrRFcG6I3PUg== X-Received: by 2002:aa7:9289:0:b029:3a9:d265:e413 with SMTP id j9-20020aa792890000b02903a9d265e413mr6786901pfa.8.1628536064444; Mon, 09 Aug 2021 12:07:44 -0700 (PDT) Received: from ?IPv6:2804:431:c7cb:9dce:a96c:8a7f:2ffc:f373? ([2804:431:c7cb:9dce:a96c:8a7f:2ffc:f373]) by smtp.gmail.com with ESMTPSA id m7sm24283214pgl.67.2021.08.09.12.07.42 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Mon, 09 Aug 2021 12:07:43 -0700 (PDT) Subject: Re: [RFC][PATCH v12 5/8] Implement dlmopen RTLD_SHARED flag (bug 22745) To: Vivek Das Mohapatra , libc-alpha@sourceware.org References: <20210708163255.812-1-vivek@collabora.com> <20210708163255.812-6-vivek@collabora.com> Message-ID: Date: Mon, 9 Aug 2021 16:07:41 -0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.11.0 MIME-Version: 1.0 In-Reply-To: <20210708163255.812-6-vivek@collabora.com> Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit 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: , From: Adhemerval Zanella via Libc-alpha Reply-To: Adhemerval Zanella Errors-To: libc-alpha-bounces+e=80x24.org@sourceware.org Sender: "Libc-alpha" On 08/07/2021 13:32, 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 Patch look good in general, but I have some comments and improvements below. The main change is to remove the 'has_gnu_unique' function and move the code on 'open_verify()'. There are also multiple indentation mismatches, where the used one differs from what the current files uses. > --- > bits/dlfcn.h | 10 ++ > elf/dl-close.c | 43 +++++---- > elf/dl-deps.c | 17 ++++ > elf/dl-fini.c | 6 +- > elf/dl-init.c | 4 +- > elf/dl-load.c | 181 +++++++++++++++++++++++++++++++++---- > elf/dl-lookup.c | 26 +++++- > elf/dl-object.c | 78 ++++++++++++++++ > elf/dl-open.c | 121 +++++++++++++++++++++++-- > elf/dl-sym.c | 14 +++ > elf/rtld.c | 2 +- > include/link.h | 6 +- > sysdeps/generic/ldsodefs.h | 7 ++ > sysdeps/mips/bits/dlfcn.h | 10 ++ > 14 files changed, 473 insertions(+), 52 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 I think you meant a period instead of a ':' here. > + 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 3720e47dd1..bcdddc75c5 100644 > --- a/elf/dl-close.c > +++ b/elf/dl-close.c > @@ -284,8 +284,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, Ok. > @@ -361,7 +362,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. */ Follow the already set indentation (using tabs instead of space). > @@ -689,8 +692,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 Same as before > @@ -730,19 +735,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); Ok (module the indentation). > diff --git a/elf/dl-deps.c b/elf/dl-deps.c > index 087a49b212..b06134c512 100644 > --- a/elf/dl-deps.c > +++ b/elf/dl-deps.c > @@ -66,6 +66,23 @@ openaux (void *a) > ? lt_library : args->map->l_type), > args->trace_mode, args->open_mode, > args->map->l_ns); > + > + /* This implies that we need to prepare a proxy in the target namespace. */ > + if (__glibc_unlikely (args->map->l_ns != LM_ID_BASE && > + args->aux->l_ns == LM_ID_BASE)) > + { > + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) > + _dl_debug_printf ("need proxy for file=%s [%lu]; initstate=%ld\n\n", > + args->aux->l_name, args->aux->l_ns, > + (long int)args->aux->l_real->l_relocated); There is no need to cast here, since the type of bitfiels will be 'int'. > + > + args->aux = _dl_new_proxy (args->aux, args->open_mode, args->map->l_ns); > + > + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) > + _dl_debug_printf ("proxying dependency=%s [%lu]; direct_opencount=%u\n\n", > + args->aux->l_name, args->aux->l_ns, > + args->aux->l_direct_opencount); > + } > } > > static ptrdiff_t Ok. > 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; Ok. > 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 Ok. > diff --git a/elf/dl-load.c b/elf/dl-load.c > index 050c64135a..19b8eb64bc 100644 > --- a/elf/dl-load.c > +++ b/elf/dl-load.c > @@ -854,6 +854,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); Line too long. > + > + if (__glibc_unlikely (bytes != sizeof (ElfW(Dyn)))) > + goto done; We should throw a "cannot read file data" for this case instead of use the default LM_ID_BASE. > + > + pos += bytes; > + > + switch (entry.d_tag) > + { > + case DT_GNU_FLAGS_1: > + unique = (entry.d_un.d_val & DF_GNU_1_UNIQUE); > + goto done; Just use 'return' here and remove the goto. > + break; > + > + case DT_NULL: > + goto done; > + break; > + } > + } > + break; > + } > + } > + > + done: > + return unique; > +} > I think there is no need to read the program headers again to check if the has the gnu unique flag since on open_verify we already do it for ".note.ABI-tag". Instead I think we should set a flag on open_verify() and use it later. > /* Process PT_GNU_PROPERTY program header PH in module L after > PT_LOAD segments are mapped. Only one NT_GNU_PROPERTY_TYPE_0 > @@ -1037,6 +1084,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 Double space after period. > + 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 Ok. > @@ -1062,8 +1135,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)) Use '!head->l_auditing" to be consistent with other cases. > { > struct audit_ifaces *afct = GLRO(dl_audit); > for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) > @@ -1089,6 +1165,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; > + } > + } > + Ok, you are moving the code below to check if DT_GNU_FLAGS_1/DF_GNU_1_UNIQUE. > + /* 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)) > @@ -1106,20 +1208,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. */ > @@ -2027,6 +2115,37 @@ open_path (const char *name, size_t namelen, int mode, > return -1; > } > Ok. > +/* 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) Ok. > @@ -2080,12 +2199,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; > } > Ok. Do we have a test for it? > diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c > index eea217eb28..42effd8e4d 100644 > --- a/elf/dl-lookup.c > +++ b/elf/dl-lookup.c > @@ -24,6 +24,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -917,11 +918,28 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, > } > } > > + /* Found a candidate symbol but it resides in the base namespace > + BUT we are in another namespace. Therefore we want a proxy > + (which should already have been created by this point. */ > + if (__glibc_likely ((current_value.m != NULL) && > + !(IS_RTLD (current_value.m)))) > + if (__glibc_unlikely (undef_map->l_ns != LM_ID_BASE && > + current_value.m->l_ns == LM_ID_BASE)) > + { I think it would be simpler to just write the comparison without trying to be too clever with branch prediction: if (current_value.m != NULL && !IS_RTLD (current_value.m) && undef_map->l_ns != LM_ID_BASE && current_value.m->l_name == LM_ID_BASE) > + struct link_map *proxy = NULL; > + proxy = _dl_find_proxy (undef_map->l_ns, current_value.m->l_name); Just attribute the proxy directly: struct link_map *proxy = _dl_find_proxy (undef_map->l_ns, current_value.m->l_name); > + > + if (proxy != NULL) > + current_value.m = proxy; > + else > + _dl_debug_printf ("Failed to find proxy for %s\n", current_value.m->l_name); All _dl_debug_printf() call are only enable if uses does set LD_DEBUG, we should do the same here: else if (GLRO(dl_debug_mask) & DL_DEBUG_SYMBOLS) _dl_debug_printf ("Failed to find proxy for %s\n", current_value.m->l_name); > + } > + > /* We have to check whether this would bind UNDEF_MAP to an object > in the global scope which was dynamically loaded. In this case > we have to prevent the latter from being unloaded unless the > UNDEF_MAP object is also unloaded. */ > - if (__glibc_unlikely (current_value.m->l_type == lt_loaded) > + if (__glibc_unlikely (current_value.m->l_real->l_type == lt_loaded) > /* Don't do this for explicit lookups as opposed to implicit > runtime lookups. */ > && (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0 Ok. > @@ -935,8 +953,8 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, > version, type_class, flags, skip_map); > > /* The object is used. */ > - if (__glibc_unlikely (current_value.m->l_used == 0)) > - current_value.m->l_used = 1; > + if (__glibc_unlikely (current_value.m->l_real->l_used == 0)) > + current_value.m->l_real->l_used = 1; > > if (__glibc_unlikely (GLRO(dl_debug_mask) > & (DL_DEBUG_BINDINGS|DL_DEBUG_PRELINK))) > @@ -944,7 +962,7 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, > ¤t_value, version, type_class, protected); > > *ref = current_value.s; > - return LOOKUP_VALUE (current_value.m); > + return LOOKUP_VALUE (current_value.m->l_real); > } > > Ok. > 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. */ Ok. > diff --git a/elf/dl-open.c b/elf/dl-open.c > index a066f39bd0..8eed817980 100644 > --- a/elf/dl-open.c > +++ b/elf/dl-open.c > @@ -488,6 +488,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 Ok. > @@ -512,6 +522,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). */ Ok. > @@ -525,6 +544,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, Ok. > @@ -545,6 +582,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)) > { Ok. > @@ -576,6 +642,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; > } > Ok. > @@ -586,7 +662,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) Ok. > @@ -680,7 +757,10 @@ dl_open_worker (void *a) > { > l = new->l_initfini[i]; > > - if (l->l_real->l_relocated) > + if (l->l_proxy) > + l = l->l_real; > + > + if (l->l_relocated) > continue; > > if (! relocation_in_progress) Ok. > @@ -769,16 +849,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); > } > This is already fixed by 3908fa933a4354. > /* Run the initializer functions of new objects. Temporarily > @@ -799,10 +884,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); > + } > } > Ok. > void * > @@ -891,8 +993,11 @@ no more namespaces available for dlmopen()")); > if (__glibc_unlikely (exception.errstring != NULL)) > { > /* Avoid keeping around a dangling reference to the libc.so link > - map in case it has been cached in libc_map. */ > - if (!args.libc_already_loaded) > + map in case it has been cached in libc_map. > + If this is a secondary namespace opened with LM_ID_NEWLM and > + WITHOUT RTLD_ISOLATE then nsid can still be -1 (LM_ID_NEWLM) > + at this point. */ > + if (!args.libc_already_loaded && nsid >= LM_ID_BASE) > GL(dl_ns)[nsid].libc_map = NULL; > > /* Remove the object from memory. It may be in an inconsistent Ok. > diff --git a/elf/dl-sym.c b/elf/dl-sym.c > index fa0cce678f..966079915a 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 Ok. > @@ -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); > } Ok. > diff --git a/elf/rtld.c b/elf/rtld.c > index fbbd60b446..9246200be1 100644 > --- a/elf/rtld.c > +++ b/elf/rtld.c > @@ -661,7 +661,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); > } Ok. > 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. */ Ok. > diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h > index 44e7097712..66cfc1deb3 100644 > --- a/sysdeps/generic/ldsodefs.h > +++ b/sysdeps/generic/ldsodefs.h > @@ -1015,6 +1015,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 > @@ -1290,6 +1295,8 @@ rtld_hidden_proto (_dl_find_dso_for_object) > extern struct link_map *_dl_find_dso (const char *name, Lmid_t nsid); > rtld_hidden_proto (_dl_find_dso) > > +extern struct link_map *_dl_find_proxy (Lmid_t nsid, const char *name); > +rtld_hidden_proto (_dl_find_proxy) > > /* Initialization which is normally done by the dynamic linker. */ > extern void _dl_non_dynamic_init (void) Ok. > 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. */ > Ok.