elf_object_t * _dl_lookup_object(const char *name) { elf_object_t *object; object = _dl_objects; while (object) { if (_dl_strcmp(name, object->load_name) == 0) return(object); object = object->next; } return(0); }
struct elf_resolve *_dl_load_shared_library(unsigned int rflags, struct dyn_elf **rpnt, struct elf_resolve *tpnt, char *full_libname, int attribute_unused trace_loaded_objects) { char *pnt; struct elf_resolve *tpnt1; char *libname; _dl_internal_error_number = 0; libname = full_libname; /* quick hack to ensure mylibname buffer doesn't overflow. don't allow full_libname or any directory to be longer than 1024. */ if (_dl_strlen(full_libname) > 1024) goto goof; /* Skip over any initial initial './' and '/' stuff to * get the short form libname with no path garbage */ pnt = _dl_strrchr(libname, '/'); if (pnt) { libname = pnt + 1; } _dl_if_debug_dprint("\tfind library='%s'; searching\n", libname); /* If the filename has any '/', try it straight and leave it at that. For IBCS2 compatibility under linux, we substitute the string /usr/i486-sysv4/lib for /usr/lib in library names. */ if (libname != full_libname) { _dl_if_debug_dprint("\ttrying file='%s'\n", full_libname); tpnt1 = _dl_load_elf_shared_library(rflags, rpnt, full_libname); if (tpnt1) { return tpnt1; } } /* * The ABI specifies that RPATH is searched before LD_LIBRARY_PATH or * the default path of /usr/lib. Check in rpath directories. */ #ifdef __LDSO_RUNPATH__ pnt = (tpnt ? (char *) tpnt->dynamic_info[DT_RPATH] : NULL); if (pnt) { pnt += (unsigned long) tpnt->dynamic_info[DT_STRTAB]; _dl_if_debug_dprint("\tsearching RPATH='%s'\n", pnt); if ((tpnt1 = search_for_named_library(libname, rflags, pnt, rpnt, tpnt->libname)) != NULL) return tpnt1; } #endif #ifdef __LDSO_LD_LIBRARY_PATH__ /* Check in LD_{ELF_}LIBRARY_PATH, if specified and allowed */ if (_dl_library_path) { _dl_if_debug_dprint("\tsearching LD_LIBRARY_PATH='%s'\n", _dl_library_path); if ((tpnt1 = search_for_named_library(libname, rflags, _dl_library_path, rpnt, NULL)) != NULL) { return tpnt1; } } #endif /* * The ABI specifies that RUNPATH is searched after LD_LIBRARY_PATH. */ #ifdef __LDSO_RUNPATH__ pnt = (tpnt ? (char *)tpnt->dynamic_info[DT_RUNPATH] : NULL); if (pnt) { pnt += (unsigned long) tpnt->dynamic_info[DT_STRTAB]; _dl_if_debug_dprint("\tsearching RUNPATH='%s'\n", pnt); if ((tpnt1 = search_for_named_library(libname, rflags, pnt, rpnt, NULL)) != NULL) return tpnt1; } #ifdef __LDSO_RUNPATH_OF_EXECUTABLE__ /* * Try the DT_RPATH of the executable itself. */ pnt = (char *) _dl_loaded_modules->dynamic_info[DT_RPATH]; if (pnt) { pnt += (unsigned long) _dl_loaded_modules->dynamic_info[DT_STRTAB]; _dl_if_debug_dprint("\tsearching exe's RPATH='%s'\n", pnt); if ((tpnt1 = search_for_named_library(libname, rflags, pnt, rpnt, NULL)) != NULL) return tpnt1; } #endif #endif /* * Where should the cache be searched? There is no such concept in the * ABI, so we have some flexibility here. For now, search it before * the hard coded paths that follow (i.e before /lib and /usr/lib). */ #ifdef __LDSO_CACHE_SUPPORT__ if (_dl_cache_addr != NULL && _dl_cache_addr != MAP_FAILED) { int i; header_t *header = (header_t *) _dl_cache_addr; libentry_t *libent = (libentry_t *) & header[1]; char *strs = (char *) &libent[header->nlibs]; _dl_if_debug_dprint("\tsearching cache='%s'\n", LDSO_CACHE); for (i = 0; i < header->nlibs; i++) { if ((libent[i].flags == LIB_ELF || libent[i].flags == LIB_ELF_LIBC0 || libent[i].flags == LIB_ELF_LIBC5) && _dl_strcmp(libname, strs + libent[i].sooffset) == 0 && (tpnt1 = _dl_load_elf_shared_library(rflags, rpnt, strs + libent[i].liboffset)) ) { return tpnt1; } } } #endif #ifdef LDSO_MULTILIB_DIR /* If multilib directory is selected, search it before falling back to standard lib directories. */ _dl_if_debug_dprint("\tsearching multilib lib path list\n"); tpnt1 = search_for_named_library(libname, rflags, UCLIBC_RUNTIME_PREFIX LDSO_MULTILIB_DIR ":" UCLIBC_RUNTIME_PREFIX "usr/" LDSO_MULTILIB_DIR, rpnt, NULL); if (tpnt1 != NULL) return tpnt1; #endif #if defined SHARED && defined __LDSO_SEARCH_INTERP_PATH__ /* Look for libraries wherever the shared library loader * was installed */ _dl_if_debug_dprint("\tsearching ldso dir='%s'\n", _dl_ldsopath); tpnt1 = search_for_named_library(libname, rflags, _dl_ldsopath, rpnt, NULL); if (tpnt1 != NULL) return tpnt1; #endif /* Lastly, search the standard list of paths for the library. This list must exactly match the list in uClibc/ldso/util/ldd.c */ _dl_if_debug_dprint("\tsearching full lib path list\n"); tpnt1 = search_for_named_library(libname, rflags, UCLIBC_RUNTIME_PREFIX "lib:" UCLIBC_RUNTIME_PREFIX "usr/lib" #ifndef __LDSO_CACHE_SUPPORT__ ":" UCLIBC_RUNTIME_PREFIX "usr/X11R6/lib" #endif , rpnt, NULL); if (tpnt1 != NULL) return tpnt1; #ifdef __LDSO_RUNPATH_OF_EXECUTABLE__ /* Very last resort, try the executable's DT_RUNPATH and DT_RPATH */ /* http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#shobj_dependencies * The set of directories specified by a given DT_RUNPATH entry is * used to find only the immediate dependencies of the executable or * shared object containing the DT_RUNPATH entry. That is, it is * used only for those dependencies contained in the DT_NEEDED * entries of the dynamic structure containing the DT_RUNPATH entry, * itself. One object's DT_RUNPATH entry does not affect the search * for any other object's dependencies. * * glibc (around 2.19) violates this and the usual suspects are * abusing this bug^Wrelaxed, user-friendly behaviour. */ pnt = (char *) _dl_loaded_modules->dynamic_info[DT_RUNPATH]; if (pnt) { pnt += (unsigned long) _dl_loaded_modules->dynamic_info[DT_STRTAB]; _dl_if_debug_dprint("\tsearching exe's RUNPATH='%s'\n", pnt); if ((tpnt1 = search_for_named_library(libname, rflags, pnt, rpnt, NULL)) != NULL) return tpnt1; } pnt = (char *) _dl_loaded_modules->dynamic_info[DT_RPATH]; if (pnt) { pnt += (unsigned long) _dl_loaded_modules->dynamic_info[DT_STRTAB]; _dl_if_debug_dprint("\tsearching exe's RPATH='%s'\n", pnt); if ((tpnt1 = search_for_named_library(libname, rflags, pnt, rpnt, NULL)) != NULL) return tpnt1; } #endif goof: /* Well, we shot our wad on that one. All we can do now is punt */ if (_dl_internal_error_number) _dl_error_number = _dl_internal_error_number; else _dl_error_number = LD_ERROR_NOFILE; _dl_if_debug_dprint("Bummer: could not find '%s'!\n", libname); return NULL; }
struct elf_resolve *_dl_load_shared_library(int secure, struct dyn_elf **rpnt, struct elf_resolve *tpnt, char *full_libname, int attribute_unused trace_loaded_objects) { char *pnt; struct elf_resolve *tpnt1; char *libname; _dl_internal_error_number = 0; libname = full_libname; /* quick hack to ensure mylibname buffer doesn't overflow. don't allow full_libname or any directory to be longer than 1024. */ if (_dl_strlen(full_libname) > 1024) goto goof; /* Skip over any initial initial './' and '/' stuff to * get the short form libname with no path garbage */ pnt = _dl_strrchr(libname, '/'); if (pnt) { libname = pnt + 1; } _dl_if_debug_dprint("\tfind library='%s'; searching\n", libname); /* If the filename has any '/', try it straight and leave it at that. For IBCS2 compatibility under linux, we substitute the string /usr/i486-sysv4/lib for /usr/lib in library names. */ if (libname != full_libname) { _dl_if_debug_dprint("\ttrying file='%s'\n", full_libname); tpnt1 = _dl_load_elf_shared_library(secure, rpnt, full_libname); if (tpnt1) { return tpnt1; } } /* * The ABI specifies that RPATH is searched before LD_LIBRARY_PATH or * the default path of /usr/lib. Check in rpath directories. */ #ifdef __LDSO_RUNPATH__ pnt = (tpnt ? (char *) tpnt->dynamic_info[DT_RPATH] : NULL); if (pnt) { pnt += (unsigned long) tpnt->dynamic_info[DT_STRTAB]; _dl_if_debug_dprint("\tsearching RPATH='%s'\n", pnt); if ((tpnt1 = search_for_named_library(libname, secure, pnt, rpnt)) != NULL) return tpnt1; } #endif /* Check in LD_{ELF_}LIBRARY_PATH, if specified and allowed */ if (_dl_library_path) { _dl_if_debug_dprint("\tsearching LD_LIBRARY_PATH='%s'\n", _dl_library_path); if ((tpnt1 = search_for_named_library(libname, secure, _dl_library_path, rpnt)) != NULL) { return tpnt1; } } /* * The ABI specifies that RUNPATH is searched after LD_LIBRARY_PATH. */ #ifdef __LDSO_RUNPATH__ pnt = (tpnt ? (char *)tpnt->dynamic_info[DT_RUNPATH] : NULL); if (pnt) { pnt += (unsigned long) tpnt->dynamic_info[DT_STRTAB]; _dl_if_debug_dprint("\tsearching RUNPATH='%s'\n", pnt); if ((tpnt1 = search_for_named_library(libname, secure, pnt, rpnt)) != NULL) return tpnt1; } #endif /* * Where should the cache be searched? There is no such concept in the * ABI, so we have some flexibility here. For now, search it before * the hard coded paths that follow (i.e before /lib and /usr/lib). */ #ifdef __LDSO_CACHE_SUPPORT__ if (_dl_cache_addr != NULL && _dl_cache_addr != MAP_FAILED) { int i; header_t *header = (header_t *) _dl_cache_addr; libentry_t *libent = (libentry_t *) & header[1]; char *strs = (char *) &libent[header->nlibs]; _dl_if_debug_dprint("\tsearching cache='%s'\n", LDSO_CACHE); for (i = 0; i < header->nlibs; i++) { if ((libent[i].flags == LIB_ELF || libent[i].flags == LIB_ELF_LIBC0 || libent[i].flags == LIB_ELF_LIBC5) && _dl_strcmp(libname, strs + libent[i].sooffset) == 0 && (tpnt1 = _dl_load_elf_shared_library(secure, rpnt, strs + libent[i].liboffset)) ) { return tpnt1; } } } #endif /* Look for libraries wherever the shared library loader * was installed */ _dl_if_debug_dprint("\tsearching ldso dir='%s'\n", _dl_ldsopath); tpnt1 = search_for_named_library(libname, secure, _dl_ldsopath, rpnt); if (tpnt1 != NULL) return tpnt1; /* Lastly, search the standard list of paths for the library. This list must exactly match the list in uClibc/ldso/util/ldd.c */ _dl_if_debug_dprint("\tsearching full lib path list\n"); tpnt1 = search_for_named_library(libname, secure, UCLIBC_RUNTIME_PREFIX "lib:" UCLIBC_RUNTIME_PREFIX "usr/lib" #ifndef __LDSO_CACHE_SUPPORT__ ":" UCLIBC_RUNTIME_PREFIX "usr/X11R6/lib" #endif , rpnt); if (tpnt1 != NULL) return tpnt1; goof: /* Well, we shot our wad on that one. All we can do now is punt */ if (_dl_internal_error_number) _dl_error_number = _dl_internal_error_number; else _dl_error_number = LD_ERROR_NOFILE; _dl_if_debug_dprint("Bummer: could not find '%s'!\n", libname); return NULL; }
/* * char *dl_realpath(const char *path, char resolved[PATH_MAX]); * * Find the real name of path, by removing all ".", ".." and symlink * components. Returns (resolved) on success, or (NULL) on failure, * in which case the path which caused trouble is left in (resolved). */ char * _dl_realpath(const char *path, char *resolved) { struct stat sb; const char *p, *s; char *q; size_t left_len, resolved_len; unsigned symlinks; int slen, mem_allocated, ret; char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; if (path[0] == '\0') { return (NULL); } if (resolved == NULL) { resolved = _dl_malloc(PATH_MAX); if (resolved == NULL) return (NULL); mem_allocated = 1; } else mem_allocated = 0; symlinks = 0; if (path[0] == '/') { resolved[0] = '/'; resolved[1] = '\0'; if (path[1] == '\0') return (resolved); resolved_len = 1; left_len = _dl_strlcpy(left, path + 1, sizeof(left)); } else { if (_dl_getcwd(resolved, PATH_MAX) <= 0) { if (mem_allocated) _dl_free(resolved); else _dl_strlcpy(resolved, ".", PATH_MAX); return (NULL); } resolved_len = _dl_strlen(resolved); left_len = _dl_strlcpy(left, path, sizeof(left)); } if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { goto err; } /* * Iterate over path components in `left'. */ while (left_len != 0) { /* * Extract the next path component and adjust `left' * and its length. */ p = _dl_strchr(left, '/'); s = p ? p : left + left_len; if (s - left >= sizeof(next_token)) { goto err; } _dl_bcopy(left, next_token, s - left); next_token[s - left] = '\0'; left_len -= s - left; if (p != NULL) _dl_bcopy(s + 1, left, left_len + 1); if (resolved[resolved_len - 1] != '/') { if (resolved_len + 1 >= PATH_MAX) { goto err; } resolved[resolved_len++] = '/'; resolved[resolved_len] = '\0'; } if (next_token[0] == '\0') continue; else if (_dl_strcmp(next_token, ".") == 0) continue; else if (_dl_strcmp(next_token, "..") == 0) { /* * Strip the last path component except when we have * single "/" */ if (resolved_len > 1) { resolved[resolved_len - 1] = '\0'; q = _dl_strrchr(resolved, '/') + 1; *q = '\0'; resolved_len = q - resolved; } continue; } /* * Append the next path component and lstat() it. If * lstat() fails we still can return successfully if * there are no more path components left. */ resolved_len = _dl_strlcat(resolved, next_token, PATH_MAX); if (resolved_len >= PATH_MAX) { goto err; } if ((ret = _dl_lstat(resolved, &sb)) != 0) { if (ret == ENOENT && p == NULL) { return (resolved); } goto err; } if (S_ISLNK(sb.st_mode)) { if (symlinks++ > SYMLOOP_MAX) { goto err; } slen = _dl_readlink(resolved, symlink, sizeof(symlink) - 1); if (slen < 0) goto err; symlink[slen] = '\0'; if (symlink[0] == '/') { resolved[1] = 0; resolved_len = 1; } else if (resolved_len > 1) { /* Strip the last path component. */ resolved[resolved_len - 1] = '\0'; q = _dl_strrchr(resolved, '/') + 1; *q = '\0'; resolved_len = q - resolved; } /* * If there are any path components left, then * append them to symlink. The result is placed * in `left'. */ if (p != NULL) { if (symlink[slen - 1] != '/') { if (slen + 1 >= sizeof(symlink)) { goto err; } symlink[slen] = '/'; symlink[slen + 1] = 0; } left_len = _dl_strlcat(symlink, left, sizeof(symlink)); if (left_len >= sizeof(left)) { goto err; } } left_len = _dl_strlcpy(left, symlink, sizeof(left)); } } /* * Remove trailing slash except when the resolved pathname * is a single "/". */ if (resolved_len > 1 && resolved[resolved_len - 1] == '/') resolved[resolved_len - 1] = '\0'; return (resolved); err: if (mem_allocated) _dl_free(resolved); return (NULL); }
/* * Perform $ORIGIN substitutions on path */ static void _dl_origin_subst_path(elf_object_t *object, const char *origin_path, char **path) { char tmp_path[PATH_MAX]; char *new_path, *tp; const char *pp, *name, *value; static struct utsname uts; size_t value_len; int skip_brace; if (uts.sysname[0] == '\0') { if (_dl_uname(&uts) != 0) return; } tp = tmp_path; pp = *path; while (*pp != '\0' && (tp - tmp_path) < sizeof(tmp_path)) { /* copy over chars up to but not including $ */ while (*pp != '\0' && *pp != '$' && (tp - tmp_path) < sizeof(tmp_path)) *tp++ = *pp++; /* substitution sequence detected */ if (*pp == '$' && (tp - tmp_path) < sizeof(tmp_path)) { pp++; if ((skip_brace = (*pp == '{'))) pp++; /* skip over name */ name = pp; while (_dl_isalnum((unsigned char)*pp) || *pp == '_') pp++; switch (_dl_subst_name(name, pp - name)) { case SUBST_ORIGIN: value = origin_path; break; case SUBST_OSNAME: value = uts.sysname; break; case SUBST_OSREL: value = uts.release; break; case SUBST_PLATFORM: value = uts.machine; break; default: value = ""; } value_len = _dl_strlen(value); if (value_len >= sizeof(tmp_path) - (tp - tmp_path)) return; _dl_bcopy(value, tp, value_len); tp += value_len; if (skip_brace && *pp == '}') pp++; } } /* no substitution made if result exceeds sizeof(tmp_path) */ if (tp - tmp_path >= sizeof(tmp_path)) return; /* NULL terminate tmp_path */ *tp = '\0'; if (_dl_strcmp(tmp_path, *path) == 0) return; new_path = _dl_strdup(tmp_path); if (new_path == NULL) return; DL_DEB(("orig_path %s\n", *path)); DL_DEB(("new_path %s\n", new_path)); _dl_free(*path); *path = new_path; }