search.c revision 1.1
1/* $NetBSD: search.c,v 1.1 1996/12/16 20:38:05 cgd Exp $ */ 2 3/* 4 * Copyright 1996 Matt Thomas <matt@3am-software.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by John Polstra. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33/* 34 * Dynamic linker for ELF. 35 * 36 * John Polstra <jdp@polstra.com>. 37 */ 38 39#include <err.h> 40#include <errno.h> 41#include <fcntl.h> 42#include <stdarg.h> 43#include <stdio.h> 44#include <stdlib.h> 45#include <string.h> 46#include <unistd.h> 47#include <sys/types.h> 48#include <sys/mman.h> 49#include <sys/stat.h> 50#include <dirent.h> 51 52#include "debug.h" 53#include "rtld.h" 54 55#define CONCAT(x,y) __CONCAT(x,y) 56#define ELFNAME(x) CONCAT(elf,CONCAT(ELFSIZE,CONCAT(_,x))) 57#define ELFNAME2(x,y) CONCAT(x,CONCAT(_elf,CONCAT(ELFSIZE,CONCAT(_,y)))) 58#define ELFNAMEEND(x) CONCAT(x,CONCAT(_elf,ELFSIZE)) 59#define ELFDEFNNAME(x) CONCAT(ELF,CONCAT(ELFSIZE,CONCAT(_,x))) 60 61/* 62 * Data declarations. 63 */ 64 65typedef struct { 66 const char *si_name; 67 const char *si_best_name; 68 char *si_best_fullpath; 69 const Search_Path *si_best_path; 70 size_t si_namelen; 71 int si_desired_major; 72 int si_desired_minor; 73 int si_best_major; 74 int si_best_minor; 75 unsigned si_exact : 1; 76} Search_Info; 77 78typedef enum { 79 Search_FoundNothing, 80 Search_FoundLower, 81 Search_FoundHigher, 82 Search_FoundExact 83} Search_Result; 84 85static bool 86_rtld_check_library( 87 const Search_Path *sp, 88 const char *name, 89 size_t namelen, 90 char **fullpath_p) 91{ 92 struct stat mystat; 93 char *fullpath; 94 Elf_Ehdr ehdr; 95 int fd; 96 97 fullpath = xmalloc(sp->sp_pathlen + 1 + namelen + 1); 98 strncpy(fullpath, sp->sp_path, sp->sp_pathlen); 99 fullpath[sp->sp_pathlen] = '/'; 100 strcpy(&fullpath[sp->sp_pathlen + 1], name); 101 102 dbg(" Trying \"%s\"", fullpath); 103 if (stat(fullpath, &mystat) >= 0 && S_ISREG(mystat.st_mode)) { 104 if ((fd = open(fullpath, O_RDONLY)) >= 0) { 105 if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) 106 goto lose; 107 108 /* Elf_e_ident includes class */ 109 if (memcmp(Elf_e_ident, ehdr.e_ident, Elf_e_siz) != 0) 110 goto lose; 111 112 switch (ehdr.e_machine) { 113 ELFDEFNNAME(MACHDEP_ID_CASES) 114 115 default: 116 goto lose; 117 } 118 119 if (ehdr.e_ident[Elf_ei_version] != Elf_ev_current || 120 ehdr.e_version != Elf_ev_current || 121 ehdr.e_ident[Elf_ei_data] != ELFDEFNNAME(MACHDEP_ENDIANNESS) || 122 ehdr.e_type != Elf_et_dyn) 123 goto lose; 124 125 if (*fullpath_p != NULL) 126 free(*fullpath_p); 127 *fullpath_p = fullpath; 128 return true; 129 130lose: 131 close(fd); 132 } 133 } 134 135 free(fullpath); 136 return false; 137} 138 139static Search_Result 140_rtld_search_directory( 141 const Search_Path *sp, 142 Search_Info *si) 143{ 144 struct dirent *entry; 145 DIR *dirp; 146 Search_Result result = Search_FoundNothing; 147 148dbg("_rtld_search_directory"); 149 if (sp->sp_path == NULL || sp->sp_path[0] == '\0') 150 return result; 151 152dbg("_rtld_search_directory 2"); 153 if ((dirp = opendir(sp->sp_path)) == NULL) { 154 dbg("_rtld_search_directory 2.1"); 155 return result; 156 } 157 158dbg("_rtld_search_directory 3"); 159 while ((entry = readdir(dirp)) != NULL) { 160 long major = -1; 161 long minor = -1; 162 if (strncmp(entry->d_name, si->si_name, si->si_namelen)) 163 continue; 164 /* 165 * We are matching libfoo.so only (no more info). Only take 166 * it as a last resort. 167 */ 168 if (si->si_exact) { 169 if (strcmp(entry->d_name, si->si_name)) 170 continue; 171#ifdef notyet 172 } else if (entry->d_namlen == si->si_namelen) { 173 if (si->si_best_path != NULL || si->si_best_major != -1) 174 continue; 175#endif 176 } else { 177 char *cp; 178 /* 179 * We expect (demand!) that it be of the form 180 * "libfoo.so.<something>" 181 */ 182 if (entry->d_name[si->si_namelen] != '.') 183 continue; 184 /* 185 * This file has a least a major number (well, maybe not if it 186 * has a name of "libfoo.so." but treat that as equivalent to 0. 187 * It had better match what we are looking for. 188 */ 189 major = strtol(&entry->d_name[si->si_namelen+1], &cp, 10); 190 if (major < 0 || (cp[0] != '\0' && cp[0] != '.') 191 || &entry->d_name[si->si_namelen+1] == cp) 192 continue; 193 if (cp[0] == '.') { 194 char *cp2; 195 minor = strtol(&cp[1], &cp2, 10); 196 if (minor < 0 || cp2[0] != '\0' || cp == cp2) 197 continue; 198 } else { 199 minor = 0; 200 } 201 if (major != si->si_desired_major || minor <= si->si_best_minor) 202 continue; 203 } 204 /* 205 * We have a better candidate... 206 */ 207 if (!_rtld_check_library(sp, entry->d_name, entry->d_namlen, 208 &si->si_best_fullpath)) 209 continue; 210 211 si->si_best_name = &si->si_best_fullpath[sp->sp_pathlen + 1]; 212 si->si_best_major = major; 213 si->si_best_minor = minor; 214 si->si_best_path = sp; 215 216 if (si->si_exact || si->si_best_minor == si->si_desired_minor) 217 result = Search_FoundExact; 218 else if (si->si_best_minor > si->si_desired_minor) 219 result = Search_FoundHigher; 220 else 221 result = Search_FoundLower; 222 223 /* 224 * We were looking for, and found, an exact match. We're done. 225 */ 226 if (si->si_exact) 227 break; 228 } 229 230 dbg("found %s (%d.%d) match for %s (%d.%d) -> %s", 231 result == Search_FoundNothing ? "no" 232 : result == Search_FoundLower ? "lower" 233 : result == Search_FoundExact ? "exact" : "higher", 234 si->si_best_major, si->si_best_minor, 235 si->si_name, 236 si->si_desired_major, si->si_desired_minor, 237 si->si_best_fullpath ? si->si_best_fullpath : sp->sp_path); 238 239 closedir(dirp); 240 return result; 241} 242 243static char * 244_rtld_search_library_paths( 245 const char *name, 246 Search_Path *paths, 247 const Search_Path *rpaths) 248{ 249 Search_Info info; 250 Search_Path *path; 251 const char *cp; 252 Search_Result result = Search_FoundNothing; 253 254 memset(&info, 0, sizeof(info)); 255 info.si_name = name; 256 info.si_desired_major = -1; 257 info.si_desired_minor = -1; 258 info.si_best_major = -1; 259 info.si_best_minor = -1; 260 261 cp = strstr(name, ".so"); 262 if (cp == NULL) { 263 info.si_exact = true; 264 } else { 265 cp += sizeof(".so") - 1; 266 info.si_namelen = cp - name; 267 if (cp[0] != '.') { 268 info.si_exact = true; 269 } else { 270 info.si_desired_major = atoi(&cp[1]); 271 if ((cp = strchr(&cp[1], '.')) != NULL) { 272 info.si_desired_minor = atoi(&cp[1]); 273 } else { 274 info.si_desired_minor = 0; 275 } 276 } 277 } 278 279 if (rpaths != NULL && result < Search_FoundHigher) { /* Exact? */ 280 dbg(" checking rpaths.."); 281 for (; rpaths != NULL; rpaths = rpaths->sp_next) { 282 dbg(" in \"%s\"", rpaths->sp_path); 283 result = _rtld_search_directory(rpaths, &info); 284 if (result >= Search_FoundHigher) /* Exact? */ 285 break; 286 } 287 } 288 if (result < Search_FoundHigher) { /* Exact? */ 289 dbg(" checking default paths.."); 290 for (path = paths; path != NULL; path = path->sp_next) { 291 dbg(" in \"%s\"", path->sp_path); 292 result = _rtld_search_directory(path, &info); 293 if (result >= Search_FoundHigher) /* Exact? */ 294 break; 295 } 296 } 297 298 if (result >= Search_FoundHigher) 299 return info.si_best_fullpath; 300 301 if (info.si_best_fullpath != NULL) 302 free(info.si_best_fullpath); 303 return NULL; 304} 305 306/* 307 * Find the library with the given name, and return its full pathname. 308 * The returned string is dynamically allocated. Generates an error 309 * message and returns NULL if the library cannot be found. 310 * 311 * If the second argument is non-NULL, then it refers to an already- 312 * loaded shared object, whose library search path will be searched. 313 */ 314char * 315_rtld_find_library( 316 const char *name, 317 const Obj_Entry *refobj) 318{ 319 char *pathname; 320 321 if (strchr(name, '/') != NULL) { /* Hard coded pathname */ 322 if (name[0] != '/' && !_rtld_trust) { 323 _rtld_error("Absolute pathname required for shared object \"%s\"", 324 name); 325 return NULL; 326 } 327#ifdef SVR4_LIBDIR 328 if (strncmp(name, SVR4_LIBDIR, SVR4_LIBDIRLEN) == 0 329 && name[SVR4_LIBDIRLEN] == '/') { /* In "/usr/lib" */ 330 /* Map hard-coded "/usr/lib" onto our ELF library directory. */ 331 pathname = xmalloc(strlen(name) + LIBDIRLEN - SVR4_LIBDIRLEN + 1); 332 strcpy(pathname, LIBDIR); 333 strcpy(pathname + LIBDIRLEN, name + SVR4_LIBDIRLEN); 334 return pathname; 335 } 336#endif /* SVR4_LIBDIR */ 337 return xstrdup(name); 338 } 339 340 dbg(" Searching for \"%s\" (%p)", name, refobj); 341 342 pathname = _rtld_search_library_paths(name, _rtld_paths, 343 refobj ? refobj->rpaths : NULL); 344 if (pathname == NULL) 345 _rtld_error("Shared object \"%s\" not found", name); 346 return pathname; 347} 348