/* Map in a shared object's segments from the file.
- Copyright (C) 1995-2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 1995-2014 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
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, write to the Free
- Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307 USA. */
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
#include <elf.h>
#include <errno.h>
#include <stackinfo.h>
#include <caller.h>
#include <sysdep.h>
+#include <stap-probe.h>
#include <dl-dst.h>
overwritten. Some losing VM systems like Linux's lack MAP_COPY. All we
get is MAP_PRIVATE, which copies each page when it is modified; this
means if the file is overwritten, we may at some point get some pages
- from the new version after starting with pages from the old version. */
+ from the new version after starting with pages from the old version.
+
+ To make up for the lack and avoid the overwriting problem,
+ what Linux does have is MAP_DENYWRITE. This prevents anyone
+ from modifying the file while we have it mapped. */
#ifndef MAP_COPY
-# define MAP_COPY MAP_PRIVATE
+# ifdef MAP_DENYWRITE
+# define MAP_COPY (MAP_PRIVATE | MAP_DENYWRITE)
+# else
+# define MAP_COPY MAP_PRIVATE
+# endif
#endif
/* Some systems link their relocatable objects for another base address
#define STRING(x) __STRING (x)
-#ifdef MAP_ANON
-/* The fd is not examined when using MAP_ANON. */
-# define ANONFD -1
-#else
-int _dl_zerofd = -1;
-# define ANONFD _dl_zerofd
-#endif
-
/* Handle situations where we have a preferred location in memory for
the shared objects. */
#ifdef ELF_PREFERRED_ADDRESS_DATA
question is how large are the ELF and program header combined. The
ELF header 32-bit files is 52 bytes long and in 64-bit files is 64
bytes long. Each program header entry is again 32 and 56 bytes
- long respectively. I.e., even with a file which has 7 program
- header entries we only have to read 512B. Add to this a bit of
- margin for program notes and reading 512B and 640B for 32-bit and
- 64-bit files respecitvely is enough. If this heuristic should
- really fail for some file the code in `_dl_map_object_from_fd'
- knows how to recover. */
+ long respectively. I.e., even with a file which has 10 program
+ header entries we only have to read 372B/624B respectively. Add to
+ this a bit of margin for program notes and reading 512B and 832B
+ for 32-bit and 64-bit files respecitvely is enough. If this
+ heuristic should really fail for some file the code in
+ `_dl_map_object_from_fd' knows how to recover. */
struct filebuf
{
ssize_t len;
#if __WORDSIZE == 32
# define FILEBUF_SIZE 512
#else
-# define FILEBUF_SIZE 640
+# define FILEBUF_SIZE 832
#endif
char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
};
/* Local version of `strdup' function. */
-static inline char *
+static char *
local_strdup (const char *s)
{
size_t len = strlen (s) + 1;
}
+static bool
+is_trusted_path (const char *path, size_t len)
+{
+ const char *trun = system_dirs;
+
+ for (size_t idx = 0; idx < nsystem_dirs_len; ++idx)
+ {
+ if (len == system_dirs_len[idx] && memcmp (trun, path, len) == 0)
+ /* Found it. */
+ return true;
+
+ trun += system_dirs_len[idx] + 1;
+ }
+
+ return false;
+}
+
+
+static bool
+is_trusted_path_normalize (const char *path, size_t len)
+{
+ if (len == 0)
+ return false;
+
+ if (*path == ':')
+ {
+ ++path;
+ --len;
+ }
+
+ char *npath = (char *) alloca (len + 2);
+ char *wnp = npath;
+ while (*path != '\0')
+ {
+ if (path[0] == '/')
+ {
+ if (path[1] == '.')
+ {
+ if (path[2] == '.' && (path[3] == '/' || path[3] == '\0'))
+ {
+ while (wnp > npath && *--wnp != '/')
+ ;
+ path += 3;
+ continue;
+ }
+ else if (path[2] == '/' || path[2] == '\0')
+ {
+ path += 2;
+ continue;
+ }
+ }
+
+ if (wnp > npath && wnp[-1] == '/')
+ {
+ ++path;
+ continue;
+ }
+ }
+
+ *wnp++ = *path++;
+ }
+
+ if (wnp == npath || wnp[-1] != '/')
+ *wnp++ = '/';
+
+ const char *trun = system_dirs;
+
+ for (size_t idx = 0; idx < nsystem_dirs_len; ++idx)
+ {
+ if (wnp - npath >= system_dirs_len[idx]
+ && memcmp (trun, npath, system_dirs_len[idx]) == 0)
+ /* Found it. */
+ return true;
+
+ trun += system_dirs_len[idx] + 1;
+ }
+
+ return false;
+}
+
+
static size_t
is_dst (const char *start, const char *name, const char *str,
int is_path, int secure)
return 0;
if (__builtin_expect (secure, 0)
- && ((name[len] != '\0' && (!is_path || name[len] != ':'))
+ && ((name[len] != '\0' && name[len] != '/'
+ && (!is_path || name[len] != ':'))
|| (name != start + 1 && (!is_path || name[-2] != ':'))))
return 0;
int is_path)
{
const char *const start = name;
- char *last_elem, *wp;
/* Now fill the result path. While copying over the string we keep
- track of the start of the last path element. When we come accross
+ track of the start of the last path element. When we come across
a DST we copy over the value or (if the value is not available)
leave the entire path element out. */
- last_elem = wp = result;
+ char *wp = result;
+ char *last_elem = result;
+ bool check_for_trusted = false;
do
{
++name;
if ((len = is_dst (start, name, "ORIGIN", is_path,
INTUSE(__libc_enable_secure))) != 0)
- repl = l->l_origin;
+ {
+ repl = l->l_origin;
+ check_for_trusted = (INTUSE(__libc_enable_secure)
+ && l->l_type == lt_executable);
+ }
else if ((len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0)
repl = GLRO(dl_platform);
else if ((len = is_dst (start, name, "LIB", is_path, 0)) != 0)
name += len;
while (*name != '\0' && (!is_path || *name != ':'))
++name;
+ /* Also skip following colon if this is the first rpath
+ element, but keep an empty element at the end. */
+ if (wp == result && is_path && *name == ':' && name[1] != '\0')
+ ++name;
}
else
/* No DST we recognize. */
{
*wp++ = *name++;
if (is_path && *name == ':')
- last_elem = wp;
+ {
+ /* In SUID/SGID programs, after $ORIGIN expansion the
+ normalized path must be rooted in one of the trusted
+ directories. */
+ if (__builtin_expect (check_for_trusted, false)
+ && !is_trusted_path_normalize (last_elem, wp - last_elem))
+ wp = last_elem;
+ else
+ last_elem = wp;
+
+ check_for_trusted = false;
+ }
}
}
while (*name != '\0');
+ /* In SUID/SGID programs, after $ORIGIN expansion the normalized
+ path must be rooted in one of the trusted directories. */
+ if (__builtin_expect (check_for_trusted, false)
+ && !is_trusted_path_normalize (last_elem, wp - last_elem))
+ wp = last_elem;
+
*wp = '\0';
return result;
belonging to the map is loaded. In this case the path element
containing $ORIGIN is left out. */
static char *
-expand_dynamic_string_token (struct link_map *l, const char *s)
+expand_dynamic_string_token (struct link_map *l, const char *s, int is_path)
{
/* We make two runs over the string. First we determine how large the
- resulting string is and then we copy it over. Since this is now
+ resulting string is and then we copy it over. Since this is no
frequently executed operation we are looking here not for performance
but rather for code size. */
size_t cnt;
char *result;
/* Determine the number of DST elements. */
- cnt = DL_DST_COUNT (s, 1);
+ cnt = DL_DST_COUNT (s, is_path);
/* If we do not have to replace anything simply copy the string. */
if (__builtin_expect (cnt, 0) == 0)
if (result == NULL)
return NULL;
- return _dl_dst_substitute (l, s, result, 1);
+ return _dl_dst_substitute (l, s, result, is_path);
}
static struct r_search_path_elem **
fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
- int check_trusted, const char *what, const char *where)
+ int check_trusted, const char *what, const char *where,
+ struct link_map *l)
{
char *cp;
size_t nelems = 0;
+ char *to_free;
while ((cp = __strsep (&rpath, sep)) != NULL)
{
struct r_search_path_elem *dirp;
+
+ to_free = cp = expand_dynamic_string_token (l, cp, 1);
+
size_t len = strlen (cp);
/* `strsep' can pass an empty string. This has to be
- interpreted as `use the current directory'. */
+ interpreted as `use the current directory'. */
if (len == 0)
{
static const char curwd[] = "./";
cp[len++] = '/';
/* Make sure we don't use untrusted directories if we run SUID. */
- if (__builtin_expect (check_trusted, 0))
+ if (__builtin_expect (check_trusted, 0) && !is_trusted_path (cp, len))
{
- const char *trun = system_dirs;
- size_t idx;
- int unsecure = 1;
-
- /* All trusted directories must be complete names. */
- if (cp[0] == '/')
- {
- for (idx = 0; idx < nsystem_dirs_len; ++idx)
- {
- if (len == system_dirs_len[idx]
- && memcmp (trun, cp, len) == 0)
- {
- /* Found it. */
- unsecure = 0;
- break;
- }
-
- trun += system_dirs_len[idx] + 1;
- }
- }
-
- if (unsecure)
- /* Simply drop this directory. */
- continue;
+ free (to_free);
+ continue;
}
/* See if this directory is already known. */
/* Put it in the result array. */
result[nelems++] = dirp;
}
+ free (to_free);
}
/* Terminate the array. */
}
-static void
+static bool
internal_function
decompose_rpath (struct r_search_path_struct *sps,
const char *rpath, struct link_map *l, const char *what)
{
/* This object is on the list of objects for which the
RUNPATH and RPATH must not be used. */
- result = calloc (1, sizeof *result);
- if (result == NULL)
- {
- signal_error_cache:
- errstring = N_("cannot create cache for search path");
- signal_error:
- _dl_signal_error (ENOMEM, NULL, NULL, errstring);
- }
-
- sps->dirs = result;
- sps->malloced = 1;
-
- return;
+ sps->dirs = (void *) -1;
+ return false;
}
while (*inhp != '\0')
while (*inhp != '\0');
}
- /* Make a writable copy. At the same time expand possible dynamic
- string tokens. */
- copy = expand_dynamic_string_token (l, rpath);
+ /* Make a writable copy. */
+ copy = local_strdup (rpath);
if (copy == NULL)
{
errstring = N_("cannot create RUNPATH/RPATH copy");
goto signal_error;
}
+ /* Ignore empty rpaths. */
+ if (*copy == 0)
+ {
+ free (copy);
+ sps->dirs = (struct r_search_path_elem **) -1;
+ return false;
+ }
+
/* Count the number of necessary elements in the result array. */
nelems = 0;
for (cp = copy; *cp != '\0'; ++cp)
result = (struct r_search_path_elem **) malloc ((nelems + 1 + 1)
* sizeof (*result));
if (result == NULL)
- goto signal_error_cache;
+ {
+ free (copy);
+ errstring = N_("cannot create cache for search path");
+ signal_error:
+ _dl_signal_error (ENOMEM, NULL, NULL, errstring);
+ }
- fillin_rpath (copy, result, ":", 0, what, where);
+ fillin_rpath (copy, result, ":", 0, what, where, l);
/* Free the copied RPATH string. `fillin_rpath' make own copies if
necessary. */
sps->dirs = result;
/* The caller will change this value if we haven't used a real malloc. */
sps->malloced = 1;
+ return true;
}
/* Make sure cached path information is stored in *SP
}
/* Make sure the cache information is available. */
- decompose_rpath (sp, (const char *) (D_PTR (l, l_info[DT_STRTAB])
- + l->l_info[tag]->d_un.d_val),
- l, what);
- return true;
+ return decompose_rpath (sp, (const char *) (D_PTR (l, l_info[DT_STRTAB])
+ + l->l_info[tag]->d_un.d_val),
+ l, what);
}
const char *strp;
struct r_search_path_elem *pelem, **aelem;
size_t round_size;
-#ifdef SHARED
- struct link_map *l;
-#endif
+ struct link_map __attribute__ ((unused)) *l = NULL;
/* Initialize to please the compiler. */
const char *errstring = NULL;
(const void *) (D_PTR (l, l_info[DT_STRTAB])
+ l->l_info[DT_RUNPATH]->d_un.d_val),
l, "RUNPATH");
+ /* During rtld init the memory is allocated by the stub malloc,
+ prevent any attempt to free it by the normal malloc. */
+ l->l_runpath_dirs.malloced = 0;
/* The RPATH is ignored. */
l->l_rpath_dirs.dirs = (void *) -1;
(const void *) (D_PTR (l, l_info[DT_STRTAB])
+ l->l_info[DT_RPATH]->d_un.d_val),
l, "RPATH");
+ /* During rtld init the memory is allocated by the stub
+ malloc, prevent any attempt to free it by the normal
+ malloc. */
l->l_rpath_dirs.malloced = 0;
}
else
{
size_t nllp;
const char *cp = llp;
- char *llp_tmp = strdupa (llp);
+ char *llp_tmp;
+
+#ifdef SHARED
+ /* Expand DSTs. */
+ size_t cnt = DL_DST_COUNT (llp, 1);
+ if (__builtin_expect (cnt == 0, 1))
+ llp_tmp = strdupa (llp);
+ else
+ {
+ /* Determine the length of the substituted string. */
+ size_t total = DL_DST_REQUIRED (l, llp, strlen (llp), cnt);
+
+ /* Allocate the necessary memory. */
+ llp_tmp = (char *) alloca (total + 1);
+ llp_tmp = _dl_dst_substitute (l, llp, llp_tmp, 1);
+ }
+#else
+ llp_tmp = strdupa (llp);
+#endif
/* Decompose the LD_LIBRARY_PATH contents. First determine how many
elements it has. */
(void) fillin_rpath (llp_tmp, env_path_list.dirs, ":;",
INTUSE(__libc_enable_secure), "LD_LIBRARY_PATH",
- NULL);
+ NULL, l);
if (env_path_list.dirs[0] == NULL)
{
}
else
env_path_list.dirs = (void *) -1;
-
- /* Remember the last search directory added at startup. */
- GLRO(dl_init_all_dirs) = GL(dl_all_dirs);
}
static void
__attribute__ ((noreturn, noinline))
lose (int code, int fd, const char *name, char *realname, struct link_map *l,
- const char *msg)
+ const char *msg, struct r_debug *r, Lmid_t nsid)
{
/* The file might already be closed. */
if (fd != -1)
(void) __close (fd);
- if (l != NULL)
+ if (l != NULL && l->l_origin != (char *) -1l)
+ free ((char *) l->l_origin);
+ free (l);
+ free (realname);
+
+ if (r != NULL)
{
- /* Remove the stillborn object from the list and free it. */
- assert (l->l_next == NULL);
- if (l->l_prev == NULL)
- /* No other module loaded. This happens only in the static library,
- or in rtld under --verify. */
- GL(dl_ns)[l->l_ns]._ns_loaded = NULL;
- else
- l->l_prev->l_next = NULL;
- --GL(dl_ns)[l->l_ns]._ns_nloaded;
- free (l);
+ r->r_state = RT_CONSISTENT;
+ _dl_debug_state ();
+ LIBC_PROBE (map_failed, 2, nsid, r);
}
- free (realname);
+
_dl_signal_error (code, name, NULL, msg);
}
call_lose_errno:
errval = errno;
call_lose:
- if (make_consistent)
- {
- r->r_state = RT_CONSISTENT;
- _dl_debug_state ();
- }
-
- lose (errval, fd, name, realname, l, errstring);
+ lose (errval, fd, name, realname, l, errstring,
+ make_consistent ? r : NULL, nsid);
}
/* Look again to see if the real name matched another already loaded. */
never be unloaded. */
__close (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. */
- return NULL;
+ {
+ /* We are not supposed to load the object unless it is already
+ loaded. So return now. */
+ free (realname);
+ __close (fd);
+ return NULL;
+ }
/* Print debugging message. */
if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0))
_dl_zerofd = _dl_sysdep_open_zero_fill ();
if (_dl_zerofd == -1)
{
+ free (realname);
__close (fd);
_dl_signal_error (errno, NULL, NULL,
N_("cannot open zero fill device"));
{
#ifdef SHARED
/* Auditing checkpoint: we are going to add new objects. */
- if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
+ if ((mode & __RTLD_AUDIT) == 0
+ && __builtin_expect (GLRO(dl_naudit) > 0, 0))
{
struct link_map *head = GL(dl_ns)[nsid]._ns_loaded;
/* Do not call the functions for any auditing object. */
linking has not been used before. */
r->r_state = RT_ADD;
_dl_debug_state ();
+ LIBC_PROBE (map_start, 2, nsid, r);
make_consistent = true;
}
else
}
}
- /* Presumed absent PT_GNU_STACK. */
- uint_fast16_t stack_flags = PF_R|PF_W|PF_X;
+ /* 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. */
+ uint_fast16_t stack_flags = DEFAULT_STACK_PERMS;
{
/* Scan the program header table, collecting its load commands. */
struct loadcmd
{
ElfW(Addr) mapstart, mapend, dataend, allocend;
- off_t mapoff;
+ ElfW(Off) mapoff;
int prot;
} loadcmds[l->l_phnum], *c;
size_t nloadcmds = 0;
break;
case PT_TLS:
-#ifdef USE_TLS
if (ph->p_memsz == 0)
/* Nothing to do for an empty segment. */
break;
break;
}
-# ifdef SHARED
+#ifdef SHARED
if (l->l_prev == NULL || (mode & __RTLD_AUDIT) != 0)
/* We are loading the executable itself when the dynamic linker
was executed directly. The setup will happen later. */
break;
+# ifdef _LIBC_REENTRANT
/* In a static binary there is no way to tell if we dynamically
loaded libpthread. */
if (GL(dl_error_catch_tsd) == &_dl_initial_error_catch_tsd)
# endif
+#endif
{
/* We have not yet loaded libpthread.
We can do the TLS setup right now! */
_dl_deallocate_tls (tcb, 1);
goto call_lose;
}
-#endif
/* Uh-oh, the binary expects TLS support but we cannot
provide it. */
/* Remember which part of the address space this object uses. */
l->l_map_start = (ElfW(Addr)) __mmap ((void *) mappref, maplength,
c->prot,
- MAP_COPY | MAP_FILE,
+ MAP_COPY|MAP_FILE,
fd, c->mapoff);
if (__builtin_expect ((void *) l->l_map_start == MAP_FAILED, 0))
{
handle the portion of the segment past the end of the file
mapping. */
__mprotect ((caddr_t) (l->l_addr + c->mapend),
- loadcmds[nloadcmds - 1].allocend - c->mapend,
+ loadcmds[nloadcmds - 1].mapstart - c->mapend,
PROT_NONE);
+ l->l_contiguous = 1;
+
goto postmap;
}
/* Remember which part of the address space this object uses. */
l->l_map_start = c->mapstart + l->l_addr;
l->l_map_end = l->l_map_start + maplength;
+ l->l_contiguous = !has_holes;
while (c < &loadcmds[nloadcmds])
{
/* Map the segment contents from the file. */
&& (__mmap ((void *) (l->l_addr + c->mapstart),
c->mapend - c->mapstart, c->prot,
- MAP_FIXED|MAP_COPY|MAP_FILE|MAP_DENYWRITE,
+ MAP_FIXED|MAP_COPY|MAP_FILE,
fd, c->mapoff)
== MAP_FAILED))
goto map_error;
l->l_text_end = l->l_addr + c->mapend;
if (l->l_phdr == 0
- && (ElfW(Off)) c->mapoff <= header->e_phoff
+ && c->mapoff <= header->e_phoff
&& ((size_t) (c->mapend - c->mapstart + c->mapoff)
>= header->e_phoff + header->e_phnum * sizeof (ElfW(Phdr))))
/* Found the program header in this segment. */
- l->l_phdr = (void *) (c->mapstart + header->e_phoff - c->mapoff);
+ l->l_phdr = (void *) (uintptr_t) (c->mapstart + header->e_phoff
+ - c->mapoff);
if (c->allocend > c->dataend)
{
caddr_t mapat;
mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,
c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
- ANONFD, 0);
+ -1, 0);
if (__builtin_expect (mapat == MAP_FAILED, 0))
{
errstring = N_("cannot map zero-fill pages");
if (__builtin_expect ((stack_flags &~ GL(dl_stack_flags)) & PF_X, 0))
{
+ if (__builtin_expect (__check_caller (RETURN_ADDRESS (0), allow_ldso),
+ 0) != 0)
+ {
+ errstring = N_("invalid caller");
+ goto call_lose;
+ }
+
/* The stack is presently not executable, but this module
requires that it be executable. We must change the
protection of the variable which contains the flags used in
the mprotect calls. */
-#ifdef HAVE_Z_RELRO
+#ifdef SHARED
if ((mode & (__RTLD_DLOPEN | __RTLD_AUDIT)) == __RTLD_DLOPEN)
{
- uintptr_t p = ((uintptr_t) &__stack_prot) & ~(GLRO(dl_pagesize) - 1);
- size_t s = (uintptr_t) &__stack_prot - p + sizeof (int);
-
- __mprotect ((void *) p, s, PROT_READ|PROT_WRITE);
- if (__builtin_expect (__check_caller (RETURN_ADDRESS (0),
- allow_ldso) == 0,
- 0))
+ const uintptr_t p = (uintptr_t) &__stack_prot & -GLRO(dl_pagesize);
+ const size_t s = (uintptr_t) (&__stack_prot + 1) - p;
+
+ struct link_map *const m = &GL(dl_rtld_map);
+ const uintptr_t relro_end = ((m->l_addr + m->l_relro_addr
+ + m->l_relro_size)
+ & -GLRO(dl_pagesize));
+ if (__builtin_expect (p + s <= relro_end, 1))
+ {
+ /* The variable lies in the region protected by RELRO. */
+ if (__mprotect ((void *) p, s, PROT_READ|PROT_WRITE) < 0)
+ {
+ errstring = N_("cannot change memory protections");
+ goto call_lose_errno;
+ }
+ __stack_prot |= PROT_READ|PROT_WRITE|PROT_EXEC;
+ __mprotect ((void *) p, s, PROT_READ);
+ }
+ else
__stack_prot |= PROT_READ|PROT_WRITE|PROT_EXEC;
- __mprotect ((void *) p, s, PROT_READ);
}
else
#endif
}
}
-#ifdef USE_TLS
/* Adjust the address of the TLS initialization image. */
if (l->l_tls_initimage != NULL)
l->l_tls_initimage = (char *) l->l_tls_initimage + l->l_addr;
-#endif
/* We are done mapping in the file. We no longer need the descriptor. */
if (__builtin_expect (__close (fd) != 0, 0))
{
/* Create an appropriate searchlist. It contains only this map.
This is the definition of DT_SYMBOLIC in SysVr4. */
- l->l_symbolic_searchlist.r_list =
- (struct link_map **) malloc (sizeof (struct link_map *));
-
- if (l->l_symbolic_searchlist.r_list == NULL)
- {
- errstring = N_("cannot create searchlist");
- goto call_lose_errno;
- }
-
l->l_symbolic_searchlist.r_list[0] = l;
l->l_symbolic_searchlist.r_nlist = 1;
add_name_to_object (l, ((const char *) D_PTR (l, l_info[DT_STRTAB])
+ l->l_info[DT_SONAME]->d_un.d_val));
+#ifdef DL_AFTER_LOAD
+ DL_AFTER_LOAD (l);
+#endif
+
+ /* Now that the object is fully initialized add it to the object list. */
+ _dl_add_to_namespace_list (l, nsid);
+
#ifdef SHARED
/* Auditing checkpoint: we have a new object. */
if (__builtin_expect (GLRO(dl_naudit) > 0, 0)
/* Print search path. */
static void
print_search_path (struct r_search_path_elem **list,
- const char *what, const char *name)
+ const char *what, const char *name)
{
char buf[max_dirnamelen + max_capstrlen];
int first = 1;
if (name != NULL)
_dl_debug_printf_c ("\t\t(%s from file %s)\n", what,
- name[0] ? name : rtld_progname);
+ DSO_FILENAME (name));
else
_dl_debug_printf_c ("\t\t(%s)\n", what);
}
user might want to know about this. */
static int
open_verify (const char *name, struct filebuf *fbp, struct link_map *loader,
- int whatcode)
+ int whatcode, bool *found_other_class, bool free_name)
{
/* This is the expected ELF header. */
#define ELF32_CLASS ELFCLASS32
#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(ver) (ver == 0)
+# 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_PAD] =
+ static const unsigned char expected[EI_NIDENT] =
{
[EI_MAG0] = ELFMAG0,
[EI_MAG1] = ELFMAG1,
#endif
/* Open the file. We always open files read-only. */
- int fd = __open (name, O_RDONLY);
+ int fd = __open (name, O_RDONLY | O_CLOEXEC);
if (fd != -1)
{
ElfW(Ehdr) *ehdr;
ElfW(Phdr) *phdr, *ph;
- ElfW(Word) *abi_note, abi_note_buf[8];
+ ElfW(Word) *abi_note;
unsigned int osversion;
size_t maplength;
/* We successfully openened the file. Now verify it is a file
we can use. */
__set_errno (0);
- fbp->len = __libc_read (fd, fbp->buf, sizeof (fbp->buf));
+ fbp->len = 0;
+ assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr)));
+ /* Read in the header. */
+ do
+ {
+ ssize_t retlen = __libc_read (fd, fbp->buf + fbp->len,
+ sizeof (fbp->buf) - fbp->len);
+ if (retlen <= 0)
+ break;
+ fbp->len += retlen;
+ }
+ while (__builtin_expect (fbp->len < sizeof (ElfW(Ehdr)), 0));
/* This is where the ELF header is loaded. */
- assert (sizeof (fbp->buf) > sizeof (ElfW(Ehdr)));
ehdr = (ElfW(Ehdr) *) fbp->buf;
/* Now run the tests. */
errstring = (errval == 0
? N_("file too short") : N_("cannot read file data"));
call_lose:
- lose (errval, fd, name, NULL, NULL, errstring);
+ if (free_name)
+ {
+ char *realname = (char *) name;
+ name = strdupa (realname);
+ free (realname);
+ }
+ lose (errval, fd, name, NULL, NULL, errstring, NULL, 0);
}
/* See whether the ELF header is what we expect. */
if (__builtin_expect (! VALID_ELF_HEADER (ehdr->e_ident, expected,
- EI_PAD), 0))
+ 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,
+ 0))
{
/* Something is wrong. */
- if (*(Elf32_Word *) &ehdr->e_ident !=
+ const Elf32_Word *magp = (const void *) ehdr->e_ident;
+ if (*magp !=
#if BYTE_ORDER == LITTLE_ENDIAN
((ELFMAG0 << (EI_MAG0 * 8)) |
(ELFMAG1 << (EI_MAG1 * 8)) |
)
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. */
- goto close_and_out;
+ {
+ /* 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;
+ goto close_and_out;
+ }
else if (ehdr->e_ident[EI_DATA] != byteorder)
{
if (BYTE_ORDER == BIG_ENDIAN)
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_ABIVERSION]))
+ 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");
/* Check .note.ABI-tag if present. */
for (ph = phdr; ph < &phdr[ehdr->e_phnum]; ++ph)
- if (ph->p_type == PT_NOTE && ph->p_filesz == 32 && ph->p_align >= 4)
+ if (ph->p_type == PT_NOTE && ph->p_filesz >= 32 && ph->p_align >= 4)
{
- if (ph->p_offset + 32 <= (size_t) fbp->len)
+ ElfW(Addr) size = ph->p_filesz;
+
+ if (ph->p_offset + size <= (size_t) fbp->len)
abi_note = (void *) (fbp->buf + ph->p_offset);
else
{
+ abi_note = alloca (size);
__lseek (fd, ph->p_offset, SEEK_SET);
- if (__libc_read (fd, (void *) abi_note_buf, 32) != 32)
+ if (__libc_read (fd, (void *) abi_note, size) != size)
goto read_error;
+ }
+
+ while (memcmp (abi_note, &expected_note, sizeof (expected_note)))
+ {
+#define ROUND(len) (((len) + sizeof (ElfW(Word)) - 1) & -sizeof (ElfW(Word)))
+ ElfW(Addr) note_size = 3 * sizeof (ElfW(Word))
+ + ROUND (abi_note[0])
+ + ROUND (abi_note[1]);
- abi_note = abi_note_buf;
+ if (size - 32 < note_size)
+ {
+ size = 0;
+ break;
+ }
+ size -= note_size;
+ abi_note = (void *) abi_note + note_size;
}
- if (memcmp (abi_note, &expected_note, sizeof (expected_note)))
+ if (size == 0)
continue;
osversion = (abi_note[5] & 0xff) * 65536
if MAY_FREE_DIRS is true. */
static int
-open_path (const char *name, size_t namelen, int preloaded,
+open_path (const char *name, size_t namelen, int secure,
struct r_search_path_struct *sps, char **realname,
- struct filebuf *fbp, struct link_map *loader, int whatcode)
+ struct filebuf *fbp, struct link_map *loader, int whatcode,
+ bool *found_other_class)
{
struct r_search_path_elem **dirs = sps->dirs;
char *buf;
if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
_dl_debug_printf (" trying file=%s\n", buf);
- fd = open_verify (buf, fbp, loader, whatcode);
+ fd = open_verify (buf, fbp, loader, whatcode, found_other_class,
+ false);
if (this_dir->status[cnt] == unknown)
{
if (fd != -1)
auditing code. We must try to disturb the program as
little as possible. */
else if (loader == NULL
- || GL(dl_ns)[loader->l_ns]._ns_loaded->l_audit == 0)
+ || GL(dl_ns)[loader->l_ns]._ns_loaded->l_auditing == 0)
{
/* We failed to open machine dependent library. Let's
test whether there is any directory at all. */
/* Remember whether we found any existing directory. */
here_any |= this_dir->status[cnt] != nonexisting;
- if (fd != -1 && __builtin_expect (preloaded, 0)
+ if (fd != -1 && __builtin_expect (secure, 0)
&& INTUSE(__libc_enable_secure))
{
/* This is an extra security effort to make sure nobody can
must not be freed using the general free() in libc. */
if (sps->malloced)
free (sps->dirs);
-#ifdef HAVE_Z_RELRO
+
/* rtld_search_dirs is attribute_relro, therefore avoid writing
into it. */
if (sps != &rtld_search_dirs)
-#endif
sps->dirs = (void *) -1;
}
struct link_map *
internal_function
-_dl_map_object (struct link_map *loader, const char *name, int preloaded,
+_dl_map_object (struct link_map *loader, const char *name,
int type, int trace_mode, int mode, Lmid_t nsid)
{
int fd;
struct filebuf fb;
assert (nsid >= 0);
- assert (nsid < DL_NNS);
+ assert (nsid < GL(dl_nns));
/* Look for this name among those already loaded. */
for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next)
/* Display information if we are debugging. */
if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)
&& loader != NULL)
- _dl_debug_printf ("\nfile=%s [%lu]; needed by %s [%lu]\n", name, nsid,
- loader->l_name[0]
- ? loader->l_name : rtld_progname, loader->l_ns);
+ _dl_debug_printf ((mode & __RTLD_CALLMAP) == 0
+ ? "\nfile=%s [%lu]; needed by %s [%lu]\n"
+ : "\nfile=%s [%lu]; dynamically loaded by %s [%lu]\n",
+ name, nsid, DSO_FILENAME (loader->l_name), loader->l_ns);
#ifdef SHARED
/* Give the auditing libraries a chance to change the name before we
}
#endif
+ /* Will be true if we found a DSO which is of the other ELF class. */
+ bool found_other_class = false;
+
if (strchr (name, '/') == NULL)
{
/* Search for NAME in several places. */
fd = -1;
/* When the object has the RUNPATH information we don't use any
- RPATHs. */
+ RPATHs. */
if (loader == NULL || loader->l_info[DT_RUNPATH] == NULL)
{
+ /* This is the executable's map (if there is one). Make sure that
+ we do not look at it twice. */
+ struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+ bool did_main_map = false;
+
/* First try the DT_RPATH of the dependent object that caused NAME
to be loaded. Then that object's dependent, and on up. */
- for (l = loader; fd == -1 && l; l = l->l_loader)
+ for (l = loader; l; l = l->l_loader)
if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH"))
- fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs,
- &realname, &fb, loader, LA_SER_RUNPATH);
+ {
+ fd = open_path (name, namelen, mode & __RTLD_SECURE,
+ &l->l_rpath_dirs,
+ &realname, &fb, loader, LA_SER_RUNPATH,
+ &found_other_class);
+ if (fd != -1)
+ break;
+
+ did_main_map |= l == main_map;
+ }
/* If dynamically linked, try the DT_RPATH of the executable
- itself. NB: we do this for lookups in any namespace. */
- if (fd == -1)
- {
- l = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
- if (l && l->l_type != lt_loaded && l != loader
- && cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH"))
- fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs,
- &realname, &fb, loader ?: l, LA_SER_RUNPATH);
- }
+ itself. NB: we do this for lookups in any namespace. */
+ if (fd == -1 && !did_main_map
+ && main_map != NULL && main_map->l_type != lt_loaded
+ && cache_rpath (main_map, &main_map->l_rpath_dirs, DT_RPATH,
+ "RPATH"))
+ fd = open_path (name, namelen, mode & __RTLD_SECURE,
+ &main_map->l_rpath_dirs,
+ &realname, &fb, loader ?: main_map, LA_SER_RUNPATH,
+ &found_other_class);
}
/* Try the LD_LIBRARY_PATH environment variable. */
if (fd == -1 && env_path_list.dirs != (void *) -1)
- fd = open_path (name, namelen, preloaded, &env_path_list,
+ fd = open_path (name, namelen, mode & __RTLD_SECURE, &env_path_list,
&realname, &fb,
loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded,
- LA_SER_LIBPATH);
+ LA_SER_LIBPATH, &found_other_class);
/* Look at the RUNPATH information for this binary. */
if (fd == -1 && loader != NULL
&& cache_rpath (loader, &loader->l_runpath_dirs,
DT_RUNPATH, "RUNPATH"))
- fd = open_path (name, namelen, preloaded,
+ fd = open_path (name, namelen, mode & __RTLD_SECURE,
&loader->l_runpath_dirs, &realname, &fb, loader,
- LA_SER_RUNPATH);
+ LA_SER_RUNPATH, &found_other_class);
+#ifdef USE_LDCONFIG
if (fd == -1
- && (__builtin_expect (! preloaded, 1)
- || ! INTUSE(__libc_enable_secure)))
+ && (__builtin_expect (! (mode & __RTLD_SECURE), 1)
+ || ! INTUSE(__libc_enable_secure))
+ && __builtin_expect (GLRO(dl_inhibit_cache) == 0, 1))
{
/* Check the list of libraries in the file /etc/ld.so.cache,
for compatibility with Linux's ldconfig program. */
if (cached != NULL)
{
-#ifdef SHARED
+# ifdef SHARED
// XXX Correct to unconditionally default to namespace 0?
- l = loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded;
-#else
+ l = (loader
+ ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded
+ ?: &GL(dl_rtld_map));
+# else
l = loader;
-#endif
+# endif
/* If the loader has the DF_1_NODEFLIB flag set we must not
use a cache entry from any of these directories. */
if (
-#ifndef SHARED
+# ifndef SHARED
/* 'l' is always != NULL for dynamically linked objects. */
l != NULL &&
-#endif
+# endif
__builtin_expect (l->l_flags_1 & DF_1_NODEFLIB, 0))
{
const char *dirp = system_dirs;
{
fd = open_verify (cached,
&fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
- LA_SER_CONFIG);
+ LA_SER_CONFIG, &found_other_class, false);
if (__builtin_expect (fd != -1, 1))
{
realname = local_strdup (cached);
}
}
}
+#endif
/* Finally, try the default path. */
if (fd == -1
&& ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL
|| __builtin_expect (!(l->l_flags_1 & DF_1_NODEFLIB), 1))
&& rtld_search_dirs.dirs != (void *) -1)
- fd = open_path (name, namelen, preloaded, &rtld_search_dirs,
- &realname, &fb, l, LA_SER_DEFAULT);
+ fd = open_path (name, namelen, mode & __RTLD_SECURE, &rtld_search_dirs,
+ &realname, &fb, l, LA_SER_DEFAULT, &found_other_class);
/* Add another newline when we are tracing the library loading. */
if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
- _dl_debug_printf ("\n");
+ _dl_debug_printf ("\n");
}
else
{
/* The path may contain dynamic string tokens. */
realname = (loader
- ? expand_dynamic_string_token (loader, name)
+ ? expand_dynamic_string_token (loader, name, 0)
: local_strdup (name));
if (realname == NULL)
fd = -1;
else
{
fd = open_verify (realname, &fb,
- loader ?: GL(dl_ns)[nsid]._ns_loaded, 0);
+ loader ?: GL(dl_ns)[nsid]._ns_loaded, 0,
+ &found_other_class, true);
if (__builtin_expect (fd, 0) == -1)
free (realname);
}
have. */
static const Elf_Symndx dummy_bucket = STN_UNDEF;
- /* Enter the new object in the list of loaded objects. */
+ /* Allocate a new object map. */
if ((name_copy = local_strdup (name)) == NULL
|| (l = _dl_new_object (name_copy, name, type, loader,
mode, nsid)) == NULL)
- _dl_signal_error (ENOMEM, name, NULL,
- N_("cannot create shared object descriptor"));
+ {
+ free (name_copy);
+ _dl_signal_error (ENOMEM, name, NULL,
+ N_("cannot create shared object descriptor"));
+ }
/* Signal that this is a faked entry. */
l->l_faked = 1;
/* Since the descriptor is initialized with zero we do not
l->l_nbuckets = 1;
l->l_relocated = 1;
+ /* Enter the object in the object list. */
+ _dl_add_to_namespace_list (l, nsid);
+
return l;
}
+ else if (found_other_class)
+ _dl_signal_error (0, name, NULL,
+ ELFW(CLASS) == ELFCLASS32
+ ? N_("wrong ELF class: ELFCLASS64")
+ : N_("wrong ELF class: ELFCLASS32"));
else
_dl_signal_error (errno, name, NULL,
N_("cannot open shared object file"));
if (counting)
{
si->dls_cnt++;
- si->dls_size += r->dirnamelen;
+ si->dls_size += MAX (2, r->dirnamelen);
}
else
{
Dl_serpath *const sp = &si->dls_serpath[idx++];
sp->dls_name = allocptr;
- allocptr = __mempcpy (allocptr,
- r->dirname, r->dirnamelen - 1);
+ if (r->dirnamelen < 2)
+ *allocptr++ = r->dirnamelen ? '/' : '.';
+ else
+ allocptr = __mempcpy (allocptr,
+ r->dirname, r->dirnamelen - 1);
*allocptr++ = '\0';
sp->dls_flags = flags;
}