@@ -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,:
@@ -28,6 +28,9 @@ libc {
dlsym;
dlvsym;
}
+ GLIBC_2.38 {
+ dlmem;
+ }
GLIBC_PRIVATE {
__libc_dlerror_result;
_dlerror_run;
new file mode 100644
@@ -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
+ <https://www.gnu.org/licenses/>. */
+
+#include <dlfcn.h>
+#include <libintl.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <shlib-compat.h>
+
+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 */
new file mode 100644
@@ -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
+ <https://www.gnu.org/licenses/>. */
+
+#include <dlfcn.h>
+#include <link.h>
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+
+#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 <support/test-driver.c>
@@ -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
@@ -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)
{
@@ -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 <dl-machine-reject-phdr.h>
#include <dl-sysdep-open.h>
#include <dl-prop.h>
+#include <dl-main.h>
#include <not-cancel.h>
#include <endian.h>
@@ -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;
@@ -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 <dl-map-segments.h> 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
@@ -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
@@ -19,15 +19,23 @@
#include <dl-load.h>
+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;
@@ -40,6 +40,7 @@
#include <dl-dst.h>
#include <dl-prop.h>
+#include <dl-main.h>
/* 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)
@@ -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);
@@ -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"
new file mode 100644
@@ -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
+ <https://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <sys/mman.h>
+#include <gnu/lib-names.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <support/xstdio.h>
+#include <support/support.h>
+
+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 <support/test-driver.c>
@@ -21,3 +21,5 @@ foo (void)
{
return 10;
}
+
+int bar = 35;
new file mode 100644
@@ -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
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <link.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+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;
+}
@@ -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);
@@ -209,6 +209,7 @@ This function is a GNU extension.
@c dladdr1
@c dlclose
@c dlerror
+@c dlmem
@c dlmopen
@c dlopen
@c dlsym
@@ -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,11 @@ void _dl_audit_activity_map (struct link_map *l, int action)
void _dl_audit_activity_nsid (Lmid_t nsid, int action)
attribute_hidden;
+/* Call la_premap_dlmem from the audit modules from the link map and
+ get back an fd for dlmem(), or -1 for anonymous space. */
+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)
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
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 <stsp2@yandex.ru> --- 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 | 13 + 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, 1339 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