/*- * Copyright (c) 2010-2013 Kai Wang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ld_path.c 2930 2013-03-17 22:54:26Z kaiwang27 $ */ #include "ld.h" #include "ld_file.h" #include "ld_path.h" static char *_search_file(struct ld *ld, const char *path, const char *file); static char * _search_file(struct ld *ld, const char *path, const char *file) { struct dirent *dp; DIR *dirp; char *fp; assert(path != NULL && file != NULL); if ((dirp = opendir(path)) == NULL) { ld_warn(ld, "opendir failed: %s", strerror(errno)); return (NULL); } fp = NULL; while ((dp = readdir(dirp)) != NULL) { if (!strcmp(dp->d_name, file)) { if ((fp = malloc(PATH_MAX + 1)) == NULL) ld_fatal_std(ld, "malloc"); fp[0] = '\0'; snprintf(fp, PATH_MAX + 1, "%s/%s", path, dp->d_name); break; } } (void) closedir(dirp); return (fp); } void ld_path_add(struct ld *ld, char *path, enum ld_path_type lpt) { struct ld_state *ls; struct ld_path *lp; assert(ld != NULL && path != NULL); ls = &ld->ld_state; if ((lp = calloc(1, sizeof(*lp))) == NULL) ld_fatal_std(ld, "calloc"); if ((lp->lp_path = strdup(path)) == NULL) ld_fatal_std(ld, "strdup"); switch (lpt) { case LPT_L: STAILQ_INSERT_TAIL(&ls->ls_lplist, lp, lp_next); break; case LPT_RPATH: STAILQ_INSERT_TAIL(&ls->ls_rplist, lp, lp_next); break; case LPT_RPATH_LINK: STAILQ_INSERT_TAIL(&ls->ls_rllist, lp, lp_next); break; default: ld_fatal(ld, "Internal: invalid path type %d", lpt); break; } } void ld_path_add_multiple(struct ld *ld, char *str, enum ld_path_type lpt) { char *p; while ((p = strsep(&str, ":")) != NULL) { if (*p != '\0') ld_path_add(ld, p, lpt); } } void ld_path_cleanup(struct ld *ld) { struct ld_state *ls; struct ld_path *lp, *_lp; ls = &ld->ld_state; STAILQ_FOREACH_SAFE(lp, &ls->ls_lplist, lp_next, _lp) { STAILQ_REMOVE(&ls->ls_lplist, lp, ld_path, lp_next); free(lp->lp_path); free(lp); } STAILQ_FOREACH_SAFE(lp, &ls->ls_rplist, lp_next, _lp) { STAILQ_REMOVE(&ls->ls_rplist, lp, ld_path, lp_next); free(lp->lp_path); free(lp); } STAILQ_FOREACH_SAFE(lp, &ls->ls_rllist, lp_next, _lp) { STAILQ_REMOVE(&ls->ls_rllist, lp, ld_path, lp_next); free(lp->lp_path); free(lp); } } char * ld_path_join_rpath(struct ld *ld) { struct ld_state *ls; struct ld_path *lp; char *s; int len; ls = &ld->ld_state; if (STAILQ_EMPTY(&ls->ls_rplist)) return (NULL); len = 0; STAILQ_FOREACH(lp, &ls->ls_rplist, lp_next) len += strlen(lp->lp_path) + 1; if ((s = malloc(len)) == NULL) ld_fatal_std(ld, "malloc"); STAILQ_FOREACH(lp, &ls->ls_rplist, lp_next) { strcat(s, lp->lp_path); if (lp != STAILQ_LAST(&ls->ls_rplist, ld_path, lp_next)) strcat(s, ":"); } return (s); } void ld_path_search_file(struct ld *ld, struct ld_file *lf) { struct ld_state *ls; struct ld_path *lp; char *fp; int found; assert(lf != NULL); ls = &ld->ld_state; found = 0; STAILQ_FOREACH(lp, &ls->ls_lplist, lp_next) { if ((fp = _search_file(ld, lp->lp_path, lf->lf_name)) != NULL) { free(lf->lf_name); lf->lf_name = fp; found = 1; break; } } if (!found) ld_fatal(ld, "cannot find %s", lf->lf_name); } void ld_path_search_library(struct ld *ld, const char *name) { struct ld_state *ls; struct ld_path *lp; struct dirent *dp; DIR *dirp; char fp[PATH_MAX + 1], sfp[PATH_MAX + 1]; size_t len; int found; assert(ld != NULL && name != NULL); ls = &ld->ld_state; len = strlen(name); found = 0; STAILQ_FOREACH(lp, &ls->ls_lplist, lp_next) { assert(lp->lp_path != NULL); if ((dirp = opendir(lp->lp_path)) == NULL) { ld_warn(ld, "opendir failed: %s", strerror(errno)); continue; } fp[0] = sfp[0] = '\0'; while ((dp = readdir(dirp)) != NULL) { if (strncmp(dp->d_name, "lib", 3)) continue; if (strncmp(name, &dp->d_name[3], len)) continue; if (ls->ls_static == 0 && !strcmp(&dp->d_name[len + 3], ".so")) { snprintf(fp, sizeof(fp), "%s/%s", lp->lp_path, dp->d_name); ld_file_add(ld, fp, LFT_DSO); (void) closedir(dirp); found = 1; goto done; } else if (*sfp == '\0' && !strcmp(&dp->d_name[len + 3], ".a")) { snprintf(sfp, sizeof(sfp), "%s/%s", lp->lp_path, dp->d_name); if (ls->ls_static == 1) { ld_file_add(ld, sfp, LFT_ARCHIVE); (void) closedir(dirp); found = 1; goto done; } } } (void) closedir(dirp); } done: if (!found) { if (ls->ls_static == 0 && *sfp != '\0') { ld_file_add(ld, sfp, LFT_ARCHIVE); } else ld_fatal(ld, "cannot find -l%s", name); } } void ld_path_search_dso_needed(struct ld *ld, struct ld_file *lf, const char *name) { struct ld_state *ls; struct ld_path *lp; struct ld_file *_lf; char *fp; ls = &ld->ld_state; /* * First check if we've seen this shared library or if it's * already listed in the input file list. */ TAILQ_FOREACH(_lf, &ld->ld_lflist, lf_next) { if (!strcmp(_lf->lf_name, name) || !strcmp(basename(_lf->lf_name), name)) return; } /* Search -rpath-link directories. */ STAILQ_FOREACH(lp, &ls->ls_rllist, lp_next) { if ((fp = _search_file(ld, lp->lp_path, name)) != NULL) goto done; } /* Search -rpath directories. */ STAILQ_FOREACH(lp, &ls->ls_rplist, lp_next) { if ((fp = _search_file(ld, lp->lp_path, name)) != NULL) goto done; } /* TODO: search additional directories and environment variables. */ /* Search /lib and /usr/lib. */ if ((fp = _search_file(ld, "/lib", name)) != NULL) goto done; if ((fp = _search_file(ld, "/usr/lib", name)) != NULL) goto done; /* Not found. */ ld_warn(ld, "cannot find needed shared library: %s", name); return; done: ld_file_add_after(ld, fp, LFT_DSO, lf); free(fp); }