diff options
Diffstat (limited to 'elf/dl-load.c')
-rw-r--r-- | elf/dl-load.c | 602 |
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) |