From patchwork Wed Feb 15 11:21:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: stsp X-Patchwork-Id: 65039 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 39AA03858022 for ; Wed, 15 Feb 2023 11:23:42 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 39AA03858022 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1676460222; bh=C+nDotYE4WpQ7hGhms4TfaPahaQgwggizU7CU46d9s8=; 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=XapyGjl2tALWw+TjKLZ59yqWKPeRx3Up/rV0Kp1Q6E1NxA+ov5adD/bJjbSn4QBNc Mw2pJyVwzT2KwCVk+fhLt9tQdDSNUMZfbQe3TcD/2TM+yxPW9WcVyBj/DnTQeWXZCA eCnaFd+Up7nj0o/3lpgLnOHOtjbvUmEWQC2xPnDI= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from forward106o.mail.yandex.net (forward106o.mail.yandex.net [37.140.190.187]) by sourceware.org (Postfix) with ESMTPS id 67DBE385483E for ; Wed, 15 Feb 2023 11:23:18 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 67DBE385483E Received: from iva1-07947c637cce.qloud-c.yandex.net (iva1-07947c637cce.qloud-c.yandex.net [IPv6:2a02:6b8:c0c:929a:0:640:794:7c63]) by forward106o.mail.yandex.net (Yandex) with ESMTP id 24539568DCA4 for ; Wed, 15 Feb 2023 14:21:23 +0300 (MSK) Received: by iva1-07947c637cce.qloud-c.yandex.net (smtp/Yandex) with ESMTPSA id KLinQ40YPW21-vCpfpxNU; Wed, 15 Feb 2023 14:21:22 +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: Wed, 15 Feb 2023 16:21:09 +0500 Message-Id: <20230215112110.2426646-2-stsp2@yandex.ru> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230215112110.2426646-1-stsp2@yandex.ru> References: <20230215112110.2426646-1-stsp2@yandex.ru> MIME-Version: 1.0 X-Spam-Status: No, score=-10.3 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, RCVD_IN_MSPIKE_H2, 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 Wed Feb 15 11:21:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: stsp X-Patchwork-Id: 65040 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 DF4263858C3A for ; Wed, 15 Feb 2023 11:28:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DF4263858C3A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1676460483; bh=ENHMSvDiERdTd2O2Ud9ghXRmnGlehQwhScluSkRoL9w=; 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=FBYc9Njk429vUvvT52Xtf7A5IzkpBt4fMpwWCKlx/oxyehAeHLQc0ultvxnDb7Szk CKipflQqCtuh3jyf0tO9pPyjaF4p8jN5oVBs1agkOLXu4N+Um1W0ojsQWyTx0f1ypu JM2RzlIcO1jG/smehL7AbSCJO+jgPJKcpY//jm2s= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from forward103j.mail.yandex.net (forward103j.mail.yandex.net [IPv6:2a02:6b8:0:801:2::106]) by sourceware.org (Postfix) with ESMTPS id 5350B3858D33 for ; Wed, 15 Feb 2023 11:27:28 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5350B3858D33 Received: from iva1-07947c637cce.qloud-c.yandex.net (iva1-07947c637cce.qloud-c.yandex.net [IPv6:2a02:6b8:c0c:929a:0:640:794:7c63]) by forward103j.mail.yandex.net (Yandex) with ESMTP id D35EE101F46 for ; Wed, 15 Feb 2023 14:21:24 +0300 (MSK) Received: by iva1-07947c637cce.qloud-c.yandex.net (smtp/Yandex) with ESMTPSA id KLinQ40YPW21-2s4d9C1a; Wed, 15 Feb 2023 14:21:23 +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: Wed, 15 Feb 2023 16:21:10 +0500 Message-Id: <20230215112110.2426646-3-stsp2@yandex.ru> X-Mailer: git-send-email 2.37.2 In-Reply-To: <20230215112110.2426646-1-stsp2@yandex.ru> References: <20230215112110.2426646-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, SPF_HELO_NONE, SPF_PASS, TXREP, T_FILL_THIS_FORM_SHORT 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 unsigned 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. This patch also adds the _dl_audit_premap_dlmem LD_AUDIT extension. It passes map length to la_premap_dlmem() and gets back an fd. If returned fd==-1 then the dlmem() space is anonymously mapped, which is a default behavior w/o audit. But it is possible to create a shm area of the needed size with shm_open()/ftruncate() and return that fd. Then you will be able to access the solib image via that fd, so that you can mmap() it to another process or another address of the same process. The test-case for that functionality is called tst-audit-dlmem. The test-suite was run on x86_64/64 and showed no regressions. Signed-off-by: Stas Sergeev --- dlfcn/Makefile | 5 +- dlfcn/Versions | 3 + dlfcn/dlmem.c | 105 ++ dlfcn/tst-dlmem.c | 100 ++ elf/Makefile | 7 + elf/dl-audit.c | 22 + elf/dl-load.c | 1023 ++++++++++------- elf/dl-load.h | 8 +- elf/dl-main.h | 21 + elf/dl-map-segments.h | 31 +- elf/dl-open.c | 37 +- elf/link.h | 1 + elf/rtld.c | 4 +- elf/tst-audit-dlmem.c | 229 ++++ elf/tst-audit18mod.c | 2 + elf/tst-auditmod-dlmem.c | 102 ++ include/dlfcn.h | 4 + manual/dynlink.texi | 1 + sysdeps/generic/ldsodefs.h | 11 + sysdeps/mach/hurd/i386/libc.abilist | 1 + sysdeps/unix/sysv/linux/aarch64/libc.abilist | 1 + sysdeps/unix/sysv/linux/alpha/libc.abilist | 1 + sysdeps/unix/sysv/linux/arc/libc.abilist | 1 + sysdeps/unix/sysv/linux/arm/be/libc.abilist | 1 + sysdeps/unix/sysv/linux/arm/le/libc.abilist | 1 + sysdeps/unix/sysv/linux/csky/libc.abilist | 1 + sysdeps/unix/sysv/linux/hppa/libc.abilist | 1 + sysdeps/unix/sysv/linux/i386/libc.abilist | 1 + sysdeps/unix/sysv/linux/ia64/libc.abilist | 1 + .../sysv/linux/loongarch/lp64/libc.abilist | 1 + .../sysv/linux/m68k/coldfire/libc.abilist | 1 + .../unix/sysv/linux/m68k/m680x0/libc.abilist | 1 + .../sysv/linux/microblaze/be/libc.abilist | 1 + .../sysv/linux/microblaze/le/libc.abilist | 1 + .../sysv/linux/mips/mips32/fpu/libc.abilist | 1 + .../sysv/linux/mips/mips32/nofpu/libc.abilist | 1 + .../sysv/linux/mips/mips64/n32/libc.abilist | 1 + .../sysv/linux/mips/mips64/n64/libc.abilist | 1 + sysdeps/unix/sysv/linux/nios2/libc.abilist | 1 + sysdeps/unix/sysv/linux/or1k/libc.abilist | 1 + .../linux/powerpc/powerpc32/fpu/libc.abilist | 1 + .../powerpc/powerpc32/nofpu/libc.abilist | 1 + .../linux/powerpc/powerpc64/be/libc.abilist | 1 + .../linux/powerpc/powerpc64/le/libc.abilist | 1 + .../unix/sysv/linux/riscv/rv32/libc.abilist | 1 + .../unix/sysv/linux/riscv/rv64/libc.abilist | 1 + .../unix/sysv/linux/s390/s390-32/libc.abilist | 1 + .../unix/sysv/linux/s390/s390-64/libc.abilist | 1 + sysdeps/unix/sysv/linux/sh/be/libc.abilist | 1 + sysdeps/unix/sysv/linux/sh/le/libc.abilist | 1 + .../sysv/linux/sparc/sparc32/libc.abilist | 1 + .../sysv/linux/sparc/sparc64/libc.abilist | 1 + .../unix/sysv/linux/x86_64/64/libc.abilist | 1 + .../unix/sysv/linux/x86_64/x32/libc.abilist | 1 + 54 files changed, 1337 insertions(+), 414 deletions(-) create mode 100644 dlfcn/dlmem.c create mode 100644 dlfcn/tst-dlmem.c create mode 100644 elf/tst-audit-dlmem.c create mode 100644 elf/tst-auditmod-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..b427c9c3a3 100644 --- a/dlfcn/Versions +++ b/dlfcn/Versions @@ -28,6 +28,9 @@ libc { dlsym; dlvsym; } + GLIBC_2.38 { + dlmem; + } GLIBC_PRIVATE { __libc_dlerror_result; _dlerror_run; diff --git a/dlfcn/dlmem.c b/dlfcn/dlmem.c new file mode 100644 index 0000000000..b8efd161ad --- /dev/null +++ b/dlfcn/dlmem.c @@ -0,0 +1,105 @@ +/* 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 unsigned 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 unsigned 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 unsigned 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_38); + +#else /* !SHARED */ +/* Also used with _dlfcn_hook. */ +void * +__dlmem (const unsigned char *buffer, size_t size, int mode, void *dl_caller) +{ + return dlmem_implementation (buffer, size, mode, dl_caller); +} + +void * +___dlmem (const unsigned 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..da9865cc43 --- /dev/null +++ b/dlfcn/tst-dlmem.c @@ -0,0 +1,100 @@ +/* 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 +#include + + +#define TEST_FUNCTION do_test +extern int do_test (void); +void *dlmem (const unsigned 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; + int num; + off_t len; + struct link_map *lm; + + 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); + 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, len, RTLD_NOW); + if (handle == NULL) + error (EXIT_FAILURE, 0, "cannot load: glreflib1.so"); + munmap (addr, len); + close (fd); + + 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"); + ret = dlinfo (handle, RTLD_DI_LINKMAP, &lm); + if (ret != 0) + error (EXIT_FAILURE, 0, "dlinfo 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); + printf ("lm->l_addr = %lx\n", lm->l_addr); + + num = sym (); + if (num != 42) + error (EXIT_FAILURE, 0, "bad return from ref1"); + + 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 diff --git a/elf/Makefile b/elf/Makefile index 2fc6391183..5b3e74f2eb 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -369,6 +369,7 @@ tests += \ tst-align \ tst-align2 \ tst-align3 \ + tst-audit-dlmem \ tst-audit-tlsdesc \ tst-audit-tlsdesc-dlopen \ tst-audit1 \ @@ -796,6 +797,7 @@ modules-names += \ tst-auditmanymod7 \ tst-auditmanymod8 \ tst-auditmanymod9 \ + tst-auditmod-dlmem \ tst-auditmod-tlsdesc \ tst-auditmod1 \ tst-auditmod9a \ @@ -2259,6 +2261,11 @@ $(objpfx)tst-audit18.out: $(objpfx)tst-auditmod18.so \ $(objpfx)tst-audit18mod.so tst-audit18-ARGS = -- $(host-test-program-cmd) +$(objpfx)tst-audit-dlmem.out: $(objpfx)tst-auditmod-dlmem.so \ + $(objpfx)tst-audit18mod.so +tst-audit-dlmem-ARGS = -- $(host-test-program-cmd) +CPPFLAGS-tst-audit-dlmem.c += -DBUILDDIR=\"$(objpfx)\" + $(objpfx)tst-audit19a.out: $(objpfx)tst-auditmod19a.so tst-audit19a-ENV = LD_AUDIT=$(objpfx)tst-auditmod19a.so diff --git a/elf/dl-audit.c b/elf/dl-audit.c index 00e794aa26..76bf365168 100644 --- a/elf/dl-audit.c +++ b/elf/dl-audit.c @@ -72,6 +72,28 @@ _dl_audit_objsearch (const char *name, struct link_map *l, unsigned int code) return name; } +int +_dl_audit_premap_dlmem (struct link_map *l, size_t maplength) +{ + if (__glibc_likely (GLRO(dl_naudit) == 0)) + return -1; + + struct audit_ifaces *afct = GLRO(dl_audit); + for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) + { + if (afct->premap_dlmem != NULL) + { + struct auditstate *state = link_map_audit_state (l, cnt); + int fd = afct->premap_dlmem (maplength, &state->cookie); + if (fd != -1) + return fd; + } + + afct = afct->next; + } + return -1; +} + void _dl_audit_objopen (struct link_map *l, Lmid_t nsid) { diff --git a/elf/dl-load.c b/elf/dl-load.c index fcb39a78d4..7baf70c0de 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) { @@ -929,147 +954,30 @@ _dl_process_pt_gnu_property (struct link_map *l, int fd, const ElfW(Phdr) *ph) } } +typedef void _dl_premap_type (struct link_map *l, void *private, + size_t maplength); -/* 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, 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, + _dl_premap_type *premap) { - 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 +985,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. */ @@ -1261,12 +1159,15 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, /* Length of the sections to be loaded. */ maplength = loadcmds[nloadcmds - 1].allocend - loadcmds[0].mapstart; + if (premap) + premap(l, fd, maplength); + /* Now process the load commands and map segments into memory. This is responsible for filling in: 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 +1280,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 +1311,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 +1419,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,266 +1429,430 @@ 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 - - return l; } - -/* Print search path. */ -static void -print_search_path (struct r_search_path_elem **list, - const char *what, const char *name) -{ - char buf[max_dirnamelen + max_capstrlen]; - int first = 1; - - _dl_debug_printf (" search path="); - while (*list != NULL && (*list)->what == what) /* Yes, ==. */ - { - char *endp = __mempcpy (buf, (*list)->dirname, (*list)->dirnamelen); - size_t cnt; +/* Map in the shared object NAME, actually located in REALNAME, and already + opened on FD. */ - for (cnt = 0; cnt < ncapstr; ++cnt) - if ((*list)->status[cnt] != nonexisting) - { -#ifdef SHARED - char *cp = __mempcpy (endp, capstr[cnt].str, capstr[cnt].len); - if (cp == buf || (cp == buf + 1 && buf[0] == '/')) - cp[0] = '\0'; - else - cp[-1] = '\0'; -#else - *endp = '\0'; +#ifndef EXTERNAL_MAP_FROM_FD +static #endif - - _dl_debug_printf_c (first ? "%s" : ":%s", buf); - first = 0; - } - - ++list; - } - - if (name != NULL) - _dl_debug_printf_c ("\t\t(%s from file %s)\n", what, - DSO_FILENAME (name)); - else - _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 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) +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) { - /* This is the expected ELF header. */ -#define ELF32_CLASS ELFCLASS32 -#define ELF64_CLASS ELFCLASS64 -#ifndef VALID_ELF_HEADER -# define VALID_ELF_HEADER(hdr,exp,size) (memcmp (hdr, exp, size) == 0) -# define VALID_ELF_OSABI(osabi) (osabi == ELFOSABI_SYSV) -# define VALID_ELF_ABIVERSION(osabi,ver) (ver == 0) -#elif defined MORE_ELF_HEADER_DATA - MORE_ELF_HEADER_DATA; -#endif - static const unsigned char expected[EI_NIDENT] = - { - [EI_MAG0] = ELFMAG0, - [EI_MAG1] = ELFMAG1, - [EI_MAG2] = ELFMAG2, - [EI_MAG3] = ELFMAG3, - [EI_CLASS] = ELFW(CLASS), - [EI_DATA] = byteorder, - [EI_VERSION] = EV_CURRENT, - [EI_OSABI] = ELFOSABI_SYSV, - [EI_ABIVERSION] = 0 - }; - /* Initialize it to make the compiler happy. */ + 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; -#ifdef SHARED - /* Give the auditing libraries a chance. */ - if (__glibc_unlikely (GLRO(dl_naudit) > 0)) + /* 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) { - const char *original_name = name; - name = _dl_audit_objsearch (name, loader, whatcode); - if (name == NULL) - return -1; - - if (fd != -1 && name != original_name && strcmp (name, original_name)) + assert (nsid == LM_ID_BASE); + memset (&id, 0, sizeof (id)); + } + else + { + if (__glibc_unlikely (!_dl_get_file_id (fd, &id))) { - /* An audit library changed what we're supposed to open, - so FD no longer matches it. */ - __close_nocancel (fd); - fd = -1; + 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 (fd == -1) - /* Open the file. We always open files read-only. */ - fd = __open64_nocancel (name, O_RDONLY | O_CLOEXEC); + 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; + } - if (fd != -1) + /* 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)) { - 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)))); +#ifdef SHARED + fail_new: +#endif + errstring = N_("cannot create shared object descriptor"); + goto lose_errno; + } - /* This is where the ELF header is loaded. */ - ehdr = (ElfW(Ehdr) *) fbp->buf; + if (_ld_map_object_1 (l, &fd, fbp, mode, loader, stack_endp, &errval, + &errstring, do_mmap, NULL)) + goto lose; - /* Now run the tests. */ - if (__glibc_unlikely (fbp->len < (ssize_t) sizeof (ElfW(Ehdr)))) - { - errval = errno; - errstring = (errval == 0 + /* 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; +} + +/* Print search path. */ +static void +print_search_path (struct r_search_path_elem **list, + const char *what, const char *name) +{ + char buf[max_dirnamelen + max_capstrlen]; + int first = 1; + + _dl_debug_printf (" search path="); + + while (*list != NULL && (*list)->what == what) /* Yes, ==. */ + { + char *endp = __mempcpy (buf, (*list)->dirname, (*list)->dirnamelen); + size_t cnt; + + for (cnt = 0; cnt < ncapstr; ++cnt) + if ((*list)->status[cnt] != nonexisting) + { +#ifdef SHARED + char *cp = __mempcpy (endp, capstr[cnt].str, capstr[cnt].len); + if (cp == buf || (cp == buf + 1 && buf[0] == '/')) + cp[0] = '\0'; + else + cp[-1] = '\0'; +#else + *endp = '\0'; +#endif + + _dl_debug_printf_c (first ? "%s" : ":%s", buf); + first = 0; + } + + ++list; + } + + if (name != NULL) + _dl_debug_printf_c ("\t\t(%s from file %s)\n", what, + DSO_FILENAME (name)); + else + _dl_debug_printf_c ("\t\t(%s)\n", what); +} + + +static ssize_t +do_pread (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 (void *arg, void *buf, size_t count, off_t offset) +{ + 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 +do_open_verify (const char *name, 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 +#define ELF64_CLASS ELFCLASS64 +#ifndef VALID_ELF_HEADER +# define VALID_ELF_HEADER(hdr,exp,size) (memcmp (hdr, exp, size) == 0) +# define VALID_ELF_OSABI(osabi) (osabi == ELFOSABI_SYSV) +# define VALID_ELF_ABIVERSION(osabi,ver) (ver == 0) +#elif defined MORE_ELF_HEADER_DATA + MORE_ELF_HEADER_DATA; +#endif + static const unsigned char expected[EI_NIDENT] = + { + [EI_MAG0] = ELFMAG0, + [EI_MAG1] = ELFMAG1, + [EI_MAG2] = ELFMAG2, + [EI_MAG3] = ELFMAG3, + [EI_CLASS] = ELFW(CLASS), + [EI_DATA] = byteorder, + [EI_VERSION] = EV_CURRENT, + [EI_OSABI] = ELFOSABI_SYSV, + [EI_ABIVERSION] = 0 + }; + /* 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); - } - __close_nocancel (fd); - _dl_signal_error (errval, name, NULL, errstring); - } + 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 != + /* 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))) + ((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))) + ((ELFMAG0 << (EI_MAG3 * 8)) + | (ELFMAG1 << (EI_MAG2 * 8)) + | (ELFMAG2 << (EI_MAG1 * 8)) + | (ELFMAG3 << (EI_MAG0 * 8))) #endif - ) - errstring = N_("invalid ELF header"); + ) + 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"); + 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; - } + 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 + 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; - } + { + 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; - } - } + 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)) + { + const char *original_name = name; + name = _dl_audit_objsearch (name, loader, whatcode); + if (name == NULL) + return -1; - if (__glibc_unlikely (elf_machine_reject_phdr_p - (phdr, ehdr->e_phnum, fbp->buf, fbp->len, - loader, fd))) + if (fd != -1 && name != original_name && strcmp (name, original_name)) { + /* An audit library changed what we're supposed to open, + so FD no longer matches it. */ __close_nocancel (fd); - __set_errno (ENOENT); - return -1; + fd = -1; } + } +#endif + if (fd == -1) + /* Open the file. We always open files read-only. */ + fd = __open64_nocancel (name, O_RDONLY | O_CLOEXEC); + + if (fd != -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 +2019,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 +2118,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 +2134,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 +2148,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 +2156,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 +2165,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 +2174,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 +2228,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 +2246,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 +2262,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 +2323,166 @@ _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, + 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, + void *arg, off_t offset, void *mapstart) +{ + const struct const_fbuf *fb = arg; + void *ret; + + assert (flags & MAP_FIXED); + assert (addr >= mapstart); + if (fb->fd == -1) + ret = __mmap (addr, length, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + else + ret = __mmap (addr, length, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, fb->fd, addr - mapstart); + if (ret == MAP_FAILED) + return ret; + if (offset < fb->len) + { + size_t to_copy = length; + if (offset + to_copy > fb->len) + to_copy = fb->len - offset; + memcpy (ret, fb->buf + offset, to_copy); + } + if (__mprotect (ret, length, prot) == -1) + return MAP_FAILED; + return ret; +} + +static void +do_dlmem_premap (struct link_map *l, void *arg, size_t maplength) +{ +#ifdef SHARED + struct const_fbuf *fb = arg; + + fb->fd = _dl_audit_premap_dlmem (l, maplength); +#endif +} + +static struct link_map * +___dl_map_object_from_mem (struct link_map *loader, const char *name, + 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, do_dlmem_premap)) + 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, + 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..4c063f5463 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, + void *arg, off_t offset, void *mapstart); /* 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, 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..d6be25aa48 100644 --- a/elf/dl-main.h +++ b/elf/dl-main.h @@ -104,6 +104,27 @@ struct dl_main_state bool version_info; }; +struct const_fbuf +{ + ssize_t len; + const unsigned char *buf; + int fd; +}; + +/* 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, 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, 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..89c0833544 100644 --- a/elf/dl-map-segments.h +++ b/elf/dl-map-segments.h @@ -19,15 +19,23 @@ #include +static void * +do_mmap (void *addr, size_t length, int prot, int flags, + void *arg, off_t offset, void *mapstart) +{ + 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) { if (__glibc_likely (c->mapalign <= GLRO(dl_pagesize))) return (ElfW(Addr)) __mmap ((void *) mappref, maplength, c->prot, - MAP_COPY|MAP_FILE, fd, c->mapoff); + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); /* If the segment alignment > the page size, allocate enough space to ensure that the segment can be properly aligned. */ @@ -44,8 +52,8 @@ _dl_map_segment (const struct loadcmd *c, ElfW(Addr) mappref, ElfW(Addr) map_start_aligned = ALIGN_UP (map_start, c->mapalign); map_start_aligned = (ElfW(Addr)) __mmap ((void *) map_start_aligned, maplength, c->prot, - MAP_COPY|MAP_FILE|MAP_FIXED, - fd, c->mapoff); + MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, + -1, 0); if (__glibc_unlikely ((void *) map_start_aligned == MAP_FAILED)) __munmap ((void *) map_start, maplen); else @@ -72,11 +80,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, 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,13 +106,18 @@ _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); if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED)) return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; l->l_map_end = l->l_map_start + maplength; l->l_addr = l->l_map_start - c->mapstart; + if (m_map ((void *) l->l_map_start, maplength, c->prot, + MAP_FIXED|MAP_COPY|MAP_FILE, fd, c->mapoff, + (void *) l->l_map_start) == MAP_FAILED) + return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; + if (has_holes) { /* Change protection on the excess portion to disallow all access; @@ -136,10 +149,10 @@ _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) + fd, c->mapoff, (void *) l->l_map_start) == MAP_FAILED)) return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT; diff --git a/elf/dl-open.c b/elf/dl-open.c index 91a2d8a538..9ec35ac1c3 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; + 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, 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, 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 unsigned 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, .fd = -1 }; + + 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/link.h b/elf/link.h index 3b5954d981..b59038c680 100644 --- a/elf/link.h +++ b/elf/link.h @@ -193,6 +193,7 @@ extern unsigned int la_version (unsigned int __version); extern void la_activity (uintptr_t *__cookie, unsigned int __flag); extern char *la_objsearch (const char *__name, uintptr_t *__cookie, unsigned int __flag); +extern int la_premap_dlmem (size_t maplength, uintptr_t *__cookie); extern unsigned int la_objopen (struct link_map *__map, Lmid_t __lmid, uintptr_t *__cookie); extern void la_preinit (uintptr_t *__cookie); diff --git a/elf/rtld.c b/elf/rtld.c index f82fbeb132..ce35d0a28b 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, @@ -989,7 +990,7 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d); return; } - enum { naudit_ifaces = 8 }; + enum { naudit_ifaces = 9 }; union { struct audit_ifaces ifaces; @@ -1003,6 +1004,7 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d); static const char audit_iface_names[] = "la_activity\0" "la_objsearch\0" + "la_premap_dlmem\0" "la_objopen\0" "la_preinit\0" LA_SYMBIND "\0" diff --git a/elf/tst-audit-dlmem.c b/elf/tst-audit-dlmem.c new file mode 100644 index 0000000000..87c6d3741e --- /dev/null +++ b/elf/tst-audit-dlmem.c @@ -0,0 +1,229 @@ +/* Check DT_AUDIT with dlmem. + Copyright (C) 2021-2023 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 +#include +#include +#include +#include +#include +#include + +static int restart; +#define CMDLINE_OPTIONS \ + { "restart", no_argument, &restart, 1 }, + +/* test value */ +#define TEST_BAR_VAL 123 + +void *dlmem (const unsigned char *buffer, size_t size, int flags); + +static void * +_dlmem_wrapper (const char *file, unsigned flags) +{ + off_t len; + int fd; + void *handle; + void *addr; + + fd = open (file, O_RDONLY); + if (fd == -1) + { + printf ("cannot open %s, %s\n", file, strerror(errno)); + exit (EXIT_FAILURE); + } + len = lseek (fd, 0, SEEK_END); + lseek (fd, 0, SEEK_SET); + addr = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + if (addr == MAP_FAILED) + { + printf ("cannot mmap %s, %s\n", file, strerror(errno)); + exit (EXIT_FAILURE); + } + handle = dlmem (addr, len, RTLD_NOW); + if (handle == NULL) + { + printf ("cannot dlmem, %s\n", strerror(errno)); + exit (EXIT_FAILURE); + } + munmap (addr, len); + close (fd); + return handle; +} + +#define dlmem_wrapper(n, f) _dlmem_wrapper (BUILDDIR n, f) + +static int +handle_restart (void) +{ + { + void *h = dlmem_wrapper ("../" LIBC_SO, RTLD_NOW); + + pid_t (*s) (void) = xdlsym (h, "getpid"); + TEST_COMPARE (s (), getpid ()); + + xdlclose (h); + } + + { + Dl_info info; + void *h = dlmem_wrapper ("tst-audit18mod.so", RTLD_NOW); + + int (*foo) (void) = xdlsym (h, "foo"); + TEST_COMPARE (foo (), 10); + + int *bar = xdlsym (h, "bar"); + TEST_COMPARE (*bar, 35); + + if (!dladdr (bar, &info)) + { + printf ("dladdr failed\n"); + exit (EXIT_FAILURE); + } + fprintf (stderr, "offset bar %lx\n", info.dli_saddr - info.dli_fbase); + /* write another value for parent to read */ + *bar = TEST_BAR_VAL; + } + + return 0; +} + +static int +do_test (int argc, char *argv[]) +{ + /* We must have either: + - One our fource parameters left if called initially: + + path to ld.so optional + + "--library-path" optional + + the library path optional + + the application name */ + + if (restart) + return handle_restart (); + + char *spargv[9]; + int i = 0; + for (; i < argc - 1; i++) + spargv[i] = argv[i + 1]; + spargv[i++] = (char *) "--direct"; + spargv[i++] = (char *) "--restart"; + spargv[i] = NULL; + + setenv ("LD_AUDIT", "tst-auditmod-dlmem.so", 0); + struct support_capture_subprocess result + = support_capture_subprogram (spargv[0], spargv); + support_capture_subprocess_check (&result, "tst-auditdlmem", 0, + sc_allow_stderr); + + struct + { + const char *name; + bool found; + } audit_iface[] = + { + { "la_version", false }, + { "la_objsearch", false }, + { "la_activity", false }, + { "la_premap_dlmem", false }, + { "la_objopen", false }, + { "la_objclose", false }, + { "la_preinit", false }, +#if __WORDSIZE == 32 + { "la_symbind32", false }, +#elif __WORDSIZE == 64 + { "la_symbind64", false }, +#endif + }; + + size_t maplength = 0; + char shm_name[256] = {}; + const char *ml = "maplength"; + int fd; + void *addr; + const char *off_str = "offset"; + char sym_name[256] = {}; + unsigned long sym_offset = 0; + int *sym; + + /* Some hooks are called more than once but the test only check if any + is called at least once. */ + FILE *out = fmemopen (result.err.buffer, result.err.length, "r"); + TEST_VERIFY (out != NULL); + char *buffer = NULL; + size_t buffer_length = 0; + while (xgetline (&buffer, &buffer_length, out)) + { + for (int i = 0; i < array_length (audit_iface); i++) + if (strncmp (buffer, audit_iface[i].name, + strlen (audit_iface[i].name)) == 0) + audit_iface[i].found = true; + if (strncmp (buffer, ml, strlen (ml)) == 0) + sscanf(buffer, "%*s %zi %255s", &maplength, shm_name); + if (strncmp (buffer, off_str, strlen (off_str)) == 0) + sscanf(buffer, "%*s %255s %lx", sym_name, &sym_offset); + } + free (buffer); + xfclose (out); + + for (int i = 0; i < array_length (audit_iface); i++) + TEST_COMPARE (audit_iface[i].found, true); + + support_capture_subprocess_free (&result); + + if (!maplength || !shm_name[0]) + { + printf ("premap_dlmem didn't work\n"); + exit (EXIT_FAILURE); + } + if (!sym_offset) + { + printf ("sym_offset not found\n"); + exit (EXIT_FAILURE); + } + printf ("maplength=%zi shm_name=%s\n", maplength, shm_name); + fd = shm_open (shm_name, O_RDWR, 0); + if (fd == -1) + { + printf ("cannot open shm\n"); + exit (EXIT_FAILURE); + } + shm_unlink (shm_name); + addr = mmap (NULL, maplength, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) + { + printf ("cannot mmap shm\n"); + exit (EXIT_FAILURE); + } + sym = addr + sym_offset; + TEST_COMPARE (*sym, TEST_BAR_VAL); + + munmap (addr, maplength); + return 0; +} + +#define TEST_FUNCTION_ARGV do_test +#include diff --git a/elf/tst-audit18mod.c b/elf/tst-audit18mod.c index f010a475dc..2bde8788eb 100644 --- a/elf/tst-audit18mod.c +++ b/elf/tst-audit18mod.c @@ -21,3 +21,5 @@ foo (void) { return 10; } + +int bar = 35; diff --git a/elf/tst-auditmod-dlmem.c b/elf/tst-auditmod-dlmem.c new file mode 100644 index 0000000000..bd413da436 --- /dev/null +++ b/elf/tst-auditmod-dlmem.c @@ -0,0 +1,102 @@ +/* Check DT_AUDIT with dlmem. + Copyright (C) 2021-2023 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 + +unsigned int +la_version (unsigned int version) +{ + fprintf (stderr, "%s\n", __func__); + return LAV_CURRENT; +} + +char * +la_objsearch (const char *name, uintptr_t *cookie, unsigned int flag) +{ + fprintf (stderr, "%s\n", __func__); + return (char *) name; +} + +void +la_activity (uintptr_t *cookie, unsigned int flag) +{ + fprintf (stderr, "%s\n", __func__); +} + +int +la_premap_dlmem (size_t maplength, uintptr_t *cookie) +{ + int fd; + int err; + const char *shm_name = "/tst-dlmem"; + + fprintf (stderr, "%s\n", __func__); + fd = shm_open (shm_name, O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd == -1) + { + perror ("shm_open()"); + return -1; + } + err = ftruncate (fd, maplength); + if (err) + { + perror ("ftruncate()"); + return -1; + } + + fprintf (stderr, "maplength %zi %s\n", maplength, shm_name); + return fd; +} + +unsigned int +la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie) +{ + fprintf (stderr, "%s\n", __func__); + return LA_FLG_BINDTO | LA_FLG_BINDFROM; +} + +unsigned int +la_objclose (uintptr_t *cookie) +{ + fprintf (stderr, "%s\n", __func__); + return 0; +} + +void +la_preinit (uintptr_t *cookie) +{ + fprintf (stderr, "%s\n", __func__); +} + +uintptr_t +#if __ELF_NATIVE_CLASS == 32 +la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, const char *symname) +#else +la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook, + uintptr_t *defcook, unsigned int *flags, const char *symname) +#endif +{ + fprintf (stderr, "%s\n", __func__); + return sym->st_value; +} diff --git a/include/dlfcn.h b/include/dlfcn.h index ae25f05303..e09cb8cc72 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -100,6 +100,8 @@ struct dlfcn_hook { /* Public interfaces. */ void *(*dlopen) (const char *file, int mode, void *dl_caller); + void *(*dlmem) (const unsigned 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 +125,8 @@ 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 unsigned 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..810d98f468 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -239,6 +239,7 @@ struct audit_ifaces { void (*activity) (uintptr_t *, unsigned int); char *(*objsearch) (const char *, uintptr_t *, unsigned int); + int (*premap_dlmem) (size_t, uintptr_t *); unsigned int (*objopen) (struct link_map *, Lmid_t, uintptr_t *); void (*preinit) (uintptr_t *); union @@ -669,6 +670,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 unsigned 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 +1252,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 unsigned 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 @@ -1360,6 +1368,9 @@ void _dl_audit_activity_map (struct link_map *l, int action) void _dl_audit_activity_nsid (Lmid_t nsid, int action) attribute_hidden; +int _dl_audit_premap_dlmem (struct link_map *l, size_t maplength) + attribute_hidden; + /* Call the la_objopen from the audit modules for the link_map L on the namespace identification NSID. */ void _dl_audit_objopen (struct link_map *l, Lmid_t nsid) diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist index 4e3200ef55..db4ef39333 100644 --- a/sysdeps/mach/hurd/i386/libc.abilist +++ b/sysdeps/mach/hurd/i386/libc.abilist @@ -2294,6 +2294,7 @@ GLIBC_2.36 arc4random_buf F GLIBC_2.36 arc4random_uniform F GLIBC_2.36 c8rtomb F GLIBC_2.36 mbrtoc8 F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist index b66fadef40..9baf6cf92c 100644 --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist @@ -2633,3 +2633,4 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist index f918bb2d48..364935fa51 100644 --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist @@ -2730,6 +2730,7 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F GLIBC_2.4 _IO_fprintf F GLIBC_2.4 _IO_printf F GLIBC_2.4 _IO_sprintf F diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist index 093043a533..d820edf258 100644 --- a/sysdeps/unix/sysv/linux/arc/libc.abilist +++ b/sysdeps/unix/sysv/linux/arc/libc.abilist @@ -2394,3 +2394,4 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist index f28402fe03..01109f19c8 100644 --- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist @@ -514,6 +514,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 _Exit F GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist index e2f56880ed..5f9d669b6d 100644 --- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist @@ -511,6 +511,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 _Exit F GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist index 319d92356e..02b9425c89 100644 --- a/sysdeps/unix/sysv/linux/csky/libc.abilist +++ b/sysdeps/unix/sysv/linux/csky/libc.abilist @@ -2670,3 +2670,4 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist index 6450e17ebe..8e62d22eb2 100644 --- a/sysdeps/unix/sysv/linux/hppa/libc.abilist +++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist @@ -2619,6 +2619,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist index 0a24ec9afd..80d09fe3ca 100644 --- a/sysdeps/unix/sysv/linux/i386/libc.abilist +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist @@ -2803,6 +2803,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist index 02c65b6482..9558053778 100644 --- a/sysdeps/unix/sysv/linux/ia64/libc.abilist +++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist @@ -2568,6 +2568,7 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist index 62faaf4c00..a1c5159b35 100644 --- a/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist +++ b/sysdeps/unix/sysv/linux/loongarch/lp64/libc.abilist @@ -2154,3 +2154,4 @@ GLIBC_2.36 wprintf F GLIBC_2.36 write F GLIBC_2.36 writev F GLIBC_2.36 wscanf F +GLIBC_2.38 dlmem F diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist index 16243a7a92..6f88cb7601 100644 --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist @@ -515,6 +515,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 _Exit F GLIBC_2.4 _IO_2_1_stderr_ D 0x98 GLIBC_2.4 _IO_2_1_stdin_ D 0x98 diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist index 564a553b27..7d4f3ced11 100644 --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist @@ -2746,6 +2746,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist index e850f47b21..6ba84f7881 100644 --- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist @@ -2719,3 +2719,4 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist index 37178c503f..0ce48a4126 100644 --- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist @@ -2716,3 +2716,4 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist index 3b30b31466..b8a47b300c 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist @@ -2711,6 +2711,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist index 0e358570a2..5c3eaee44e 100644 --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist @@ -2709,6 +2709,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist index 59c598b98f..aa17ef60dd 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist @@ -2717,6 +2717,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist index 2f7f1ccaf7..2dfd4321c7 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist @@ -2619,6 +2619,7 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist index 463e01ab84..3ffe7b6530 100644 --- a/sysdeps/unix/sysv/linux/nios2/libc.abilist +++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist @@ -2758,3 +2758,4 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F diff --git a/sysdeps/unix/sysv/linux/or1k/libc.abilist b/sysdeps/unix/sysv/linux/or1k/libc.abilist index ffdb8819d5..2cf1bc4df7 100644 --- a/sysdeps/unix/sysv/linux/or1k/libc.abilist +++ b/sysdeps/unix/sysv/linux/or1k/libc.abilist @@ -2140,3 +2140,4 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist index 405d40d11c..1291a13c3d 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist @@ -2773,6 +2773,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 _IO_fprintf F GLIBC_2.4 _IO_printf F GLIBC_2.4 _IO_sprintf F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist index ce89602b93..491abbf990 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist @@ -2806,6 +2806,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 _IO_fprintf F GLIBC_2.4 _IO_printf F GLIBC_2.4 _IO_sprintf F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist index 849863e639..2c78254f0d 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist @@ -2527,6 +2527,7 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F GLIBC_2.4 _IO_fprintf F GLIBC_2.4 _IO_printf F GLIBC_2.4 _IO_sprintf F diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist index b2ccee08c6..eeb53b3bf7 100644 --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist @@ -2829,3 +2829,4 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist index ff90d1bff2..4a3c25b9ab 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist @@ -2396,3 +2396,4 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist index f1017f6ec5..70215e8479 100644 --- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist @@ -2596,3 +2596,4 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist index 5ca051a9eb..abda2933b4 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist @@ -2771,6 +2771,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 _IO_fprintf F GLIBC_2.4 _IO_printf F GLIBC_2.4 _IO_sprintf F diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist index 0e0b3df973..ba73fa4be0 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist @@ -2564,6 +2564,7 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F GLIBC_2.4 _IO_fprintf F GLIBC_2.4 _IO_printf F GLIBC_2.4 _IO_sprintf F diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist index 5b48168ec6..61f475d5c2 100644 --- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist +++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist @@ -2626,6 +2626,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist index c42b39cea8..a3b14edbfc 100644 --- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist +++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist @@ -2623,6 +2623,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist index 5a0a662dee..034e1bf1a7 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist @@ -2766,6 +2766,7 @@ GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F GLIBC_2.37 __ppoll64_chk F +GLIBC_2.38 dlmem F GLIBC_2.4 _IO_fprintf F GLIBC_2.4 _IO_printf F GLIBC_2.4 _IO_sprintf F diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist index 9ec4a0bc7f..f97c995f45 100644 --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist @@ -2591,6 +2591,7 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist index 367c8d0a03..441f397cb0 100644 --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist @@ -2542,6 +2542,7 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F GLIBC_2.4 __confstr_chk F GLIBC_2.4 __fgets_chk F GLIBC_2.4 __fgets_unlocked_chk F diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist index 6a614efb62..b24093a386 100644 --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist @@ -2648,3 +2648,4 @@ GLIBC_2.36 pidfd_open F GLIBC_2.36 pidfd_send_signal F GLIBC_2.36 process_madvise F GLIBC_2.36 process_mrelease F +GLIBC_2.38 dlmem F