summaryrefslogtreecommitdiff
path: root/elf/dl-load.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/dl-load.c')
-rw-r--r--elf/dl-load.c602
1 files changed, 296 insertions, 306 deletions
diff --git a/elf/dl-load.c b/elf/dl-load.c
index c0d6249373..c51e4b3718 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1,5 +1,5 @@
/* Map in a shared object's segments from the file.
- Copyright (C) 1995-2016 Free Software Foundation, Inc.
+ Copyright (C) 1995-2018 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
@@ -30,13 +30,39 @@
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
+
+/* Type for the buffer we put the ELF header and hopefully the program
+ header. This buffer does not really have to be too large. In most
+ cases the program header follows the ELF header directly. If this
+ is not the case all bets are off and we can make the header
+ arbitrarily large and still won't get it read. This means the only
+ 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 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 832
+#endif
+ char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
+};
+
#include "dynamic-link.h"
#include <abi-tag.h>
#include <stackinfo.h>
-#include <caller.h>
#include <sysdep.h>
#include <stap-probe.h>
-#include <libc-internal.h>
+#include <libc-pointer-arith.h>
+#include <array_length.h>
#include <dl-dst.h>
#include <dl-load.h>
@@ -44,7 +70,8 @@
#include <dl-unmap-segments.h>
#include <dl-machine-reject-phdr.h>
#include <dl-sysdep-open.h>
-
+#include <dl-prop.h>
+#include <not-cancel.h>
#include <endian.h>
#if BYTE_ORDER == BIG_ENDIAN
@@ -69,31 +96,6 @@ int __stack_prot attribute_hidden attribute_relro
#endif
-/* Type for the buffer we put the ELF header and hopefully the program
- header. This buffer does not really have to be too large. In most
- cases the program header follows the ELF header directly. If this
- is not the case all bets are off and we can make the header
- arbitrarily large and still won't get it read. This means the only
- 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 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 832
-#endif
- char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
-};
-
/* This is the decomposed LD_LIBRARY_PATH search path. */
static struct r_search_path_struct env_path_list attribute_relro;
@@ -103,7 +105,9 @@ static size_t ncapstr attribute_relro;
static size_t max_capstrlen attribute_relro;
-/* Get the generated information about the trusted directories. */
+/* Get the generated information about the trusted directories. Use
+ an array of concatenated strings to avoid relocations. See
+ gen-trusted-dirs.awk. */
#include "trusted-dirs.h"
static const char system_dirs[] = SYSTEM_DIRS;
@@ -111,27 +115,7 @@ static const size_t system_dirs_len[] =
{
SYSTEM_DIRS_LEN
};
-#define nsystem_dirs_len \
- (sizeof (system_dirs_len) / sizeof (system_dirs_len[0]))
-
-
-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;
-}
-
+#define nsystem_dirs_len array_length (system_dirs_len)
static bool
is_trusted_path_normalize (const char *path, size_t len)
@@ -139,12 +123,6 @@ 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')
@@ -195,126 +173,167 @@ is_trusted_path_normalize (const char *path, size_t len)
return false;
}
+/* Given a substring starting at INPUT, just after the DST '$' start
+ token, determine if INPUT contains DST token REF, following the
+ ELF gABI rules for DSTs:
+
+ * Longest possible sequence using the rules (greedy).
+
+ * Must start with a $ (enforced by caller).
+
+ * Must follow $ with one underscore or ASCII [A-Za-z] (caller
+ follows these rules for REF) or '{' (start curly quoted name).
+
+ * Must follow first two characters with zero or more [A-Za-z0-9_]
+ (enforced by caller) or '}' (end curly quoted name).
+ If the sequence is a DST matching REF then the length of the DST
+ (excluding the $ sign but including curly braces, if any) is
+ returned, otherwise 0. */
static size_t
-is_dst (const char *start, const char *name, const char *str,
- int is_path, int secure)
+is_dst (const char *input, const char *ref)
{
- size_t len;
bool is_curly = false;
- if (name[0] == '{')
+ /* Is a ${...} input sequence? */
+ if (input[0] == '{')
{
is_curly = true;
- ++name;
+ ++input;
}
- len = 0;
- while (name[len] == str[len] && name[len] != '\0')
- ++len;
-
- if (is_curly)
- {
- if (name[len] != '}')
- return 0;
-
- /* Point again at the beginning of the name. */
- --name;
- /* Skip over closing curly brace and adjust for the --name. */
- len += 2;
- }
- else if (name[len] != '\0' && name[len] != '/'
- && (!is_path || name[len] != ':'))
+ /* Check for matching name, following closing curly brace (if
+ required), or trailing characters which are part of an
+ identifier. */
+ size_t rlen = strlen (ref);
+ if (strncmp (input, ref, rlen) != 0
+ || (is_curly && input[rlen] != '}')
+ || ((input[rlen] >= 'A' && input[rlen] <= 'Z')
+ || (input[rlen] >= 'a' && input[rlen] <= 'z')
+ || (input[rlen] >= '0' && input[rlen] <= '9')
+ || (input[rlen] == '_')))
return 0;
- if (__glibc_unlikely (secure)
- && ((name[len] != '\0' && name[len] != '/'
- && (!is_path || name[len] != ':'))
- || (name != start + 1 && (!is_path || name[-2] != ':'))))
- return 0;
-
- return len;
+ if (is_curly)
+ /* Count the two curly braces. */
+ return rlen + 2;
+ else
+ return rlen;
}
-
+/* INPUT should be the start of a path e.g DT_RPATH or name e.g.
+ DT_NEEDED. The return value is the number of known DSTs found. We
+ count all known DSTs regardless of __libc_enable_secure; the caller
+ is responsible for enforcing the security of the substitution rules
+ (usually _dl_dst_substitute). */
size_t
-_dl_dst_count (const char *name, int is_path)
+_dl_dst_count (const char *input)
{
- const char *const start = name;
size_t cnt = 0;
+ input = strchr (input, '$');
+
+ /* Most likely there is no DST. */
+ if (__glibc_likely (input == NULL))
+ return 0;
+
do
{
size_t len;
- /* $ORIGIN is not expanded for SUID/GUID programs (except if it
- is $ORIGIN alone) and it must always appear first in path. */
- ++name;
- if ((len = is_dst (start, name, "ORIGIN", is_path,
- __libc_enable_secure)) != 0
- || (len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0
- || (len = is_dst (start, name, "LIB", is_path, 0)) != 0)
+ ++input;
+ /* All DSTs must follow ELF gABI rules, see is_dst (). */
+ if ((len = is_dst (input, "ORIGIN")) != 0
+ || (len = is_dst (input, "PLATFORM")) != 0
+ || (len = is_dst (input, "LIB")) != 0)
++cnt;
- name = strchr (name + len, '$');
+ /* There may be more than one DST in the input. */
+ input = strchr (input + len, '$');
}
- while (name != NULL);
+ while (input != NULL);
return cnt;
}
-
+/* Process INPUT for DSTs and store in RESULT using the information
+ from link map L to resolve the DSTs. This function only handles one
+ path at a time and does not handle colon-separated path lists (see
+ fillin_rpath ()). Lastly the size of result in bytes should be at
+ least equal to the value returned by DL_DST_REQUIRED. Note that it
+ is possible for a DT_NEEDED, DT_AUXILIARY, and DT_FILTER entries to
+ have colons, but we treat those as literal colons here, not as path
+ list delimeters. */
char *
-_dl_dst_substitute (struct link_map *l, const char *name, char *result,
- int is_path)
+_dl_dst_substitute (struct link_map *l, const char *input, char *result)
{
- const char *const start = name;
-
- /* Now fill the result path. While copying over the string we keep
- 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. */
+ /* Copy character-by-character from input into the working pointer
+ looking for any DSTs. We track the start of input and if we are
+ going to check for trusted paths, all of which are part of $ORIGIN
+ handling in SUID/SGID cases (see below). In some cases, like when
+ a DST cannot be replaced, we may set result to an empty string and
+ return. */
char *wp = result;
- char *last_elem = result;
+ const char *start = input;
bool check_for_trusted = false;
do
{
- if (__glibc_unlikely (*name == '$'))
+ if (__glibc_unlikely (*input == '$'))
{
const char *repl = NULL;
size_t len;
- ++name;
- if ((len = is_dst (start, name, "ORIGIN", is_path,
- __libc_enable_secure)) != 0)
+ ++input;
+ if ((len = is_dst (input, "ORIGIN")) != 0)
{
- repl = l->l_origin;
+ /* For SUID/GUID programs we normally ignore the path with
+ $ORIGIN in DT_RUNPATH, or DT_RPATH. However, there is
+ one exception to this rule, and it is:
+
+ * $ORIGIN appears as the first path element, and is
+ the only string in the path or is immediately
+ followed by a path separator and the rest of the
+ path,
+
+ and ...
+
+ * The path is rooted in a trusted directory.
+
+ This exception allows such programs to reference
+ shared libraries in subdirectories of trusted
+ directories. The use case is one of general
+ organization and deployment flexibility.
+ Trusted directories are usually such paths as "/lib64"
+ or "/usr/lib64", and the usual RPATHs take the form of
+ [$ORIGIN/../$LIB/somedir]. */
+ if (__glibc_unlikely (__libc_enable_secure)
+ && !(input == start + 1
+ && (input[len] == '\0' || input[len] == '/')))
+ repl = (const char *) -1;
+ else
+ repl = l->l_origin;
+
check_for_trusted = (__libc_enable_secure
&& l->l_type == lt_executable);
}
- else if ((len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0)
+ else if ((len = is_dst (input, "PLATFORM")) != 0)
repl = GLRO(dl_platform);
- else if ((len = is_dst (start, name, "LIB", is_path, 0)) != 0)
+ else if ((len = is_dst (input, "LIB")) != 0)
repl = DL_DST_LIB;
if (repl != NULL && repl != (const char *) -1)
{
wp = __stpcpy (wp, repl);
- name += len;
+ input += len;
}
- else if (len > 1)
+ else if (len != 0)
{
- /* We cannot use this path element, the value of the
- replacement is unknown. */
- wp = last_elem;
- 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;
+ /* We found a valid DST that we know about, but we could
+ not find a replacement value for it, therefore we
+ cannot use this path and discard it. */
+ *result = '\0';
+ return result;
}
else
/* No DST we recognize. */
@@ -322,29 +341,26 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result,
}
else
{
- *wp++ = *name++;
- if (is_path && *name == ':')
- {
- /* In SUID/SGID programs, after $ORIGIN expansion the
- normalized path must be rooted in one of the trusted
- directories. */
- if (__glibc_unlikely (check_for_trusted)
- && !is_trusted_path_normalize (last_elem, wp - last_elem))
- wp = last_elem;
- else
- last_elem = wp;
-
- check_for_trusted = false;
- }
+ *wp++ = *input++;
}
}
- while (*name != '\0');
+ while (*input != '\0');
/* In SUID/SGID programs, after $ORIGIN expansion the normalized
- path must be rooted in one of the trusted directories. */
+ path must be rooted in one of the trusted directories. The $LIB
+ and $PLATFORM DST cannot in any way be manipulated by the caller
+ because they are fixed values that are set by the dynamic loader
+ and therefore any paths using just $LIB or $PLATFORM need not be
+ checked for trust, the authors of the binaries themselves are
+ trusted to have designed this correctly. Only $ORIGIN is tested in
+ this way because it may be manipulated in some ways with hard
+ links. */
if (__glibc_unlikely (check_for_trusted)
- && !is_trusted_path_normalize (last_elem, wp - last_elem))
- wp = last_elem;
+ && !is_trusted_path_normalize (result, wp - result))
+ {
+ *result = '\0';
+ return result;
+ }
*wp = '\0';
@@ -352,13 +368,13 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result,
}
-/* Return copy of argument with all recognized dynamic string tokens
- ($ORIGIN and $PLATFORM for now) replaced. On some platforms it
- might not be possible to determine the path from which the object
- belonging to the map is loaded. In this case the path element
- containing $ORIGIN is left out. */
+/* Return a malloc allocated copy of INPUT with all recognized DSTs
+ replaced. On some platforms it might not be possible to determine the
+ path from which the object belonging to the map is loaded. In this
+ case the path containing the DST is left out. On error NULL
+ is returned. */
static char *
-expand_dynamic_string_token (struct link_map *l, const char *s, int is_path)
+expand_dynamic_string_token (struct link_map *l, const char *input)
{
/* 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 no
@@ -368,22 +384,22 @@ expand_dynamic_string_token (struct link_map *l, const char *s, int is_path)
size_t total;
char *result;
- /* Determine the number of DST elements. */
- cnt = DL_DST_COUNT (s, is_path);
+ /* Determine the number of DSTs. */
+ cnt = _dl_dst_count (input);
/* If we do not have to replace anything simply copy the string. */
if (__glibc_likely (cnt == 0))
- return __strdup (s);
+ return __strdup (input);
/* Determine the length of the substituted string. */
- total = DL_DST_REQUIRED (l, s, strlen (s), cnt);
+ total = DL_DST_REQUIRED (l, input, strlen (input), cnt);
/* Allocate the necessary memory. */
result = (char *) malloc (total + 1);
if (result == NULL)
return NULL;
- return _dl_dst_substitute (l, s, result, is_path);
+ return _dl_dst_substitute (l, input, result);
}
@@ -392,7 +408,6 @@ expand_dynamic_string_token (struct link_map *l, const char *s, int is_path)
be freed if the shared object already has this name.
Returns false if the object already had this name. */
static void
-internal_function
add_name_to_object (struct link_map *l, const char *name)
{
struct libname_list *lnp, *lastp;
@@ -428,42 +443,43 @@ static size_t max_dirnamelen;
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,
- struct link_map *l)
+ 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;
+ char *to_free = NULL;
+ size_t len = 0;
- 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'. */
- if (len == 0)
+ /* `strsep' can pass an empty string. */
+ if (*cp != '\0')
{
- static const char curwd[] = "./";
- cp = (char *) curwd;
- }
+ to_free = cp = expand_dynamic_string_token (l, cp);
- /* Remove trailing slashes (except for "/"). */
- while (len > 1 && cp[len - 1] == '/')
- --len;
+ /* expand_dynamic_string_token can return NULL in case of empty
+ path or memory allocation failure. */
+ if (cp == NULL)
+ continue;
- /* Now add one if there is none so far. */
- if (len > 0 && cp[len - 1] != '/')
- cp[len++] = '/';
+ /* Compute the length after dynamic string token expansion and
+ ignore empty paths. */
+ len = strlen (cp);
+ if (len == 0)
+ {
+ free (to_free);
+ continue;
+ }
- /* Make sure we don't use untrusted directories if we run SUID. */
- if (__glibc_unlikely (check_trusted) && !is_trusted_path (cp, len))
- {
- free (to_free);
- continue;
+ /* Remove trailing slashes (except for "/"). */
+ while (len > 1 && cp[len - 1] == '/')
+ --len;
+
+ /* Now add one if there is none so far. */
+ if (len > 0 && cp[len - 1] != '/')
+ cp[len++] = '/';
}
/* See if this directory is already known. */
@@ -536,13 +552,11 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
static bool
-internal_function
decompose_rpath (struct r_search_path_struct *sps,
const char *rpath, struct link_map *l, const char *what)
{
/* Make a copy we can work with. */
const char *where = l->l_name;
- char *copy;
char *cp;
struct r_search_path_elem **result;
size_t nelems;
@@ -581,22 +595,21 @@ decompose_rpath (struct r_search_path_struct *sps,
while (*inhp != '\0');
}
+ /* Ignore empty rpaths. */
+ if (*rpath == '\0')
+ {
+ sps->dirs = (struct r_search_path_elem **) -1;
+ return false;
+ }
+
/* Make a writable copy. */
- copy = __strdup (rpath);
+ char *copy = __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)
@@ -615,12 +628,20 @@ decompose_rpath (struct r_search_path_struct *sps,
_dl_signal_error (ENOMEM, NULL, NULL, errstring);
}
- fillin_rpath (copy, result, ":", 0, what, where, l);
+ fillin_rpath (copy, result, ":", what, where, l);
/* Free the copied RPATH string. `fillin_rpath' make own copies if
necessary. */
free (copy);
+ /* There is no path after expansion. */
+ if (result[0] == NULL)
+ {
+ free (result);
+ sps->dirs = (struct r_search_path_elem **) -1;
+ return false;
+ }
+
sps->dirs = result;
/* The caller will change this value if we haven't used a real malloc. */
sps->malloced = 1;
@@ -656,7 +677,6 @@ cache_rpath (struct link_map *l,
void
-internal_function
_dl_init_paths (const char *llp)
{
size_t idx;
@@ -688,9 +708,8 @@ _dl_init_paths (const char *llp)
+ ncapstr * sizeof (enum r_dir_status))
/ sizeof (struct r_search_path_elem));
- rtld_search_dirs.dirs[0] = (struct r_search_path_elem *)
- malloc ((sizeof (system_dirs) / sizeof (system_dirs[0]))
- * round_size * sizeof (struct r_search_path_elem));
+ rtld_search_dirs.dirs[0] = malloc (nsystem_dirs_len * round_size
+ * sizeof (*rtld_search_dirs.dirs[0]));
if (rtld_search_dirs.dirs[0] == NULL)
{
errstring = N_("cannot create cache for search path");
@@ -776,37 +795,14 @@ _dl_init_paths (const char *llp)
if (llp != NULL && *llp != '\0')
{
- size_t nllp;
- const char *cp = llp;
- char *llp_tmp;
-
-#ifdef SHARED
- /* Expand DSTs. */
- size_t cnt = DL_DST_COUNT (llp, 1);
- if (__glibc_likely (cnt == 0))
- 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
+ char *llp_tmp = strdupa (llp);
/* Decompose the LD_LIBRARY_PATH contents. First determine how many
elements it has. */
- nllp = 1;
- while (*cp)
- {
- if (*cp == ':' || *cp == ';')
- ++nllp;
- ++cp;
- }
+ size_t nllp = 1;
+ for (const char *cp = llp_tmp; *cp != '\0'; ++cp)
+ if (*cp == ':' || *cp == ';')
+ ++nllp;
env_path_list.dirs = (struct r_search_path_elem **)
malloc ((nllp + 1) * sizeof (struct r_search_path_elem *));
@@ -817,8 +813,7 @@ _dl_init_paths (const char *llp)
}
(void) fillin_rpath (llp_tmp, env_path_list.dirs, ":;",
- __libc_enable_secure, "LD_LIBRARY_PATH",
- NULL, l);
+ "LD_LIBRARY_PATH", NULL, l);
if (env_path_list.dirs[0] == NULL)
{
@@ -840,7 +835,7 @@ lose (int code, int fd, const char *name, char *realname, struct link_map *l,
{
/* The file might already be closed. */
if (fd != -1)
- (void) __close (fd);
+ (void) __close_nocancel (fd);
if (l != NULL && l->l_origin != (char *) -1l)
free ((char *) l->l_origin);
free (l);
@@ -899,7 +894,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
{
/* The object is already loaded.
Just bump its reference count and return it. */
- __close (fd);
+ __close_nocancel (fd);
/* If the name is not in the list of names for this object add
it. */
@@ -927,7 +922,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
/* No need to bump the refcount of the real object, ld.so will
never be unloaded. */
- __close (fd);
+ __close_nocancel (fd);
/* Add the map for the mirrored object to the object list. */
_dl_add_to_namespace_list (l, nsid);
@@ -941,7 +936,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
/* We are not supposed to load the object unless it is already
loaded. So return now. */
free (realname);
- __close (fd);
+ __close_nocancel (fd);
return NULL;
}
@@ -960,7 +955,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
if (_dl_zerofd == -1)
{
free (realname);
- __close (fd);
+ __close_nocancel (fd);
_dl_signal_error (errno, NULL, NULL,
N_("cannot open zero fill device"));
}
@@ -1026,7 +1021,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
{
phdr = alloca (maplength);
__lseek (fd, header->e_phoff, SEEK_SET);
- if ((size_t) __libc_read (fd, (void *) phdr, maplength) != maplength)
+ if ((size_t) __read_nocancel (fd, (void *) phdr, maplength) != maplength)
{
errstring = N_("cannot read file data");
goto call_lose_errno;
@@ -1055,8 +1050,14 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
segments are mapped in. We record the addresses it says
verbatim, and later correct for the run-time load address. */
case PT_DYNAMIC:
- l->l_ld = (void *) ph->p_vaddr;
- l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
+ if (ph->p_filesz)
+ {
+ /* Debuginfo only files from "objcopy --only-keep-debug"
+ contain a PT_DYNAMIC segment with p_filesz == 0. Skip
+ such a segment to avoid a crash later. */
+ l->l_ld = (void *) ph->p_vaddr;
+ l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
+ }
break;
case PT_PHDR:
@@ -1135,54 +1136,14 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
}
#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
+ /* We are loading the executable itself when the dynamic
+ linker was executed directly. The setup will happen
+ later. Otherwise, the TLS data structures are already
+ initialized, and we assigned a TLS modid above. */
+ assert (l->l_prev == NULL || (mode & __RTLD_AUDIT) != 0);
+#else
+ assert (false && "TLS not initialized in static application");
#endif
- {
- /* We have not yet loaded libpthread.
- We can do the TLS setup right now! */
-
- void *tcb;
-
- /* The first call allocates TLS bookkeeping data structures.
- Then we allocate the TCB for the initial thread. */
- if (__glibc_unlikely (_dl_tls_setup ())
- || __glibc_unlikely ((tcb = _dl_allocate_tls (NULL)) == NULL))
- {
- errval = ENOMEM;
- errstring = N_("\
-cannot allocate TLS data structures for initial thread");
- goto call_lose;
- }
-
- /* Now we install the TCB in the thread register. */
- errstring = TLS_INIT_TP (tcb);
- if (__glibc_likely (errstring == NULL))
- {
- /* Now we are all good. */
- l->l_tls_modid = ++GL(dl_tls_max_dtv_idx);
- break;
- }
-
- /* The kernel is too old or somesuch. */
- errval = 0;
- _dl_deallocate_tls (tcb, 1);
- goto call_lose;
- }
-
- /* Uh-oh, the binary expects TLS support but we cannot
- provide it. */
- errval = 0;
- errstring = N_("cannot handle TLS data");
- goto call_lose;
break;
case PT_GNU_STACK:
@@ -1193,6 +1154,14 @@ cannot allocate TLS data structures for initial thread");
l->l_relro_addr = ph->p_vaddr;
l->l_relro_size = ph->p_memsz;
break;
+
+ case PT_NOTE:
+ if (_dl_process_pt_note (l, ph, fd, fbp))
+ {
+ errstring = N_("cannot process note segment");
+ goto call_lose;
+ }
+ break;
}
if (__glibc_unlikely (nloadcmds == 0))
@@ -1280,12 +1249,6 @@ cannot allocate TLS data structures for initial thread");
if (__glibc_unlikely ((stack_flags &~ GL(dl_stack_flags)) & PF_X))
{
- if (__glibc_unlikely (__check_caller (RETURN_ADDRESS (0), allow_ldso) != 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
@@ -1336,7 +1299,7 @@ cannot enable executable stack as shared object requires");
l->l_tls_initimage = (char *) l->l_tls_initimage + l->l_addr;
/* We are done mapping in the file. We no longer need the descriptor. */
- if (__glibc_unlikely (__close (fd) != 0))
+ if (__glibc_unlikely (__close_nocancel (fd) != 0))
{
errstring = N_("cannot close file descriptor");
goto call_lose_errno;
@@ -1551,7 +1514,7 @@ open_verify (const char *name, int fd,
{
/* An audit library changed what we're supposed to open,
so FD no longer matches it. */
- __close (fd);
+ __close_nocancel (fd);
fd = -1;
}
}
@@ -1559,13 +1522,14 @@ open_verify (const char *name, int fd,
if (fd == -1)
/* Open the file. We always open files read-only. */
- fd = __open (name, O_RDONLY | O_CLOEXEC);
+ fd = __open64_nocancel (name, O_RDONLY | O_CLOEXEC);
if (fd != -1)
{
ElfW(Ehdr) *ehdr;
ElfW(Phdr) *phdr, *ph;
ElfW(Word) *abi_note;
+ ElfW(Word) *abi_note_malloced = NULL;
unsigned int osversion;
size_t maplength;
@@ -1577,8 +1541,8 @@ open_verify (const char *name, int fd,
/* Read in the header. */
do
{
- ssize_t retlen = __libc_read (fd, fbp->buf + fbp->len,
- sizeof (fbp->buf) - fbp->len);
+ ssize_t retlen = __read_nocancel (fd, fbp->buf + fbp->len,
+ sizeof (fbp->buf) - fbp->len);
if (retlen <= 0)
break;
fbp->len += retlen;
@@ -1701,7 +1665,8 @@ open_verify (const char *name, int fd,
{
phdr = alloca (maplength);
__lseek (fd, ehdr->e_phoff, SEEK_SET);
- if ((size_t) __libc_read (fd, (void *) phdr, maplength) != maplength)
+ if ((size_t) __read_nocancel (fd, (void *) phdr, maplength)
+ != maplength)
{
read_error:
errval = errno;
@@ -1720,23 +1685,49 @@ open_verify (const char *name, int fd,
if (ph->p_type == PT_NOTE && ph->p_filesz >= 32 && ph->p_align >= 4)
{
ElfW(Addr) size = ph->p_filesz;
+ /* NB: Some PT_NOTE segment may have alignment value of 0
+ or 1. gABI specifies that PT_NOTE segments should be
+ aligned to 4 bytes in 32-bit objects and to 8 bytes in
+ 64-bit objects. As a Linux extension, we also support
+ 4 byte alignment in 64-bit objects. If p_align is less
+ than 4, we treate alignment as 4 bytes since some note
+ segments have 0 or 1 byte alignment. */
+ ElfW(Addr) align = ph->p_align;
+ if (align < 4)
+ align = 4;
+ else if (align != 4 && align != 8)
+ continue;
if (ph->p_offset + size <= (size_t) fbp->len)
abi_note = (void *) (fbp->buf + ph->p_offset);
else
{
- abi_note = alloca (size);
+ /* Note: __libc_use_alloca is not usable here, because
+ thread info may not have been set up yet. */
+ if (size < __MAX_ALLOCA_CUTOFF)
+ abi_note = alloca (size);
+ else
+ {
+ /* There could be multiple PT_NOTEs. */
+ abi_note_malloced = realloc (abi_note_malloced, size);
+ if (abi_note_malloced == NULL)
+ goto read_error;
+
+ abi_note = abi_note_malloced;
+ }
__lseek (fd, ph->p_offset, SEEK_SET);
- if (__libc_read (fd, (void *) abi_note, size) != size)
- goto read_error;
+ if (__read_nocancel (fd, (void *) abi_note, size) != size)
+ {
+ free (abi_note_malloced);
+ 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]);
+ ElfW(Addr) note_size
+ = ELF_NOTE_NEXT_OFFSET (abi_note[0], abi_note[1],
+ align);
if (size - 32 < note_size)
{
@@ -1757,13 +1748,14 @@ open_verify (const char *name, int fd,
|| (GLRO(dl_osversion) && GLRO(dl_osversion) < osversion))
{
close_and_out:
- __close (fd);
+ __close_nocancel (fd);
__set_errno (ENOENT);
fd = -1;
}
break;
}
+ free (abi_note_malloced);
}
return fd;
@@ -1873,7 +1865,7 @@ open_path (const char *name, size_t namelen, int mode,
/* The shared object cannot be tested for being SUID
or this bit is not set. In this case we must not
use this object. */
- __close (fd);
+ __close_nocancel (fd);
fd = -1;
/* We simply ignore the file, signal this by setting
the error value which would have been set by `open'. */
@@ -1894,7 +1886,7 @@ open_path (const char *name, size_t namelen, int mode,
{
/* No memory for the name, we certainly won't be able
to load and link it. */
- __close (fd);
+ __close_nocancel (fd);
return -1;
}
}
@@ -1927,7 +1919,6 @@ open_path (const char *name, size_t namelen, int mode,
/* Map in the shared object file NAME. */
struct link_map *
-internal_function
_dl_map_object (struct link_map *loader, const char *name,
int type, int trace_mode, int mode, Lmid_t nsid)
{
@@ -2168,7 +2159,7 @@ _dl_map_object (struct link_map *loader, const char *name,
{
/* The path may contain dynamic string tokens. */
realname = (loader
- ? expand_dynamic_string_token (loader, name, 0)
+ ? expand_dynamic_string_token (loader, name)
: __strdup (name));
if (realname == NULL)
fd = -1;
@@ -2281,7 +2272,6 @@ add_path (struct add_path_state *p, const struct r_search_path_struct *sps,
}
void
-internal_function
_dl_rtld_di_serinfo (struct link_map *loader, Dl_serinfo *si, bool counting)
{
if (counting)