diff options
Diffstat (limited to 'elf/dl-load.c')
-rw-r--r-- | elf/dl-load.c | 465 |
1 files changed, 374 insertions, 91 deletions
diff --git a/elf/dl-load.c b/elf/dl-load.c index 87859219f1..1301e73996 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1,4 +1,4 @@ -/* _dl_map_object -- Map in a shared object's segments from the file. +/* Map in a shared object's segments from the file. Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -17,14 +17,15 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <errno.h> +#include <fcntl.h> #include <link.h> -#include <sys/types.h> -#include <sys/mman.h> +#include <stdlib.h> #include <string.h> -#include <fcntl.h> #include <unistd.h> -#include <stdlib.h> -#include <errno.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> #include "dynamic-link.h" @@ -90,6 +91,8 @@ ELF_PREFERRED_ADDRESS_DATA; size_t _dl_pagesize; +extern const char *_dl_platform; +extern size_t _dl_platformlen; /* Local version of `strdup' function. */ static inline char * @@ -105,6 +108,292 @@ local_strdup (const char *s) } +/* Implement cache for search path lookup. */ +#if 0 +/* This is how generated should look like. I'll remove this once I'm + sure everything works correctly. */ +static struct r_search_path_elem rtld_search_dir1 = + { "/lib/", 5, unknown, 0, unknown, NULL }; +static struct r_search_path_elem rtld_search_dir2 = + { "/usr/lib/", 9, unknown, 0, unknown, &r ld_search_dir1 }; + +static struct r_search_path_elem *rtld_search_dirs[] = +{ + &rtld_search_dir1, + &rtld_search_dir2, + NULL +}; + +static struct r_search_path_elem *all_dirs = &rtld_search_dir2; +#else +# include "rtldtbl.h" +#endif + +static size_t max_dirnamelen; + +static inline struct r_search_path_elem ** +fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep, + const char **trusted) +{ + char *cp; + size_t nelems = 0; + + while ((cp = __strsep (&rpath, sep)) != NULL) + { + struct r_search_path_elem *dirp; + size_t len = strlen (cp); + /* Remove trailing slashes. */ + while (len > 1 && cp[len - 1] == '/') + --len; + + /* Make sure we don't use untrusted directories if we run SUID. */ + if (trusted != NULL) + { + const char **trun = trusted; + + /* All trusted directory must be complete name. */ + if (cp[0] != '/') + continue; + + while (*trun != NULL + && (memcmp (*trun, cp, len) != 0 || (*trun)[len] != '\0')) + ++trun; + + if (*trun == NULL) + /* It's no trusted directory, skip it. */ + continue; + } + + /* Now add one. */ + if (len > 0) + cp[len++] = '/'; + + /* See if this directory is already known. */ + for (dirp = all_dirs; dirp != NULL; dirp = dirp->next) + if (dirp->dirnamelen == len && strcmp (cp, dirp->dirname) == 0) + break; + + if (dirp != NULL) + { + /* It is available, see whether it's in our own list. */ + size_t cnt; + for (cnt = 0; cnt < nelems; ++cnt) + if (result[cnt] == dirp) + break; + + if (cnt == nelems) + result[nelems++] = dirp; + } + else + { + /* It's a new directory. Create an entry and add it. */ + dirp = (struct r_search_path_elem *) malloc (sizeof (*dirp)); + if (dirp == NULL) + _dl_signal_error (ENOMEM, NULL, + "cannot create cache for search path"); + + dirp->dirnamelen = len; + dirp->dirstatus = unknown; + + /* Add the name of the machine dependent directory if a machine + is defined. */ + if (_dl_platform != NULL) + { + char *tmp; + + dirp->machdirnamelen = len + _dl_platformlen + 1; + tmp = (char *) malloc (len + _dl_platformlen + 2); + if (tmp == NULL) + _dl_signal_error (ENOMEM, NULL, + "cannot create cache for search path"); + memcpy (tmp, cp, len); + memcpy (tmp + len, _dl_platform, _dl_platformlen); + tmp[len + _dl_platformlen] = '/'; + tmp[len + _dl_platformlen + 1] = '\0'; + + dirp->dirname = tmp; + dirp->machdirstatus = unknown; + + if (max_dirnamelen < dirp->machdirnamelen) + max_dirnamelen = dirp->machdirnamelen; + } + else + { + char *tmp; + + dirp->machdirnamelen = len; + dirp->machdirstatus = nonexisting; + + tmp = (char *) malloc (len + 1); + if (tmp == NULL) + _dl_signal_error (ENOMEM, NULL, + "cannot create cache for search path"); + memcpy (tmp, cp, len); + tmp[len] = '\0'; + + if (max_dirnamelen < dirp->dirnamelen) + max_dirnamelen = dirp->dirnamelen; + + dirp->dirname = tmp; + } + + dirp->next = all_dirs; + all_dirs = dirp; + + /* Put it in the result array. */ + result[nelems++] = dirp; + } + } + + /* Terminate the array. */ + result[nelems] = NULL; + + return result; +} + + +static struct r_search_path_elem ** +decompose_rpath (const char *rpath, size_t additional_room) +{ + /* Make a copy we can work with. */ + char *copy = strdupa (rpath); + char *cp; + struct r_search_path_elem **result; + /* First count the number of necessary elements in the result array. */ + size_t nelems = 0; + + for (cp = copy; *cp != '\0'; ++cp) + if (*cp == ':') + ++nelems; + + /* Allocate room for the result. NELEMS + 1 + ADDITIONAL_ROOM is an upper + limit for the number of necessary entries. */ + result = (struct r_search_path_elem **) malloc ((nelems + 1 + + additional_room + 1) + * sizeof (*result)); + if (result == NULL) + _dl_signal_error (ENOMEM, NULL, "cannot create cache for search path"); + + return fillin_rpath (copy, result, ":", NULL); +} + + +void +_dl_init_paths (void) +{ + struct r_search_path_elem **pelem; + + /* We have in `search_path' the information about the RPATH of the + dynamic loader. Now fill in the information about the applications + RPATH and the directories addressed by the LD_LIBRARY_PATH environment + variable. */ + struct link_map *l; + + /* First determine how many elements the LD_LIBRARY_PATH contents has. */ + const char *llp = getenv ("LD_LIBRARY_PATH"); + size_t nllp; + + if (llp != NULL && *llp != '\0') + { + /* Simply count the number of colons. */ + const char *cp = llp; + nllp = 1; + while (*cp) + if (*cp++ == ':') + ++nllp; + } + else + nllp = 0; + + l = _dl_loaded; + if (l && l->l_type != lt_loaded && l->l_info[DT_RPATH]) + { + /* Allocate room for the search path and fill in information from + RPATH. */ + l->l_rpath_dirs = + decompose_rpath ((const char *) (l->l_addr + + l->l_info[DT_STRTAB]->d_un.d_ptr + + l->l_info[DT_RPATH]->d_un.d_val), + nllp); + } + else + { + /* If we have no LD_LIBRARY_PATH and no RPATH we must tell this + somehow to prevent we look this up again and again. */ + if (nllp == 0) + l->l_rpath_dirs = (struct r_search_path_elem **) -1l; + else + { + l->l_rpath_dirs = + (struct r_search_path_elem **) malloc ((nllp + 1) + * sizeof (*l->l_rpath_dirs)); + if (l->l_rpath_dirs == NULL) + _dl_signal_error (ENOMEM, NULL, + "cannot create cache for search path"); + l->l_rpath_dirs[0] = NULL; + } + } + + if (nllp > 0) + { + static const char *trusted_dirs[] = + { +#include "trusted-dirs.h" + NULL + }; + char *copy = strdupa (llp); + + /* Decompose the LD_LIBRARY_PATH and fill in the result. + First search for the next place to enter elements. */ + struct r_search_path_elem **result = l->l_rpath_dirs; + while (*result != NULL) + ++result; + + /* We need to take care that the LD_LIBRARY_PATH environement + variable can contain a semicolon. */ + (void) fillin_rpath (copy, result, ":;", + __libc_enable_secure ? trusted_dirs : NULL); + } + + /* Now set up the rest of the rtld_search_dirs. */ + for (pelem = rtld_search_dirs; *pelem != NULL; ++pelem) + { + struct r_search_path_elem *relem = *pelem; + + if (_dl_platform != NULL) + { + char *tmp; + + relem->machdirnamelen = relem->dirnamelen + _dl_platformlen + 1; + tmp = (char *) malloc (relem->machdirnamelen + 1); + if (tmp == NULL) + _dl_signal_error (ENOMEM, NULL, + "cannot create cache for search path"); + + memcpy (tmp, relem->dirname, relem->dirnamelen); + memcpy (tmp + relem->dirnamelen, _dl_platform, _dl_platformlen); + tmp[relem->dirnamelen + _dl_platformlen] = '/'; + tmp[relem->dirnamelen + _dl_platformlen + 1] = '\0'; + + relem->dirname = tmp; + + relem->machdirstatus = unknown; + + if (max_dirnamelen < relem->machdirnamelen) + max_dirnamelen = relem->machdirnamelen; + } + else + { + relem->machdirnamelen = relem->dirnamelen; + relem->machdirstatus = nonexisting; + + if (max_dirnamelen < relem->dirnamelen) + max_dirnamelen = relem->dirnamelen; + } + } +} + + /* Map in the shared object NAME, actually located in REALNAME, and already opened on FD. */ @@ -131,7 +420,7 @@ _dl_map_object_from_fd (char *name, int fd, char *realname, l->l_next->l_prev = l->l_prev; free (l); } - free (name); + free (name); /* XXX Can this be correct? --drepper */ free (realname); _dl_signal_error (code, name, msg); } @@ -207,9 +496,6 @@ _dl_map_object_from_fd (char *name, int fd, char *realname, return l; } - if (_dl_pagesize == 0) - _dl_pagesize = __getpagesize (); - /* Map in the first page to read the header. */ header = map (0, sizeof *header); @@ -458,83 +744,87 @@ _dl_map_object_from_fd (char *name, int fd, char *realname, return l; } -/* Try to open NAME in one of the directories in DIRPATH. +/* Try to open NAME in one of the directories in DIRS. Return the fd, or -1. If successful, fill in *REALNAME with the malloc'd full directory name. */ static int open_path (const char *name, size_t namelen, - const char *dirpath, - char **realname, - const char *trusted_dirs[]) + struct r_search_path_elem **dirs, + char **realname) { char *buf; - const char *p; - int fd; + int fd = -1; - p = dirpath; - if (p == NULL || *p == '\0') + if (dirs == NULL || *dirs == NULL) { __set_errno (ENOENT); return -1; } - buf = __alloca (strlen (dirpath) + 1 + namelen); + buf = __alloca (max_dirnamelen + namelen); do { - size_t buflen; - size_t this_len; + struct r_search_path_elem *this_dir = *dirs; + size_t buflen = 0; - dirpath = p; - p = strpbrk (dirpath, ":;"); - if (p == NULL) - p = strchr (dirpath, '\0'); - - this_len = p - dirpath; - - /* When we run a setuid program we do not accept any directory. */ - if (__libc_enable_secure) + if (this_dir->machdirstatus != nonexisting) { - /* All trusted directory must be complete name. */ - if (dirpath[0] != '/') - continue; + /* Construct the pathname to try. */ + (void) memcpy (buf, this_dir->dirname, this_dir->machdirnamelen); + (void) memcpy (buf + this_dir->machdirnamelen, name, namelen); + buflen = this_dir->machdirnamelen + namelen; + + fd = __open (buf, O_RDONLY); + if (this_dir->machdirstatus == unknown) + if (fd != -1) + this_dir->machdirstatus = existing; + else + { + /* We failed to open machine dependent library. Let's + test whether there is any directory at all. */ + struct stat st; - /* If we got a list of trusted directories only accept one - of these. */ - if (trusted_dirs != NULL) - { - const char **trust = trusted_dirs; + buf[this_dir->machdirnamelen - 1] = '\0'; - while (*trust != NULL) - if (memcmp (dirpath, *trust, this_len) == 0 - && (*trust)[this_len] == '\0') - break; + if (stat (buf, &st) != 0 || ! S_ISDIR (st.st_mode)) + /* The directory does not exist ot it is no directory. */ + this_dir->machdirstatus = nonexisting; else - ++trust; - - /* If directory is not trusted, ignore this directory. */ - if (*trust == NULL) - continue; - } + this_dir->machdirstatus = existing; + } } - if (this_len == 0) - { - /* Two adjacent colons, or a colon at the beginning or the end of - the path means to search the current directory. */ - (void) memcpy (buf, name, namelen); - buflen = namelen; - } - else + if (fd == -1 && this_dir->dirstatus != nonexisting) { /* Construct the pathname to try. */ - (void) memcpy (buf, dirpath, this_len); - buf[this_len] = '/'; - (void) memcpy (&buf[this_len + 1], name, namelen); - buflen = this_len + 1 + namelen; + (void) memcpy (buf, this_dir->dirname, this_dir->dirnamelen); + (void) memcpy (buf + this_dir->dirnamelen, name, namelen); + buflen = this_dir->dirnamelen + namelen; + + fd = __open (buf, O_RDONLY); + if (this_dir->dirstatus == unknown) + if (fd != -1) + this_dir->dirstatus = existing; + else + /* We failed to open library. Let's test whether there + is any directory at all. */ + if (this_dir->dirnamelen <= 1) + this_dir->dirstatus = existing; + else + { + struct stat st; + + buf[this_dir->dirnamelen - 1] = '\0'; + + if (stat (buf, &st) != 0 || ! S_ISDIR (st.st_mode)) + /* The directory does not exist ot it is no directory. */ + this_dir->dirstatus = nonexisting; + else + this_dir->dirstatus = existing; + } } - fd = __open (buf, O_RDONLY); if (fd != -1) { *realname = malloc (buflen); @@ -555,7 +845,7 @@ open_path (const char *name, size_t namelen, /* The file exists and is readable, but something went wrong. */ return -1; } - while (*p++ != '\0'); + while (*++dirs != NULL); return -1; } @@ -593,39 +883,34 @@ _dl_map_object (struct link_map *loader, const char *name, int type, size_t namelen = strlen (name) + 1; - inline void trypath (const char *dirpath, const char *trusted[]) - { - fd = open_path (name, namelen, dirpath, &realname, trusted); - } - fd = -1; /* 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) if (l && l->l_info[DT_RPATH]) - trypath ((const char *) (l->l_addr + - l->l_info[DT_STRTAB]->d_un.d_ptr + - l->l_info[DT_RPATH]->d_un.d_val), NULL); - /* If dynamically linked, try the DT_RPATH of the executable itself. */ - l = _dl_loaded; - if (fd == -1 && l && l->l_type != lt_loaded && l->l_info[DT_RPATH]) - trypath ((const char *) (l->l_addr + - l->l_info[DT_STRTAB]->d_un.d_ptr + - l->l_info[DT_RPATH]->d_un.d_val), NULL); - /* Try an environment variable (unless setuid). */ - if (fd == -1) - { - static const char *trusted_dirs[] = { -#include "trusted-dirs.h" - NULL - }; - const char *ld_library_path = getenv ("LD_LIBRARY_PATH"); + /* Make sure the cache information is available. */ + if (l->l_rpath_dirs == NULL) + { + size_t ptrval = (l->l_addr + + l->l_info[DT_STRTAB]->d_un.d_ptr + + l->l_info[DT_RPATH]->d_un.d_val); + l->l_rpath_dirs = + decompose_rpath ((const char *) ptrval, 0); + } + + if (l->l_rpath_dirs != (struct r_search_path_elem **) -1l) + fd = open_path (name, namelen, l->l_rpath_dirs, &realname); + } + + /* If dynamically linked, try the DT_RPATH of the executable itself + and the LD_LIBRARY_PATH environment variable. */ + l = _dl_loaded; + if (fd == -1 && l && l->l_type != lt_loaded + && l->l_rpath_dirs != (struct r_search_path_elem **) -1l) + fd = open_path (name, namelen, l->l_rpath_dirs, &realname); - if (ld_library_path != NULL && *ld_library_path != '\0') - trypath (ld_library_path, trusted_dirs); - } if (fd == -1) { /* Check the list of libraries in the file /etc/ld.so.cache, @@ -646,12 +931,10 @@ _dl_map_object (struct link_map *loader, const char *name, int type, } } } + /* Finally, try the default path. */ if (fd == -1) - { - extern const char *_dl_rpath; /* Set in rtld.c. */ - trypath (_dl_rpath, NULL); - } + fd = open_path (name, namelen, rtld_search_dirs, &realname); } else { |