diff options
Diffstat (limited to 'ld/ld_path.c')
-rw-r--r-- | ld/ld_path.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/ld/ld_path.c b/ld/ld_path.c new file mode 100644 index 0000000000000..15687b5a402cc --- /dev/null +++ b/ld/ld_path.c @@ -0,0 +1,295 @@ +/*- + * 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); +} |