📄 dl-load.c
字号:
/* Map in a shared object's segments from the file. Copyright (C) 1995,96,97,98,99,2000,2001 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */#include <elf.h>#include <errno.h>#include <fcntl.h>#include <libintl.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <unistd.h>#include <ldsodefs.h>#include <sys/mman.h>#include <sys/param.h>#include <sys/stat.h>#include <sys/types.h>#include "dynamic-link.h"#include <abi-tag.h>#include <dl-osinfo.h>#include <dl-dst.h>/* On some systems, no flag bits are given to specify file mapping. */#ifndef MAP_FILE# define MAP_FILE 0#endif/* The right way to map in the shared library files is MAP_COPY, which makes a virtual copy of the data at the time of the mmap call; this guarantees the mapped pages will be consistent even if the file is 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. */#ifndef MAP_COPY# define MAP_COPY MAP_PRIVATE#endif/* Some systems link their relocatable objects for another base address than 0. We want to know the base address for these such that we can subtract this address from the segment addresses during mapping. This results in a more efficient address space usage. Defaults to zero for almost all systems. */#ifndef MAP_BASE_ADDR# define MAP_BASE_ADDR(l) 0#endif#include <endian.h>#if BYTE_ORDER == BIG_ENDIAN# define byteorder ELFDATA2MSB#elif BYTE_ORDER == LITTLE_ENDIAN# define byteorder ELFDATA2LSB#else# error "Unknown BYTE_ORDER " BYTE_ORDER# define byteorder ELFDATANONE#endif#define STRING(x) __STRING (x)#ifdef MAP_ANON/* The fd is not examined when using MAP_ANON. */# define ANONFD -1#elseint _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_DATAELF_PREFERRED_ADDRESS_DATA;#endif#ifndef ELF_PREFERRED_ADDRESS# define ELF_PREFERRED_ADDRESS(loader, maplength, mapstartpref) (mapstartpref)#endif#ifndef ELF_FIXED_ADDRESS# define ELF_FIXED_ADDRESS(loader, mapstart) ((void) 0)#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 in 64-bit files is 56 bytes long. Each program header entry is again 56 bytes long. I.e., even with a file which has 17 program header entries we only have to read 1kB. And 17 program header entries is plenty, normal files have < 10. 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; char buf[1024];};size_t _dl_pagesize;unsigned int _dl_osversion;int _dl_clktck;extern const char *_dl_platform;extern size_t _dl_platformlen;/* The object to be initialized first. */struct link_map *_dl_initfirst;/* This is the decomposed LD_LIBRARY_PATH search path. */static struct r_search_path_struct env_path_list;/* List of the hardware capabilities we might end up using. */static const struct r_strlenpair *capstr;static size_t ncapstr;static size_t max_capstrlen;/* Get the generated information about the trusted directories. */#include "trusted-dirs.h"static const char system_dirs[] = SYSTEM_DIRS;static const size_t system_dirs_len[] ={ SYSTEM_DIRS_LEN};#define nsystem_dirs_len \ (sizeof (system_dirs_len) / sizeof (system_dirs_len[0]))/* Local version of `strdup' function. */static inline char *local_strdup (const char *s){ size_t len = strlen (s) + 1; void *new = malloc (len); if (new == NULL) return NULL; return (char *) memcpy (new, s, len);}static size_tis_dst (const char *start, const char *name, const char *str, size_t cmplen, int is_path, int secure){ size_t len; if (strncmp (name, str, cmplen) == 0) len = cmplen + 1; else if (strncmp (name, str + 1, cmplen - 2) == 0 && (name[cmplen - 2] == '\0' || name[cmplen - 2] == '/' || (is_path && name[cmplen - 2] == ':'))) len = cmplen - 1; else return 0; if (__builtin_expect (secure, 0) && ((name[len - 1] != '\0' && (!is_path || name[len - 1] != ':')) || (name != start + 1 && (!is_path || name[-2] != ':')))) return 0; return len;}size_t_dl_dst_count (const char *name, int is_path){ const char *const start = name; size_t cnt = 0; do { size_t len = 1; /* $ORIGIN is not expanded for SUID/GUID programs (except if it is $ORIGIN alone) and it must always appear first in path. Note that it is no bug that the string in the second and fourth `strncmp' call is longer than the sequence which is actually tested. */ if ((len = is_dst (start, name + 1, "{ORIGIN}", 8, is_path, 0)) != 0 || ((len = is_dst (start, name + 1, "{PLATFORM}", 10, is_path, 0)) != 0)) ++cnt; name = strchr (name + len, '$'); } while (name != NULL); return cnt;}char *_dl_dst_substitute (struct link_map *l, const char *name, char *result, 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 a DST we copy over the value or (if the value is not available) leave the entire path element out. */ last_elem = wp = result; do { if (__builtin_expect (*name == '$', 0)) { const char *repl = NULL; size_t len = 1; /* Note that it is no bug that the string in the second and fourth `strncmp' call is longer than the sequence which is actually tested. */ if ((len = is_dst (start, name + 1, "{ORIGIN}", 8, is_path, 0)) != 0) repl = l->l_origin; else if ((len = is_dst (start, name + 1, "{PLATFORM}", 10, is_path, 0)) != 0) repl = _dl_platform; if (repl != NULL && repl != (const char *) -1) { wp = strcpy (wp, repl); wp += strlen (repl); name += len; } else if (len > 1) { /* 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; } else /* No DST we recognize. */ *wp++ = *name++; } else { *wp++ = *name++; if (is_path && *name == ':') last_elem = wp; } } while (*name != '\0'); *wp = '\0'; return 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. */static char *expand_dynamic_string_token (struct link_map *l, const char *s){ /* 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 frequently executed operation we are looking here not for performance but rather for code size. */ size_t cnt; size_t total; char *result; /* Determine the number of DST elements. */ cnt = DL_DST_COUNT (s, 1); /* If we do not have to replace anything simply copy the string. */ if (__builtin_expect (cnt, 0) == 0) return local_strdup (s); /* Determine the length of the substituted string. */ total = DL_DST_REQUIRED (l, s, strlen (s), cnt); /* Allocate the necessary memory. */ result = (char *) malloc (total + 1); if (result == NULL) return NULL; return DL_DST_SUBSTITUTE (l, s, result, 1);}/* Add `name' to the list of names for a particular shared object. `name' is expected to have been allocated with malloc and will be freed if the shared object already has this name. Returns false if the object already had this name. */static voidinternal_functionadd_name_to_object (struct link_map *l, const char *name){ struct libname_list *lnp, *lastp; struct libname_list *newname; size_t name_len; lastp = NULL; for (lnp = l->l_libname; lnp != NULL; lastp = lnp, lnp = lnp->next) if (strcmp (name, lnp->name) == 0) return; name_len = strlen (name) + 1; newname = (struct libname_list *) malloc (sizeof *newname + name_len); if (newname == NULL) { /* No more memory. */ _dl_signal_error (ENOMEM, name, NULL, N_("cannot allocate name record")); return; } /* The object should have a libname set from _dl_new_object. */ assert (lastp != NULL); newname->name = memcpy (newname + 1, name, name_len); newname->next = NULL; newname->dont_free = 0; lastp->next = newname;}/* All known directories in sorted order. */struct r_search_path_elem *_dl_all_dirs;/* All directories after startup. */struct r_search_path_elem *_dl_init_all_dirs;/* Standard search directories. */static struct r_search_path_struct rtld_search_dirs;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, int check_trusted, const char *what, const char *where){ char *cp; size_t nelems = 0; printf("In fillin_rpath\n"); while ((cp = strsep (&rpath, sep)) != NULL) { struct r_search_path_elem *dirp; size_t len = strlen (cp); /* `strsep' can pass an empty string. This has to be interpreted as `use the current directory'. */ if (len == 0) { static const char curwd[] = "./"; cp = (char *) curwd; } /* 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++] = '/'; /* Make sure we don't use untrusted directories if we run SUID. */ if (__builtin_expect (check_trusted, 0)) { 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; } /* See if this directory is already known. */ for (dirp = _dl_all_dirs; dirp != NULL; dirp = dirp->next) if (dirp->dirnamelen == len && memcmp (cp, dirp->dirname, len) == 0) break; if (dirp != NULL) { /* It is available, see whether it's on our own list. */ size_t cnt; for (cnt = 0; cnt < nelems; ++cnt) if (result[cnt] == dirp) break; if (cnt == nelems) result[nelems++] = dirp; } else { size_t cnt; enum r_dir_status init_val; size_t where_len = where ? strlen (where) + 1 : 0; /* It's a new directory. Create an entry and add it. */ dirp = (struct r_search_path_elem *) malloc (sizeof (*dirp) + ncapstr * sizeof (enum r_dir_status) + where_len + len + 1); if (dirp == NULL) _dl_signal_error (ENOMEM, NULL, NULL, N_("cannot create cache for search path")); dirp->dirname = ((char *) dirp + sizeof (*dirp) + ncapstr * sizeof (enum r_dir_status)); *((char *) (memcpy ((char *) dirp->dirname, cp, len) + len)) = '\0'; dirp->dirnamelen = len; if (len > max_dirnamelen) max_dirnamelen = len; /* We have to make sure all the relative directories are never ignored. The current directory might change and all our saved information would be void. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -