@@ -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,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)
@@ -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