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 (at) 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 (at) 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
65 typedef 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
78 typedef enum {
79 Search_FoundNothing,
80 Search_FoundLower,
81 Search_FoundHigher,
82 Search_FoundExact
83 } Search_Result;
84
85 static 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
130 lose:
131 close(fd);
132 }
133 }
134
135 free(fullpath);
136 return false;
137 }
138
139 static 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
148 dbg("_rtld_search_directory");
149 if (sp->sp_path == NULL || sp->sp_path[0] == '\0')
150 return result;
151
152 dbg("_rtld_search_directory 2");
153 if ((dirp = opendir(sp->sp_path)) == NULL) {
154 dbg("_rtld_search_directory 2.1");
155 return result;
156 }
157
158 dbg("_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
243 static char *
245 _rtld_search_library_paths(
246 const char *name,
247 Search_Path *paths,
248 const Search_Path *rpaths)
249 {
250 Search_Info info;
251 Search_Path *path;
252 const char *cp;
253 Search_Result result = Search_FoundNothing;
254
255 memset(&info, 0, sizeof(info));
256 info.si_name = name;
257 info.si_desired_major = -1;
258 info.si_desired_minor = -1;
259 info.si_best_major = -1;
260 info.si_best_minor = -1;
261
262 cp = strstr(name, ".so");
263 if (cp == NULL) {
264 info.si_exact = true;
265 } else {
266 cp += sizeof(".so") - 1;
267 info.si_namelen = cp - name;
268 if (cp[0] != '.') {
269 info.si_exact = true;
270 } else {
271 info.si_desired_major = atoi(&cp[1]);
272 if ((cp = strchr(&cp[1], '.')) != NULL) {
273 info.si_desired_minor = atoi(&cp[1]);
274 } else {
275 info.si_desired_minor = 0;
276 }
277 }
278 }
279
280 if (rpaths != NULL && result < Search_FoundHigher) { /* Exact? */
281 dbg(" checking rpaths..");
282 for (; rpaths != NULL; rpaths = rpaths->sp_next) {
283 dbg(" in \"%s\"", rpaths->sp_path);
284 result = _rtld_search_directory(rpaths, &info);
285 if (result >= Search_FoundHigher) /* Exact? */
286 break;
287 }
288 }
289 if (result < Search_FoundHigher) { /* Exact? */
290 dbg(" checking default paths..");
291 for (path = paths; path != NULL; path = path->sp_next) {
292 dbg(" in \"%s\"", path->sp_path);
293 result = _rtld_search_directory(path, &info);
294 if (result >= Search_FoundHigher) /* Exact? */
295 break;
296 }
297 }
298
299 if (result >= Search_FoundHigher)
300 return info.si_best_fullpath;
301
302 if (info.si_best_fullpath != NULL)
303 free(info.si_best_fullpath);
304 return NULL;
305 }
306
307 /*
308 * Find the library with the given name, and return its full pathname.
309 * The returned string is dynamically allocated. Generates an error
310 * message and returns NULL if the library cannot be found.
311 *
312 * If the second argument is non-NULL, then it refers to an already-
313 * loaded shared object, whose library search path will be searched.
314 */
315 char *
316 _rtld_find_library(
317 const char *name,
318 const Obj_Entry *refobj)
319 {
320 char *pathname;
321
322 if (strchr(name, '/') != NULL) { /* Hard coded pathname */
323 if (name[0] != '/' && !_rtld_trust) {
324 _rtld_error("Absolute pathname required for shared object \"%s\"",
325 name);
326 return NULL;
327 }
328 #ifdef SVR4_LIBDIR
329 if (strncmp(name, SVR4_LIBDIR, SVR4_LIBDIRLEN) == 0
330 && name[SVR4_LIBDIRLEN] == '/') { /* In "/usr/lib" */
331 /* Map hard-coded "/usr/lib" onto our ELF library directory. */
332 pathname = xmalloc(strlen(name) + LIBDIRLEN - SVR4_LIBDIRLEN + 1);
333 strcpy(pathname, LIBDIR);
334 strcpy(pathname + LIBDIRLEN, name + SVR4_LIBDIRLEN);
335 return pathname;
336 }
337 #endif /* SVR4_LIBDIR */
338 return xstrdup(name);
339 }
340
341 dbg(" Searching for \"%s\" (%p)", name, refobj);
342
343 pathname = _rtld_search_library_paths(name, _rtld_paths,
344 refobj ? refobj->rpaths : NULL);
345 if (pathname == NULL)
346 _rtld_error("Shared object \"%s\" not found", name);
347 return pathname;
348 }
349