/* * 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); }
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; }