/* $OpenBSD: sod.c,v 1.35 2017/12/12 15:33:34 deraadt Exp $ */ /* * Copyright (c) 1993 Paul Kranenburg * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Paul Kranenburg. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include "syscall.h" #include "archdep.h" #include "path.h" #include "util.h" #include "sod.h" int _dl_hinthash(char *cp, int vmajor, int vminor); void _dl_maphints(void); /* * Populate sod struct for dlopen's call to map_object */ void _dl_build_sod(const char *name, struct sod *sodp) { unsigned int tuplet; int major, minor; char *realname, *tok, *etok, *cp; /* default is an absolute or relative path */ sodp->sod_name = (long)_dl_strdup(name); /* strtok is destructive */ if (sodp->sod_name == 0) _dl_oom(); sodp->sod_library = 0; sodp->sod_major = sodp->sod_minor = 0; /* does it look like /^lib/ ? */ if (_dl_strncmp((char *)sodp->sod_name, "lib", 3) != 0) goto backout; /* is this a filename? */ if (_dl_strchr((char *)sodp->sod_name, '/')) goto backout; /* skip over 'lib' */ cp = (char *)sodp->sod_name + 3; realname = cp; /* dot guardian */ if ((_dl_strchr(cp, '.') == NULL) || (*(cp+_dl_strlen(cp)-1) == '.')) goto backout; cp = _dl_strstr(cp, ".so"); if (cp == NULL) goto backout; /* default */ major = minor = -1; /* loop through name - parse skipping name */ for (tuplet = 0; (tok = _dl_strsep(&cp, ".")) != NULL; tuplet++) { switch (tuplet) { case 0: /* empty tok, we already skipped to "\.so.*" */ break; case 1: /* 'so' extension */ break; case 2: /* major version extension */ major = _dl_strtol(tok, &etok, 10); if (*tok == '\0' || *etok != '\0') goto backout; break; case 3: /* minor version extension */ minor = _dl_strtol(tok, &etok, 10); if (*tok == '\0' || *etok != '\0') goto backout; break; /* if we get here, it must be weird */ default: goto backout; } } if (realname == NULL) goto backout; cp = (char *)sodp->sod_name; sodp->sod_name = (long)_dl_strdup(realname); if (sodp->sod_name == 0) _dl_oom(); _dl_free(cp); sodp->sod_library = 1; sodp->sod_major = major; sodp->sod_minor = minor; return; backout: _dl_free((char *)sodp->sod_name); sodp->sod_name = (long)_dl_strdup(name); if (sodp->sod_name == 0) _dl_oom(); } void _dl_set_sod(const char *path, struct sod *sod) { char *fname = _dl_strrchr(path, '/'); if (fname != NULL) _dl_build_sod(++fname, sod); else _dl_build_sod(path, sod); } static struct hints_header *hheader = NULL; static struct hints_bucket *hbuckets; static char *hstrtab; char **_dl_hint_search_path = NULL; #define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1) void _dl_maphints(void) { struct stat sb; caddr_t addr = MAP_FAILED; long hsize = 0; int hfd; if ((hfd = _dl_open(_PATH_LD_HINTS, O_RDONLY | O_CLOEXEC)) < 0) { hfd = -1; goto bad_hints; } if (_dl_fstat(hfd, &sb) != 0 || !S_ISREG(sb.st_mode) || sb.st_size < sizeof(struct hints_header) || sb.st_size > LONG_MAX) goto bad_hints; hsize = (long)sb.st_size; addr = (void *)_dl_mmap(0, hsize, PROT_READ, MAP_PRIVATE, hfd, 0); if (_dl_mmap_error(addr)) goto bad_hints; hheader = (struct hints_header *)addr; if (HH_BADMAG(*hheader) || hheader->hh_ehints > hsize) goto bad_hints; if (hheader->hh_version != LD_HINTS_VERSION_2) goto bad_hints; hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab); hstrtab = (char *)(addr + hheader->hh_strtab); if (hheader->hh_version >= LD_HINTS_VERSION_2) _dl_hint_search_path = _dl_split_path(hstrtab + hheader->hh_dirlist); /* close the file descriptor, leaving the hints mapped */ _dl_close(hfd); return; bad_hints: if (!_dl_mmap_error(addr)) _dl_munmap(addr, hsize); if (hfd != -1) _dl_close(hfd); hheader = (struct hints_header *)-1; } char * _dl_findhint(char *name, int major, int minor, char *preferred_path) { struct hints_bucket *bp; /* * If not mapped, and we have not tried before, try to map the * hints, if previous attempts failed hheader is -1 and we * do not wish to retry it. */ if (hheader == NULL) _dl_maphints(); /* if it failed to map, return failure */ if (!(HINTS_VALID)) return NULL; if (hheader->hh_nbucket == 0) return NULL; bp = hbuckets + (_dl_hinthash(name, major, minor) % hheader->hh_nbucket); while (1) { /* Sanity check */ if (bp->hi_namex >= hheader->hh_strtab_sz) _dl_die("bad name index: %#x", bp->hi_namex); if (bp->hi_pathx >= hheader->hh_strtab_sz) _dl_die("bad path index: %#x", bp->hi_pathx); if (_dl_strcmp(name, hstrtab + bp->hi_namex) == 0) { /* It's `name', check version numbers */ if (bp->hi_major == major && (bp->hi_ndewey < 2 || bp->hi_minor >= minor)) { if (preferred_path == NULL) { return hstrtab + bp->hi_pathx; } else { char *path = hstrtab + bp->hi_pathx; char *edir = _dl_strrchr(path, '/'); if ((_dl_strncmp(preferred_path, path, (edir - path)) == 0) && (preferred_path[edir - path] == '\0')) return path; } } } if (bp->hi_next == -1) break; /* Move on to next in bucket */ bp = &hbuckets[bp->hi_next]; } /* No hints available for name */ return NULL; } int _dl_hinthash(char *cp, int vmajor, int vminor) { int k = 0; while (*cp) k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff; k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff; return k; }