From patchwork Fri Feb 10 14:07:43 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: stsp X-Patchwork-Id: 64669 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6E8ED3858025 for ; Fri, 10 Feb 2023 14:08:15 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6E8ED3858025 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1676038095; bh=C+nDotYE4WpQ7hGhms4TfaPahaQgwggizU7CU46d9s8=; h=To:Cc:Subject:Date:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From:Reply-To:From; b=NvPhan6YIGCxzWKgPYXj/N6xHE9Tduwb5QfBYOobBx6TO31hY+CyrSypnXbBNJKuT 6ct6zTMVPu+rlipOKQxFiEJHdRMGcAlUR+lx6LGv82wpMdd+hhnZYJX1VOgrmzy0E0 wyGJI6sV6faGn8RDehuBna6L9oCNWdJbCzvq+F7s= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from forward105o.mail.yandex.net (forward105o.mail.yandex.net [37.140.190.183]) by sourceware.org (Postfix) with ESMTPS id 5FB063858D28 for ; Fri, 10 Feb 2023 14:07:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5FB063858D28 Received: from forward100q.mail.yandex.net (forward100q.mail.yandex.net [IPv6:2a02:6b8:c0e:4b:0:640:4012:bb97]) by forward105o.mail.yandex.net (Yandex) with ESMTP id 8381F4C3E8C for ; Fri, 10 Feb 2023 17:07:50 +0300 (MSK) Received: from vla3-aeadfdeff55e.qloud-c.yandex.net (vla3-aeadfdeff55e.qloud-c.yandex.net [IPv6:2a02:6b8:c15:341d:0:640:aead:fdef]) by forward100q.mail.yandex.net (Yandex) with ESMTP id 7F50E6F40007 for ; Fri, 10 Feb 2023 17:07:50 +0300 (MSK) Received: by vla3-aeadfdeff55e.qloud-c.yandex.net (smtp/Yandex) with ESMTPSA id l7gEnKCZhiE1-koQgyCyR; Fri, 10 Feb 2023 17:07:49 +0300 X-Yandex-Fwd: 1 To: libc-alpha@sourceware.org Cc: Stas Sergeev Subject: [PATCH 1/2] elf: strdup() l_name if no realname [BZ #30100] Date: Fri, 10 Feb 2023 19:07:43 +0500 Message-Id: <20230210140744.2828787-1-stsp2@yandex.ru> X-Mailer: git-send-email 2.37.2 MIME-Version: 1.0 X-Spam-Status: No, score=-10.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, GIT_PATCH_0, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) 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: Stas Sergeev via Libc-alpha From: stsp Reply-To: Stas Sergeev Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" _dl_close_worker() has this code: /* This name always is allocated. */ free (imap->l_name); But in that particular case, while indeed being allocated, l_name doesn't point to the start of an allocation: new = (struct link_map *) calloc (sizeof (*new) + audit_space + sizeof (struct link_map *) + sizeof (*newname) + libname_len, 1); ... new->l_symbolic_searchlist.r_list = (struct link_map **) ((char *) (new + 1) + audit_space); new->l_libname = newname = (struct libname_list *) (new->l_symbolic_searchlist.r_list + 1); newname->name = (char *) memcpy (newname + 1, libname, libname_len); ... new->l_name = (char *) newname->name + libname_len - 1; It therefore cannot be freed separately. Use strdup() as a simple fix. Signed-off-by: Stas Sergeev --- elf/dl-object.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elf/dl-object.c b/elf/dl-object.c index f1f2ec956c..c92daf37d1 100644 --- a/elf/dl-object.c +++ b/elf/dl-object.c @@ -122,7 +122,7 @@ _dl_new_object (char *realname, const char *libname, int type, #endif new->l_name = realname; else - new->l_name = (char *) newname->name + libname_len - 1; + new->l_name = __strdup ((char *) newname->name + libname_len - 1); new->l_type = type; /* If we set the bit now since we know it is never used we avoid From patchwork Fri Feb 10 14:07:44 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: stsp X-Patchwork-Id: 64670 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D637D385B53E for ; Fri, 10 Feb 2023 14:17:43 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D637D385B53E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1676038663; bh=GAm6zmRAUXlr9zUlOcQlS250xQP2l+Jn5LIsHg0eEwg=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=YfVK1r62ieY3fWWWPtkWVOvEXhWkaIaf3keIhe9M6ifu2jOn1zIIBlBuy83H5UXnI 4zOtllwixsauKNGsyhzNmGX6GgGQAT/+7HaRKjt5xHJp8mtzmfxaUpUuyt3WLBH0pt B6bA4kCvDYuZzmTCAURWdudGbH/FFziOZGFVGgrA= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from forward103p.mail.yandex.net (forward103p.mail.yandex.net [77.88.28.106]) by sourceware.org (Postfix) with ESMTPS id E1FB4385840D for ; Fri, 10 Feb 2023 14:17:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E1FB4385840D Received: from forward102q.mail.yandex.net (forward102q.mail.yandex.net [IPv6:2a02:6b8:c0e:1ba:0:640:516:4e7d]) by forward103p.mail.yandex.net (Yandex) with ESMTP id E79DB5A2FED for ; Fri, 10 Feb 2023 17:07:57 +0300 (MSK) Received: from vla3-aeadfdeff55e.qloud-c.yandex.net (vla3-aeadfdeff55e.qloud-c.yandex.net [IPv6:2a02:6b8:c15:341d:0:640:aead:fdef]) by forward102q.mail.yandex.net (Yandex) with ESMTP id E2CF9BF00002 for ; Fri, 10 Feb 2023 17:07:57 +0300 (MSK) Received: by vla3-aeadfdeff55e.qloud-c.yandex.net (smtp/Yandex) with ESMTPSA id l7gEnKCZhiE1-tshLIF2V; Fri, 10 Feb 2023 17:07:56 +0300 X-Yandex-Fwd: 1 To: libc-alpha@sourceware.org Cc: Stas Sergeev Subject: [PATCH 2/2] dlfcn,elf: implement dlmem() function [BZ #11767] Date: Fri, 10 Feb 2023 19:07:44 +0500 Message-Id: <20230210140744.2828787-2-stsp2@yandex.ru> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230210140744.2828787-1-stsp2@yandex.ru> References: <20230210140744.2828787-1-stsp2@yandex.ru> MIME-Version: 1.0 X-Spam-Status: No, score=-10.8 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, GIT_PATCH_0, KAM_DMARC_STATUS, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) 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: Stas Sergeev via Libc-alpha From: stsp Reply-To: Stas Sergeev Errors-To: libc-alpha-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libc-alpha" This patch adds the following function: void *dlmem(const char *buffer, size_t size, int flags); It is the same as dlopen() but allows to dynamic-link solibs from the memory buffer, rather than from a file as dlopen() does. "buffer" arg is the pointer to the solib image in memory. "size" is the solib image size. Must be smaller-or-equal to the actual buffer size. "flags" is the same flags argument used in dlopen(). The idea behind the implementation is very simple: where the dlopen() would mmap() the file, dlmem() does anonymous mmap()+memcpy(). Unfortunately the glibc code was too bound to the file reads, so the patch looks bigger than it should. Some refactorings were needed to avoid big copy/pasts and code duplications. In particular, _dl_map_object_from_fd() was split and now calls 2 functions that are also called from __dl_map_object_from_mem(). The same treatment was applied to open_verify() - the part that can be shared, was split into do_open_verify(). This patch adds a test-case named tst-dlmem. The test-suite was run on x86_64/64 and showed no regressions. Signed-off-by: Stas Sergeev --- dlfcn/Makefile | 5 +- dlfcn/Versions | 1 + dlfcn/dlmem.c | 108 +++ dlfcn/tst-dlmem.c | 92 ++ elf/dl-load.c | 862 +++++++++++------- elf/dl-load.h | 8 +- elf/dl-main.h | 20 + elf/dl-map-segments.h | 23 +- elf/dl-open.c | 37 +- elf/rtld.c | 1 + include/dlfcn.h | 2 + manual/dynlink.texi | 1 + sysdeps/generic/ldsodefs.h | 7 + .../unix/sysv/linux/x86_64/64/libc.abilist | 1 + 14 files changed, 824 insertions(+), 344 deletions(-) create mode 100644 dlfcn/dlmem.c create mode 100644 dlfcn/tst-dlmem.c diff --git a/dlfcn/Makefile b/dlfcn/Makefile index 1fa7fea1ef..c6deef6e43 100644 --- a/dlfcn/Makefile +++ b/dlfcn/Makefile @@ -28,6 +28,7 @@ routines = \ dlclose \ dlerror \ dlinfo \ + dlmem \ dlmopen \ dlopen \ dlsym \ @@ -51,7 +52,8 @@ endif ifeq (yes,$(build-shared)) tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \ bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \ - bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen + bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen tst-dlmem +CPPFLAGS-tst-dlmem.c += -DBUILDDIR=\"$(objpfx)\" endif modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \ defaultmod2 errmsg1mod modatexit modcxaatexit \ @@ -102,6 +104,7 @@ $(objpfx)glrefmain.out: $(objpfx)glrefmain \ $(objpfx)failtest.out: $(objpfx)failtestmod.so $(objpfx)tst-dladdr.out: $(objpfx)glreflib1.so +$(objpfx)tst-dlmem.out: $(objpfx)glreflib1.so $(objpfx)tst-dlinfo.out: $(objpfx)glreflib3.so LDFLAGS-glreflib3.so = -Wl,-rpath,: diff --git a/dlfcn/Versions b/dlfcn/Versions index cc34eb824d..6ce06cfaaf 100644 --- a/dlfcn/Versions +++ b/dlfcn/Versions @@ -25,6 +25,7 @@ libc { dlinfo; dlmopen; dlopen; + dlmem; dlsym; dlvsym; } diff --git a/dlfcn/dlmem.c b/dlfcn/dlmem.c new file mode 100644 index 0000000000..d02dffe636 --- /dev/null +++ b/dlfcn/dlmem.c @@ -0,0 +1,108 @@ +/* Load a shared object from memory. + Copyright (C) 1995-2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include + +struct dlmem_args +{ + /* The arguments for dlmem_doit. */ + const char *buffer; + size_t size; + int mode; + /* The return value of dlmem_doit. */ + void *new; + /* Address of the caller. */ + const void *caller; +}; + + +/* Non-shared code has no support for multiple namespaces. */ +#ifdef SHARED +# define NS __LM_ID_CALLER +#else +# define NS LM_ID_BASE +#endif + + +static void +dlmem_doit (void *a) +{ + struct dlmem_args *args = (struct dlmem_args *) a; + + if (args->mode & ~(RTLD_BINDING_MASK | RTLD_NOLOAD | RTLD_DEEPBIND + | RTLD_GLOBAL | RTLD_LOCAL | RTLD_NODELETE + | __RTLD_SPROF)) + _dl_signal_error (0, NULL, NULL, _("invalid mode parameter")); + + args->new = GLRO(dl_mem) (args->buffer, args->size, + args->mode | __RTLD_DLOPEN, + args->caller, + NS, + __libc_argc, __libc_argv, __environ); +} + + +static void * +dlmem_implementation (const char *buffer, size_t size, int mode, + void *dl_caller) +{ + struct dlmem_args args; + args.buffer = buffer; + args.size = size; + args.mode = mode; + args.caller = dl_caller; + + return _dlerror_run (dlmem_doit, &args) ? NULL : args.new; +} + +#ifdef SHARED +void * +___dlmem (const char *buffer, size_t size, int mode) +{ + if (GLRO (dl_dlfcn_hook) != NULL) + return GLRO (dl_dlfcn_hook)->dlmem (buffer, size, mode, + RETURN_ADDRESS (0)); + else + return dlmem_implementation (buffer, size, mode, RETURN_ADDRESS (0)); +} +versioned_symbol (libc, ___dlmem, dlmem, GLIBC_2_34); + +# if OTHER_SHLIB_COMPAT (libdl, GLIBC_2_1, GLIBC_2_34) +compat_symbol (libdl, ___dlmem, dlmem, GLIBC_2_1); +# endif +#else /* !SHARED */ +/* Also used with _dlfcn_hook. */ +void * +__dlmem (const char *buffer, size_t size, int mode, void *dl_caller) +{ + return dlmem_implementation (buffer, size, mode, dl_caller); +} + +void * +___dlmem (const char *buffer, size_t size, int mode) +{ + return __dlmem (buffer, size, mode, RETURN_ADDRESS (0)); +} +weak_alias (___dlmem, dlmem) +static_link_warning (dlmem) +#endif /* !SHARED */ diff --git a/dlfcn/tst-dlmem.c b/dlfcn/tst-dlmem.c new file mode 100644 index 0000000000..dd1cb477e2 --- /dev/null +++ b/dlfcn/tst-dlmem.c @@ -0,0 +1,92 @@ +/* Test for dlmem. + Copyright (C) 2000-2022 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define TEST_FUNCTION do_test () +extern int do_test (void); +void *dlmem(const char *buffer, size_t size, int flags); + +int +do_test (void) +{ + void *handle; + void *addr; + int (*sym) (void); /* We load ref1 from glreflib1.c. */ + Dl_info info; + int ret; + int fd; + off_t len; + off_t orig_len; + + fd = open (BUILDDIR "glreflib1.so", O_RDONLY); + if (fd == -1) + error (EXIT_FAILURE, 0, "cannot open: glreflib1.so"); + len = lseek (fd, 0, SEEK_END); + lseek (fd, 0, SEEK_SET); + orig_len = len; + len = (len + getpagesize() - 1) & ~(getpagesize() - 1); + addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + if (addr == MAP_FAILED) + error (EXIT_FAILURE, 0, "cannot mmap: glreflib1.so"); + handle = dlmem (addr, orig_len, RTLD_NOW); + if (handle == NULL) + error (EXIT_FAILURE, 0, "cannot load: glreflib1.so"); + munmap (addr, len); + + sym = dlsym (handle, "ref1"); + if (sym == NULL) + error (EXIT_FAILURE, 0, "dlsym failed"); + + memset (&info, 0, sizeof (info)); + ret = dladdr (sym, &info); + + if (ret == 0) + error (EXIT_FAILURE, 0, "dladdr failed"); + + printf ("ret = %d\n", ret); + printf ("info.dli_fname = %p (\"%s\")\n", info.dli_fname, info.dli_fname); + printf ("info.dli_fbase = %p\n", info.dli_fbase); + printf ("info.dli_sname = %p (\"%s\")\n", info.dli_sname, info.dli_sname); + printf ("info.dli_saddr = %p\n", info.dli_saddr); + + if (info.dli_fname == NULL) + error (EXIT_FAILURE, 0, "dli_fname is NULL"); + if (info.dli_fbase == NULL) + error (EXIT_FAILURE, 0, "dli_fbase is NULL"); + if (info.dli_sname == NULL) + error (EXIT_FAILURE, 0, "dli_sname is NULL"); + if (info.dli_saddr == NULL) + error (EXIT_FAILURE, 0, "dli_saddr is NULL"); + + dlclose (handle); + + return 0; +} + + +#include "../test-skeleton.c" diff --git a/elf/dl-load.c b/elf/dl-load.c index fcb39a78d4..c65f58794d 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -55,7 +55,8 @@ struct filebuf #else # define FILEBUF_SIZE 832 #endif - char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr))))); + ssize_t allocated; + char *buf; }; #include "dynamic-link.h" @@ -74,6 +75,7 @@ struct filebuf #include #include #include +#include #include #include @@ -124,6 +126,29 @@ static const size_t system_dirs_len[] = }; #define nsystem_dirs_len array_length (system_dirs_len) +static void +filebuf_done (struct filebuf *fb) +{ + free (fb->buf); + fb->buf = NULL; + fb->allocated = 0; +} + +static bool +filebuf_ensure (struct filebuf *fb, size_t size) +{ + bool ret = false; + + if (size > fb->allocated) + { + size_t new_len = size + FILEBUF_SIZE; + fb->buf = realloc (fb->buf, new_len); + fb->allocated = new_len; + ret = true; + } + return ret; +} + static bool is_trusted_path_normalize (const char *path, size_t len) { @@ -930,146 +955,26 @@ _dl_process_pt_gnu_property (struct link_map *l, int fd, const ElfW(Phdr) *ph) } -/* Map in the shared object NAME, actually located in REALNAME, and already - opened on FD. */ - -#ifndef EXTERNAL_MAP_FROM_FD -static -#endif -struct link_map * -_dl_map_object_from_fd (const char *name, const char *origname, int fd, - struct filebuf *fbp, char *realname, - struct link_map *loader, int l_type, int mode, - void **stack_endp, Lmid_t nsid) +static int +_ld_map_object_1 (struct link_map *l, const void *fd, + struct filebuf *fbp, + int mode, struct link_map *loader, + void **stack_endp, int *errval_p, + const char **errstring_p, + __typeof (do_mmap) *m_map) { - struct link_map *l = NULL; const ElfW(Ehdr) *header; const ElfW(Phdr) *phdr; const ElfW(Phdr) *ph; size_t maplength; int type; /* Initialize to keep the compiler happy. */ - const char *errstring = NULL; - int errval = 0; - struct r_debug *r = _dl_debug_update (nsid); - bool make_consistent = false; - - /* Get file information. To match the kernel behavior, do not fill - in this information for the executable in case of an explicit - loader invocation. */ - struct r_file_id id; - if (mode & __RTLD_OPENEXEC) - { - assert (nsid == LM_ID_BASE); - memset (&id, 0, sizeof (id)); - } - else - { - if (__glibc_unlikely (!_dl_get_file_id (fd, &id))) - { - errstring = N_("cannot stat shared object"); - lose_errno: - errval = errno; - lose: - /* The file might already be closed. */ - if (fd != -1) - __close_nocancel (fd); - if (l != NULL && l->l_map_start != 0) - _dl_unmap_segments (l); - if (l != NULL && l->l_origin != (char *) -1l) - free ((char *) l->l_origin); - if (l != NULL && !l->l_libname->dont_free) - free (l->l_libname); - if (l != NULL && l->l_phdr_allocated) - free ((void *) l->l_phdr); - free (l); - free (realname); - - if (make_consistent && r != NULL) - { - r->r_state = RT_CONSISTENT; - _dl_debug_state (); - LIBC_PROBE (map_failed, 2, nsid, r); - } - - _dl_signal_error (errval, name, NULL, errstring); - } - - /* Look again to see if the real name matched another already loaded. */ - for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next) - if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id)) - { - /* The object is already loaded. - Just bump its reference count and return it. */ - __close_nocancel (fd); - - /* If the name is not in the list of names for this object add - it. */ - free (realname); - add_name_to_object (l, name); - - return l; - } - } - -#ifdef SHARED - /* When loading into a namespace other than the base one we must - avoid loading ld.so since there can only be one copy. Ever. */ - if (__glibc_unlikely (nsid != LM_ID_BASE) - && (_dl_file_id_match_p (&id, &GL(dl_rtld_map).l_file_id) - || _dl_name_match_p (name, &GL(dl_rtld_map)))) - { - /* This is indeed ld.so. Create a new link_map which refers to - the real one for almost everything. */ - l = _dl_new_object (realname, name, l_type, loader, mode, nsid); - if (l == NULL) - goto fail_new; - - /* Refer to the real descriptor. */ - l->l_real = &GL(dl_rtld_map); - - /* Copy l_addr and l_ld to avoid a GDB warning with dlmopen(). */ - l->l_addr = l->l_real->l_addr; - l->l_ld = l->l_real->l_ld; - - /* No need to bump the refcount of the real object, ld.so will - never be unloaded. */ - __close_nocancel (fd); - - /* Add the map for the mirrored object to the object list. */ - _dl_add_to_namespace_list (l, nsid); - - return l; - } -#endif - - if (mode & RTLD_NOLOAD) - { - /* We are not supposed to load the object unless it is already - loaded. So return now. */ - free (realname); - __close_nocancel (fd); - return NULL; - } - - /* Print debugging message. */ - if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) - _dl_debug_printf ("file=%s [%lu]; generating link map\n", name, nsid); +#define errstring (*errstring_p) +#define errval (*errval_p) /* This is the ELF header. We read it in `open_verify'. */ header = (void *) fbp->buf; - /* 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)) - { -#ifdef SHARED - fail_new: -#endif - errstring = N_("cannot create shared object descriptor"); - goto lose_errno; - } - /* Extract the remaining details we need from the ELF header and then read in the program header table. */ l->l_entry = header->e_entry; @@ -1077,23 +982,13 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, 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; - } - } + assert (header->e_phoff + maplength <= (size_t) fbp->len); + phdr = (void *) (fbp->buf + header->e_phoff); /* 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. */ - unsigned int stack_flags = DEFAULT_STACK_PERMS; + unsigned int stack_flags = DEFAULT_STACK_PERMS; { /* Scan the program header table, collecting its load commands. */ @@ -1266,7 +1161,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, l_map_start, l_map_end, l_addr, l_contiguous, l_text_end, l_phdr */ errstring = _dl_map_segments (l, fd, header, type, loadcmds, nloadcmds, - maplength, has_holes, loader); + maplength, has_holes, loader, m_map); if (__glibc_unlikely (errstring != NULL)) { /* Mappings can be in an inconsistent state: avoid unmap. */ @@ -1379,22 +1274,13 @@ cannot enable executable stack as shared object requires"); switch (ph[-1].p_type) { case PT_NOTE: - _dl_process_pt_note (l, fd, &ph[-1]); + _dl_process_pt_note (l, -1, &ph[-1]); break; case PT_GNU_PROPERTY: - _dl_process_pt_gnu_property (l, fd, &ph[-1]); + _dl_process_pt_gnu_property (l, -1, &ph[-1]); break; } - /* We are done mapping in the file. We no longer need the descriptor. */ - if (__glibc_unlikely (__close_nocancel (fd) != 0)) - { - errstring = N_("cannot close file descriptor"); - goto lose_errno; - } - /* Signal that we closed the file. */ - fd = -1; - /* Failures before this point are handled locally via lose. There are no more failures in this function until return, to change that the cleanup handling needs to be updated. */ @@ -1419,6 +1305,23 @@ cannot enable executable stack as shared object requires"); (unsigned long int) l->l_phdr, (int) sizeof (void *) * 2, l->l_phnum); + return 0; + +lose_errno: + errval = errno; +lose: + return -1; + +#undef errval +#undef errstring +} + +static void +_ld_map_object_2 (struct link_map *l, int mode, + struct r_file_id id, const char *origname, + Lmid_t nsid, struct r_debug *r, + bool *make_consistent_p) +{ /* Set up the symbol hash table. */ _dl_setup_hash (l); @@ -1510,7 +1413,7 @@ cannot enable executable stack as shared object requires"); r->r_state = RT_ADD; _dl_debug_state (); LIBC_PROBE (map_start, 2, nsid, r); - make_consistent = true; + *make_consistent_p = true; } else assert (r->r_state == RT_ADD); @@ -1520,7 +1423,154 @@ cannot enable executable stack as shared object requires"); if (!GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing) _dl_audit_objopen (l, nsid); #endif +} + +/* Map in the shared object NAME, actually located in REALNAME, and already + opened on FD. */ + +#ifndef EXTERNAL_MAP_FROM_FD +static +#endif +struct link_map * +_dl_map_object_from_fd (const char *name, const char *origname, int fd, + struct filebuf *fbp, char *realname, + struct link_map *loader, int l_type, int mode, + void **stack_endp, Lmid_t nsid) +{ + struct link_map *l = NULL; + /* Initialize to keep the compiler happy. */ + const char *errstring = NULL; + int errval = 0; + struct r_debug *r = _dl_debug_update (nsid); + bool make_consistent = false; + /* Get file information. To match the kernel behavior, do not fill + in this information for the executable in case of an explicit + loader invocation. */ + struct r_file_id id; + if (mode & __RTLD_OPENEXEC) + { + assert (nsid == LM_ID_BASE); + memset (&id, 0, sizeof (id)); + } + else + { + if (__glibc_unlikely (!_dl_get_file_id (fd, &id))) + { + errstring = N_("cannot stat shared object"); + lose_errno: + errval = errno; + lose: + /* The file might already be closed. */ + if (fd != -1) + __close_nocancel (fd); + if (l != NULL && l->l_map_start != 0) + _dl_unmap_segments (l); + if (l != NULL && l->l_origin != (char *) -1l) + free ((char *) l->l_origin); + if (l != NULL && !l->l_libname->dont_free) + free (l->l_libname); + if (l != NULL && l->l_phdr_allocated) + free ((void *) l->l_phdr); + free (l); + free (realname); + + if (make_consistent && r != NULL) + { + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + LIBC_PROBE (map_failed, 2, nsid, r); + } + + _dl_signal_error (errval, name, NULL, errstring); + } + + /* Look again to see if the real name matched another already loaded. */ + for (l = GL(dl_ns)[nsid]._ns_loaded; l != NULL; l = l->l_next) + if (!l->l_removed && _dl_file_id_match_p (&l->l_file_id, &id)) + { + /* The object is already loaded. + Just bump its reference count and return it. */ + __close_nocancel (fd); + + /* If the name is not in the list of names for this object add + it. */ + free (realname); + add_name_to_object (l, name); + + return l; + } + } + +#ifdef SHARED + /* When loading into a namespace other than the base one we must + avoid loading ld.so since there can only be one copy. Ever. */ + if (__glibc_unlikely (nsid != LM_ID_BASE) + && (_dl_file_id_match_p (&id, &GL(dl_rtld_map).l_file_id) + || _dl_name_match_p (name, &GL(dl_rtld_map)))) + { + /* This is indeed ld.so. Create a new link_map which refers to + the real one for almost everything. */ + l = _dl_new_object (realname, name, l_type, loader, mode, nsid); + if (l == NULL) + goto fail_new; + + /* Refer to the real descriptor. */ + l->l_real = &GL(dl_rtld_map); + + /* Copy l_addr and l_ld to avoid a GDB warning with dlmopen(). */ + l->l_addr = l->l_real->l_addr; + l->l_ld = l->l_real->l_ld; + + /* No need to bump the refcount of the real object, ld.so will + never be unloaded. */ + __close_nocancel (fd); + + /* Add the map for the mirrored object to the object list. */ + _dl_add_to_namespace_list (l, nsid); + + return l; + } +#endif + + if (mode & RTLD_NOLOAD) + { + /* We are not supposed to load the object unless it is already + loaded. So return now. */ + free (realname); + __close_nocancel (fd); + return NULL; + } + + /* Print debugging message. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("file=%s [%lu]; generating link map\n", name, nsid); + + /* 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)) + { +#ifdef SHARED + fail_new: +#endif + errstring = N_("cannot create shared object descriptor"); + goto lose_errno; + } + + if (_ld_map_object_1 (l, &fd, fbp, mode, loader, stack_endp, &errval, + &errstring, do_mmap)) + goto lose; + + /* We are done mapping in the file. We no longer need the descriptor. */ + if (__glibc_unlikely (__close_nocancel (fd) != 0)) + { + errstring = N_("cannot close file descriptor"); + goto lose_errno; + } + /* Signal that we closed the file. */ + fd = -1; + + _ld_map_object_2 (l, mode, id, origname, nsid, r, &make_consistent); return l; } @@ -1566,18 +1616,32 @@ print_search_path (struct r_search_path_elem **list, _dl_debug_printf_c ("\t\t(%s)\n", what); } -/* Open a file and verify it is an ELF file for this architecture. We - ignore only ELF files for other architectures. Non-ELF files and - ELF files with different header information cause fatal errors since - this could mean there is something wrong in the installation and the - user might want to know about this. - If FD is not -1, then the file is already open and FD refers to it. - In that case, FD is consumed for both successful and error returns. */ +static ssize_t +do_pread (const void *arg, void *buf, size_t count, off_t offset) +{ + int fd = *(const int *) arg; + return __pread64_nocancel (fd, buf, count, offset); +} + +static ssize_t +do_pread_memcpy (const void *arg, void *buf, size_t count, off_t offset) +{ + const struct const_fbuf *fb = arg; + if (offset >= fb->len) + return -1; + if (offset + count > fb->len) + count = fb->len - offset; + if (count) + memcpy (buf, fb->buf + offset, count); + return count; +} + static int -open_verify (const char *name, int fd, - struct filebuf *fbp, struct link_map *loader, - int whatcode, int mode, bool *found_other_class, bool free_name) +do_open_verify (const char *name, const void *fd, + struct filebuf *fbp, struct link_map *loader, + bool *found_other_class, bool free_name, + __typeof (do_pread) *__pread64_nocancel) { /* This is the expected ELF header. */ #define ELF32_CLASS ELFCLASS32 @@ -1604,7 +1668,152 @@ open_verify (const char *name, int fd, /* Initialize it to make the compiler happy. */ const char *errstring = NULL; int errval = 0; + ElfW(Ehdr) _ehdr; + ElfW(Ehdr) *ehdr = &_ehdr;; + ElfW(Phdr) *phdr; + size_t maplength; + + /* We successfully opened the file. Now verify it is a file + we can use. */ + __set_errno (0); + /* Read in the header. */ + if (__pread64_nocancel (fd, &_ehdr, sizeof(_ehdr), 0) != sizeof(_ehdr)) + { + errval = errno; + errstring = (errval == 0 + ? N_("file too short") : N_("cannot read file data")); + lose: + if (free_name) + { + char *realname = (char *) name; + name = strdupa (realname); + free (realname); + } + _dl_signal_error (errval, name, NULL, errstring); + return -1; + } + + /* See whether the ELF header is what we expect. */ + if (__glibc_unlikely (! VALID_ELF_HEADER (ehdr->e_ident, expected, + EI_ABIVERSION) + || !VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI], + ehdr->e_ident[EI_ABIVERSION]) + || memcmp (&ehdr->e_ident[EI_PAD], + &expected[EI_PAD], + EI_NIDENT - EI_PAD) != 0)) + { + /* Something is wrong. */ + const Elf32_Word *magp = (const void *) ehdr->e_ident; + if (*magp != +#if BYTE_ORDER == LITTLE_ENDIAN + ((ELFMAG0 << (EI_MAG0 * 8)) + | (ELFMAG1 << (EI_MAG1 * 8)) + | (ELFMAG2 << (EI_MAG2 * 8)) + | (ELFMAG3 << (EI_MAG3 * 8))) +#else + ((ELFMAG0 << (EI_MAG3 * 8)) + | (ELFMAG1 << (EI_MAG2 * 8)) + | (ELFMAG2 << (EI_MAG1 * 8)) + | (ELFMAG3 << (EI_MAG0 * 8))) +#endif + ) + errstring = N_("invalid ELF header"); + + else if (ehdr->e_ident[EI_CLASS] != ELFW(CLASS)) + { + /* This is not a fatal error. On architectures where + 32-bit and 64-bit binaries can be run this might + happen. */ + *found_other_class = true; + __set_errno (ENOENT); + return -1; + } + else if (ehdr->e_ident[EI_DATA] != byteorder) + { + if (BYTE_ORDER == BIG_ENDIAN) + errstring = N_("ELF file data encoding not big-endian"); + else + errstring = N_("ELF file data encoding not little-endian"); + } + else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) + errstring + = N_("ELF file version ident does not match current one"); + /* XXX We should be able so set system specific versions which are + allowed here. */ + else if (!VALID_ELF_OSABI (ehdr->e_ident[EI_OSABI])) + errstring = N_("ELF file OS ABI invalid"); + else if (!VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI], + ehdr->e_ident[EI_ABIVERSION])) + errstring = N_("ELF file ABI version invalid"); + else if (memcmp (&ehdr->e_ident[EI_PAD], &expected[EI_PAD], + EI_NIDENT - EI_PAD) != 0) + errstring = N_("nonzero padding in e_ident"); + else + /* Otherwise we don't know what went wrong. */ + errstring = N_("internal error"); + + goto lose; + } + if (__glibc_unlikely (ehdr->e_version != EV_CURRENT)) + { + errstring = N_("ELF file version does not match current one"); + goto lose; + } + if (! __glibc_likely (elf_machine_matches_host (ehdr))) + { + __set_errno (ENOENT); + return -1; + } + else if (__glibc_unlikely (ehdr->e_type != ET_DYN + && ehdr->e_type != ET_EXEC)) + { + errstring = N_("only ET_DYN and ET_EXEC can be loaded"); + goto lose; + } + else if (__glibc_unlikely (ehdr->e_phentsize != sizeof (ElfW(Phdr)))) + { + errstring = N_("ELF file's phentsize not the expected size"); + goto lose; + } + + maplength = ehdr->e_phnum * sizeof (ElfW(Phdr)); + filebuf_ensure (fbp, maplength + ehdr->e_phoff); + if ((size_t) __pread64_nocancel (fd, fbp->buf, maplength + + ehdr->e_phoff, 0) != maplength + + ehdr->e_phoff) + { + errval = errno; + errstring = N_("cannot read file data"); + goto lose; + } + fbp->len = maplength + ehdr->e_phoff; + phdr = (void *) (fbp->buf + ehdr->e_phoff); + + if (__glibc_unlikely (elf_machine_reject_phdr_p + (phdr, ehdr->e_phnum, fbp->buf, fbp->len, + loader, -1))) + { + __set_errno (ENOENT); + return -1; + } + + return 0; +} + +/* Open a file and verify it is an ELF file for this architecture. We + ignore only ELF files for other architectures. Non-ELF files and + ELF files with different header information cause fatal errors since + this could mean there is something wrong in the installation and the + user might want to know about this. + + If FD is not -1, then the file is already open and FD refers to it. + In that case, FD is consumed for both successful and error returns. */ +static int +open_verify (const char *name, int fd, + struct filebuf *fbp, struct link_map *loader, + int whatcode, int mode, bool *found_other_class, bool free_name) +{ #ifdef SHARED /* Give the auditing libraries a chance. */ if (__glibc_unlikely (GLRO(dl_naudit) > 0)) @@ -1630,156 +1839,14 @@ open_verify (const char *name, int fd, if (fd != -1) { - ElfW(Ehdr) *ehdr; - ElfW(Phdr) *phdr; - size_t maplength; - - /* We successfully opened the file. Now verify it is a file - we can use. */ - __set_errno (0); - fbp->len = 0; - assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr))); - /* Read in the header. */ - do - { - ssize_t retlen = __read_nocancel (fd, fbp->buf + fbp->len, - sizeof (fbp->buf) - fbp->len); - if (retlen <= 0) - break; - fbp->len += retlen; - } - while (__glibc_unlikely (fbp->len < sizeof (ElfW(Ehdr)))); - - /* This is where the ELF header is loaded. */ - ehdr = (ElfW(Ehdr) *) fbp->buf; - - /* Now run the tests. */ - if (__glibc_unlikely (fbp->len < (ssize_t) sizeof (ElfW(Ehdr)))) - { - errval = errno; - errstring = (errval == 0 - ? N_("file too short") : N_("cannot read file data")); - lose: - if (free_name) - { - char *realname = (char *) name; - name = strdupa (realname); - free (realname); - } - __close_nocancel (fd); - _dl_signal_error (errval, name, NULL, errstring); - } - - /* See whether the ELF header is what we expect. */ - if (__glibc_unlikely (! VALID_ELF_HEADER (ehdr->e_ident, expected, - EI_ABIVERSION) - || !VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI], - ehdr->e_ident[EI_ABIVERSION]) - || memcmp (&ehdr->e_ident[EI_PAD], - &expected[EI_PAD], - EI_NIDENT - EI_PAD) != 0)) - { - /* Something is wrong. */ - const Elf32_Word *magp = (const void *) ehdr->e_ident; - if (*magp != -#if BYTE_ORDER == LITTLE_ENDIAN - ((ELFMAG0 << (EI_MAG0 * 8)) - | (ELFMAG1 << (EI_MAG1 * 8)) - | (ELFMAG2 << (EI_MAG2 * 8)) - | (ELFMAG3 << (EI_MAG3 * 8))) -#else - ((ELFMAG0 << (EI_MAG3 * 8)) - | (ELFMAG1 << (EI_MAG2 * 8)) - | (ELFMAG2 << (EI_MAG1 * 8)) - | (ELFMAG3 << (EI_MAG0 * 8))) -#endif - ) - errstring = N_("invalid ELF header"); - - else if (ehdr->e_ident[EI_CLASS] != ELFW(CLASS)) - { - /* This is not a fatal error. On architectures where - 32-bit and 64-bit binaries can be run this might - happen. */ - *found_other_class = true; - __close_nocancel (fd); - __set_errno (ENOENT); - return -1; - } - else if (ehdr->e_ident[EI_DATA] != byteorder) - { - if (BYTE_ORDER == BIG_ENDIAN) - errstring = N_("ELF file data encoding not big-endian"); - else - errstring = N_("ELF file data encoding not little-endian"); - } - else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) - errstring - = N_("ELF file version ident does not match current one"); - /* XXX We should be able so set system specific versions which are - allowed here. */ - else if (!VALID_ELF_OSABI (ehdr->e_ident[EI_OSABI])) - errstring = N_("ELF file OS ABI invalid"); - else if (!VALID_ELF_ABIVERSION (ehdr->e_ident[EI_OSABI], - ehdr->e_ident[EI_ABIVERSION])) - errstring = N_("ELF file ABI version invalid"); - else if (memcmp (&ehdr->e_ident[EI_PAD], &expected[EI_PAD], - EI_NIDENT - EI_PAD) != 0) - errstring = N_("nonzero padding in e_ident"); - else - /* Otherwise we don't know what went wrong. */ - errstring = N_("internal error"); - - goto lose; - } - - if (__glibc_unlikely (ehdr->e_version != EV_CURRENT)) - { - errstring = N_("ELF file version does not match current one"); - goto lose; - } - if (! __glibc_likely (elf_machine_matches_host (ehdr))) - { - __close_nocancel (fd); - __set_errno (ENOENT); - return -1; - } - else if (__glibc_unlikely (ehdr->e_type != ET_DYN - && ehdr->e_type != ET_EXEC)) - { - errstring = N_("only ET_DYN and ET_EXEC can be loaded"); - goto lose; - } - else if (__glibc_unlikely (ehdr->e_phentsize != sizeof (ElfW(Phdr)))) - { - errstring = N_("ELF file's phentsize not the expected size"); - goto lose; - } - - maplength = ehdr->e_phnum * sizeof (ElfW(Phdr)); - if (ehdr->e_phoff + maplength <= (size_t) fbp->len) - phdr = (void *) (fbp->buf + ehdr->e_phoff); - else - { - phdr = alloca (maplength); - if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength, - ehdr->e_phoff) != maplength) - { - errval = errno; - errstring = N_("cannot read file data"); - goto lose; - } - } - - if (__glibc_unlikely (elf_machine_reject_phdr_p - (phdr, ehdr->e_phnum, fbp->buf, fbp->len, - loader, fd))) - { - __close_nocancel (fd); - __set_errno (ENOENT); - return -1; - } - + int err = do_open_verify (name, &fd, fbp, loader, + found_other_class, + free_name, do_pread); + if (err) + { + __close_nocancel (fd); + return -1; + } } return fd; @@ -1946,16 +2013,16 @@ open_path (const char *name, size_t namelen, int mode, /* Map in the shared object file NAME. */ -struct link_map * -_dl_map_object (struct link_map *loader, const char *name, - int type, int trace_mode, int mode, Lmid_t nsid) +static struct link_map * +___dl_map_object (struct link_map *loader, const char *name, + int type, int trace_mode, int mode, Lmid_t nsid, + struct filebuf *fbp) { int fd; const char *origname = NULL; char *realname; char *name_copy; struct link_map *l; - struct filebuf fb; assert (nsid >= 0); assert (nsid < GL(dl_nns)); @@ -2045,7 +2112,7 @@ _dl_map_object (struct link_map *loader, const char *name, { fd = open_path (name, namelen, mode, &l->l_rpath_dirs, - &realname, &fb, loader, LA_SER_RUNPATH, + &realname, fbp, loader, LA_SER_RUNPATH, &found_other_class); if (fd != -1) break; @@ -2061,7 +2128,7 @@ _dl_map_object (struct link_map *loader, const char *name, "RPATH")) fd = open_path (name, namelen, mode, &main_map->l_rpath_dirs, - &realname, &fb, loader ?: main_map, LA_SER_RUNPATH, + &realname, fbp, loader ?: main_map, LA_SER_RUNPATH, &found_other_class); /* Also try DT_RUNPATH in the executable for LD_AUDIT dlopen @@ -2075,7 +2142,7 @@ _dl_map_object (struct link_map *loader, const char *name, if (cache_rpath (main_map, &l_rpath_dirs, DT_RUNPATH, "RUNPATH")) fd = open_path (name, namelen, mode, &l_rpath_dirs, - &realname, &fb, loader ?: main_map, + &realname, fbp, loader ?: main_map, LA_SER_RUNPATH, &found_other_class); } } @@ -2083,7 +2150,7 @@ _dl_map_object (struct link_map *loader, const char *name, /* Try the LD_LIBRARY_PATH environment variable. */ if (fd == -1 && __rtld_env_path_list.dirs != (void *) -1) fd = open_path (name, namelen, mode, &__rtld_env_path_list, - &realname, &fb, + &realname, fbp, loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded, LA_SER_LIBPATH, &found_other_class); @@ -2092,7 +2159,7 @@ _dl_map_object (struct link_map *loader, const char *name, && cache_rpath (loader, &loader->l_runpath_dirs, DT_RUNPATH, "RUNPATH")) fd = open_path (name, namelen, mode, - &loader->l_runpath_dirs, &realname, &fb, loader, + &loader->l_runpath_dirs, &realname, fbp, loader, LA_SER_RUNPATH, &found_other_class); if (fd == -1) @@ -2101,7 +2168,7 @@ _dl_map_object (struct link_map *loader, const char *name, if (realname != NULL) { fd = open_verify (realname, fd, - &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded, + fbp, loader ?: GL(dl_ns)[nsid]._ns_loaded, LA_SER_CONFIG, mode, &found_other_class, false); if (fd == -1) @@ -2155,7 +2222,7 @@ _dl_map_object (struct link_map *loader, const char *name, if (cached != NULL) { fd = open_verify (cached, -1, - &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded, + fbp, loader ?: GL(dl_ns)[nsid]._ns_loaded, LA_SER_CONFIG, mode, &found_other_class, false); if (__glibc_likely (fd != -1)) @@ -2173,7 +2240,7 @@ _dl_map_object (struct link_map *loader, const char *name, || __glibc_likely (!(l->l_flags_1 & DF_1_NODEFLIB))) && __rtld_search_dirs.dirs != (void *) -1) fd = open_path (name, namelen, mode, &__rtld_search_dirs, - &realname, &fb, l, LA_SER_DEFAULT, &found_other_class); + &realname, fbp, l, LA_SER_DEFAULT, &found_other_class); /* Add another newline when we are tracing the library loading. */ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) @@ -2189,7 +2256,7 @@ _dl_map_object (struct link_map *loader, const char *name, fd = -1; else { - fd = open_verify (realname, -1, &fb, + fd = open_verify (realname, -1, fbp, loader ?: GL(dl_ns)[nsid]._ns_loaded, 0, mode, &found_other_class, true); if (__glibc_unlikely (fd == -1)) @@ -2250,10 +2317,145 @@ _dl_map_object (struct link_map *loader, const char *name, } void *stack_end = __libc_stack_end; - return _dl_map_object_from_fd (name, origname, fd, &fb, realname, loader, + return _dl_map_object_from_fd (name, origname, fd, fbp, realname, loader, type, mode, &stack_end, nsid); } +struct link_map * +__dl_map_object (struct link_map *loader, const char *name, + const void *private, int type, int trace_mode, + int mode, Lmid_t nsid) +{ + struct link_map *ret; + struct filebuf fb = {}; + + ret = ___dl_map_object (loader, name, type, trace_mode, mode, nsid, &fb); + filebuf_done (&fb); + return ret; +} + +struct link_map * +_dl_map_object (struct link_map *loader, const char *name, + int type, int trace_mode, int mode, Lmid_t nsid) +{ + return __dl_map_object (loader, name, NULL, type, trace_mode, mode, nsid); +} + +static void * +do_mmapcpy (void *addr, size_t length, int prot, int flags, + const void *arg, off_t offset) +{ + const struct const_fbuf *fb = arg; + void *ret; + + if (offset + length > fb->len) + return MAP_FAILED; + ret = __mmap (addr, length, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | (flags & MAP_FIXED), -1, 0); + if (ret == MAP_FAILED) + return ret; + memcpy (ret, fb->buf + offset, length); + __mprotect (ret, length, prot); + return ret; +} + +static struct link_map * +___dl_map_object_from_mem (struct link_map *loader, const char *name, + const void *private, int type, int trace_mode, + int mode, Lmid_t nsid, struct filebuf *fbp) +{ + struct link_map *l; + int err; + /* Initialize to keep the compiler happy. */ + const char *errstring = NULL; + int errval = 0; + struct r_debug *r = _dl_debug_update (nsid); + bool make_consistent = false; + struct r_file_id id = {}; + + assert (nsid >= 0); + assert (nsid < GL(dl_nns)); + + /* Will be true if we found a DSO which is of the other ELF class. */ + bool found_other_class = false; + + err = do_open_verify (name, private, fbp, + loader ?: GL(dl_ns)[nsid]._ns_loaded, + &found_other_class, false, do_pread_memcpy); + if (err) + return NULL; + + /* In case the LOADER information has only been provided to get to + the appropriate RUNPATH/RPATH information we do not need it + anymore. */ + if (mode & __RTLD_CALLMAP) + loader = NULL; + + if (mode & RTLD_NOLOAD) + { + /* We are not supposed to load the object unless it is already + loaded. So return now. */ + return NULL; + } + + /* Print debugging message. */ + if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("dlmem [%lu]; generating link map\n", nsid); + + /* Enter the new object in the list of loaded objects. */ + l = _dl_new_object ((char *) name, name, type, loader, mode, nsid); + if (__glibc_unlikely (l == NULL)) + { + errstring = N_("cannot create shared object descriptor"); + goto lose_errno; + } + + void *stack_end = __libc_stack_end; + if (_ld_map_object_1 (l, private, fbp, mode, loader, &stack_end, &errval, + &errstring, do_mmapcpy)) + goto lose; + + _ld_map_object_2 (l, mode, id, NULL, nsid, r, &make_consistent); + return l; + +lose_errno: + errval = errno; +lose: + if (l != NULL && l->l_map_start != 0) + _dl_unmap_segments (l); + if (l != NULL && l->l_origin != (char *) -1l) + free ((char *) l->l_origin); + if (l != NULL && !l->l_libname->dont_free) + free (l->l_libname); + if (l != NULL && l->l_phdr_allocated) + free ((void *) l->l_phdr); + free (l); + + if (make_consistent && r != NULL) + { + r->r_state = RT_CONSISTENT; + _dl_debug_state (); + LIBC_PROBE (map_failed, 2, nsid, r); + } + + _dl_signal_error (errval, NULL, NULL, errstring); + return NULL; +} + +struct link_map * +__dl_map_object_from_mem (struct link_map *loader, const char *name, + const void *private, int type, int trace_mode, + int mode, Lmid_t nsid) +{ + struct link_map *ret; + struct filebuf fb = {}; + + ret = ___dl_map_object_from_mem (loader, name, private, type, trace_mode, + mode, nsid, &fb); + filebuf_done (&fb); + return ret; +} + struct add_path_state { bool counting; diff --git a/elf/dl-load.h b/elf/dl-load.h index ecf6910c68..4efb2f0494 100644 --- a/elf/dl-load.h +++ b/elf/dl-load.h @@ -100,6 +100,9 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header, - c->mapoff); } +static void * +do_mmap (void *addr, size_t length, int prot, int flags, + const void *arg, off_t offset); /* This is a subroutine of _dl_map_object_from_fd. It is responsible for filling in several fields in *L: l_map_start, l_map_end, l_addr, @@ -113,13 +116,14 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header, The file defines this function. The canonical implementation in elf/dl-map-segments.h might be replaced by a sysdeps version. */ -static const char *_dl_map_segments (struct link_map *l, int fd, +static const char *_dl_map_segments (struct link_map *l, const void *fd, const ElfW(Ehdr) *header, int type, const struct loadcmd loadcmds[], size_t nloadcmds, const size_t maplength, bool has_holes, - struct link_map *loader); + struct link_map *loader, + __typeof (do_mmap) *m_map); /* All the error message strings _dl_map_segments might return are listed here so that different implementations in different sysdeps diff --git a/elf/dl-main.h b/elf/dl-main.h index 92766d06b4..7047bd97bf 100644 --- a/elf/dl-main.h +++ b/elf/dl-main.h @@ -104,6 +104,26 @@ struct dl_main_state bool version_info; }; +struct const_fbuf +{ + ssize_t len; + const char *buf; +}; + +/* Open the shared object NAME and map in its segments. + LOADER's DT_RPATH is used in searching for NAME. + If the object is already opened, returns its existing map. */ +extern struct link_map * +__dl_map_object (struct link_map *loader, + const char *name, const void *private, + int type, int trace_mode, int mode, + Lmid_t nsid) attribute_hidden; +extern struct link_map * +__dl_map_object_from_mem (struct link_map *loader, + const char *name, const void *private, + int type, int trace_mode, int mode, + Lmid_t nsid) attribute_hidden; + /* Helper function to invoke _dl_init_paths with the right arguments from *STATE. */ static inline void diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h index 504cfc0a41..b07771e4f0 100644 --- a/elf/dl-map-segments.h +++ b/elf/dl-map-segments.h @@ -19,14 +19,23 @@ #include +static void * +do_mmap (void *addr, size_t length, int prot, int flags, + const void *arg, off_t offset) +{ + int fd = *(const int *) arg; + return __mmap (addr, length, prot, flags, fd, offset); +} + /* Map a segment and align it properly. */ static __always_inline ElfW(Addr) _dl_map_segment (const struct loadcmd *c, ElfW(Addr) mappref, - const size_t maplength, int fd) + const size_t maplength, const void *fd, + __typeof (do_mmap) *m_map) { if (__glibc_likely (c->mapalign <= GLRO(dl_pagesize))) - return (ElfW(Addr)) __mmap ((void *) mappref, maplength, c->prot, + return (ElfW(Addr)) m_map ((void *) mappref, maplength, c->prot, MAP_COPY|MAP_FILE, fd, c->mapoff); /* If the segment alignment > the page size, allocate enough space to @@ -42,7 +51,7 @@ _dl_map_segment (const struct loadcmd *c, ElfW(Addr) mappref, return map_start; ElfW(Addr) map_start_aligned = ALIGN_UP (map_start, c->mapalign); - map_start_aligned = (ElfW(Addr)) __mmap ((void *) map_start_aligned, + map_start_aligned = (ElfW(Addr)) m_map ((void *) map_start_aligned, maplength, c->prot, MAP_COPY|MAP_FILE|MAP_FIXED, fd, c->mapoff); @@ -72,11 +81,11 @@ _dl_map_segment (const struct loadcmd *c, ElfW(Addr) mappref, other use of those parts of the address space). */ static __always_inline const char * -_dl_map_segments (struct link_map *l, int fd, +_dl_map_segments (struct link_map *l, const void *fd, const ElfW(Ehdr) *header, int type, const struct loadcmd loadcmds[], size_t nloadcmds, const size_t maplength, bool has_holes, - struct link_map *loader) + struct link_map *loader, __typeof (do_mmap) *m_map) { const struct loadcmd *c = loadcmds; @@ -98,7 +107,7 @@ _dl_map_segments (struct link_map *l, int fd, - MAP_BASE_ADDR (l)); /* Remember which part of the address space this object uses. */ - l->l_map_start = _dl_map_segment (c, mappref, maplength, fd); + l->l_map_start = _dl_map_segment (c, mappref, maplength, fd, m_map); if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED)) return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; @@ -136,7 +145,7 @@ _dl_map_segments (struct link_map *l, int fd, { if (c->mapend > c->mapstart /* Map the segment contents from the file. */ - && (__mmap ((void *) (l->l_addr + c->mapstart), + && (m_map ((void *) (l->l_addr + c->mapstart), c->mapend - c->mapstart, c->prot, MAP_FIXED|MAP_COPY|MAP_FILE, fd, c->mapoff) diff --git a/elf/dl-open.c b/elf/dl-open.c index 91a2d8a538..acd04e1025 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -40,6 +40,7 @@ #include #include +#include /* We must be careful not to leave us in an inconsistent state. Thus we @@ -48,6 +49,7 @@ struct dl_open_args { const char *file; + const void *private; int mode; /* This is the caller of the dlopen() function. */ const void *caller_dlopen; @@ -55,6 +57,10 @@ struct dl_open_args /* Namespace ID. */ Lmid_t nsid; + struct link_map * + (*dl_map) (struct link_map *loader, const char *name, const void *private, + int type, int trace_mode, int mode, Lmid_t nsid); + /* Original value of _ns_global_scope_pending_adds. Set by dl_open_worker. Only valid if nsid is a real namespace (non-negative). */ @@ -531,7 +537,7 @@ dl_open_worker_begin (void *a) /* Load the named object. */ struct link_map *new; - args->map = new = _dl_map_object (call_map, file, lt_loaded, 0, + args->map = new = args->dl_map (call_map, file, args->private, lt_loaded, 0, mode | __RTLD_CALLMAP, args->nsid); /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is @@ -818,9 +824,11 @@ dl_open_worker (void *a) new->l_name, new->l_ns, new->l_direct_opencount); } -void * -_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid, - int argc, char *argv[], char *env[]) +static void * +do_dl_open (const char *file, const void *private, int mode, + const void *caller_dlopen, Lmid_t nsid, + int argc, char *argv[], char *env[], + __typeof (__dl_map_object) *dl_map) { if ((mode & RTLD_BINDING_MASK) == 0) /* One of the flags must be set. */ @@ -870,10 +878,12 @@ no more namespaces available for dlmopen()")); struct dl_open_args args; args.file = file; + args.private = private; args.mode = mode; args.caller_dlopen = caller_dlopen; args.map = NULL; args.nsid = nsid; + args.dl_map = dl_map; /* args.libc_already_loaded is always assigned by dl_open_worker (before any explicit/non-local returns). */ args.argc = argc; @@ -935,6 +945,25 @@ no more namespaces available for dlmopen()")); return args.map; } +void * +_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid, + int argc, char *argv[], char *env[]) +{ + return do_dl_open (file, NULL, mode, caller_dlopen, nsid, argc, argv, env, + __dl_map_object); +} + +void * +_dl_mem (const char *buffer, size_t size, int mode, + const void *caller_dlopen, Lmid_t nsid, + int argc, char *argv[], char *env[]) +{ + struct const_fbuf fb = { .buf = buffer, .len = size }; + + return do_dl_open ("", &fb, mode, caller_dlopen, nsid, argc, argv, env, + __dl_map_object_from_mem); +} + void _dl_show_scope (struct link_map *l, int from) diff --git a/elf/rtld.c b/elf/rtld.c index f82fbeb132..1877ec9f16 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -370,6 +370,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro = ._dl_mcount = _dl_mcount, ._dl_lookup_symbol_x = _dl_lookup_symbol_x, ._dl_open = _dl_open, + ._dl_mem = _dl_mem, ._dl_close = _dl_close, ._dl_catch_error = _dl_catch_error, ._dl_error_free = _dl_error_free, diff --git a/include/dlfcn.h b/include/dlfcn.h index ae25f05303..82abf6b7ef 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -100,6 +100,7 @@ struct dlfcn_hook { /* Public interfaces. */ void *(*dlopen) (const char *file, int mode, void *dl_caller); + void *(*dlmem) (const char *buffer, size_t size, int mode, void *dl_caller); int (*dlclose) (void *handle); void *(*dlsym) (void *handle, const char *name, void *dl_caller); void *(*dlvsym) (void *handle, const char *name, const char *version, @@ -123,6 +124,7 @@ struct dlfcn_hook the __libc_dl* functions defined in elf/dl-libc.c instead. */ extern void *__dlopen (const char *file, int mode, void *caller); +extern void *__dlmem (const char *file, size_t size, int mode, void *caller); extern void *__dlmopen (Lmid_t nsid, const char *file, int mode, void *dl_caller); extern int __dlclose (void *handle); diff --git a/manual/dynlink.texi b/manual/dynlink.texi index 6a4a50d3f0..21bc6c067c 100644 --- a/manual/dynlink.texi +++ b/manual/dynlink.texi @@ -209,6 +209,7 @@ This function is a GNU extension. @c dladdr1 @c dlclose @c dlerror +@c dlmem @c dlmopen @c dlopen @c dlsym diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index c99dad77cc..ff320bf33b 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -669,6 +669,9 @@ struct rtld_global_ro struct link_map *); void *(*_dl_open) (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid, int argc, char *argv[], char *env[]); + void *(*_dl_mem) (const char *buffer, size_t size, int mode, + const void *caller_dlopen, + Lmid_t nsid, int argc, char *argv[], char *env[]); void (*_dl_close) (void *map); /* libdl in a secondary namespace (after dlopen) must use _dl_catch_error from the main namespace, so it has to be @@ -1248,6 +1251,10 @@ extern char *_dl_dst_substitute (struct link_map *l, const char *name, extern void *_dl_open (const char *name, int mode, const void *caller, Lmid_t nsid, int argc, char *argv[], char *env[]) attribute_hidden; +extern void *_dl_mem (const char *buffer, size_t size, int mode, + const void *caller, + Lmid_t nsid, int argc, char *argv[], char *env[]) + attribute_hidden; /* Free or queue for freeing scope OLD. If other threads might be in the middle of _dl_fixup, _dl_profile_fixup or dl*sym using the diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist index 367c8d0a03..51422245b6 100644 --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist @@ -2351,6 +2351,7 @@ GLIBC_2.34 dladdr1 F GLIBC_2.34 dlclose F GLIBC_2.34 dlerror F GLIBC_2.34 dlinfo F +GLIBC_2.34 dlmem F GLIBC_2.34 dlmopen F GLIBC_2.34 dlopen F GLIBC_2.34 dlsym F