search.c revision 1.2 1 /* $NetBSD: search.c,v 1.2 1997/02/03 19:45:02 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 close(fd);
129 return true;
130
131 lose:
132 close(fd);
133 }
134 }
135
136 free(fullpath);
137 return false;
138 }
139
140 static Search_Result
141 _rtld_search_directory(
142 const Search_Path *sp,
143 Search_Info *si)
144 {
145 struct dirent *entry;
146 DIR *dirp;
147 Search_Result result = Search_FoundNothing;
148
149 dbg("_rtld_search_directory");
150 if (sp->sp_path == NULL || sp->sp_path[0] == '\0')
151 return result;
152
153 dbg("_rtld_search_directory 2");
154 if ((dirp = opendir(sp->sp_path)) == NULL) {
155 dbg("_rtld_search_directory 2.1");
156 return result;
157 }
158
159 dbg("_rtld_search_directory 3");
160 while ((entry = readdir(dirp)) != NULL) {
161 long major = -1;
162 long minor = -1;
163 if (strncmp(entry->d_name, si->si_name, si->si_namelen))
164 continue;
165 /*
166 * We are matching libfoo.so only (no more info). Only take
167 * it as a last resort.
168 */
169 if (si->si_exact) {
170 if (strcmp(entry->d_name, si->si_name))
171 continue;
172 #ifdef notyet
173 } else if (entry->d_namlen == si->si_namelen) {
174 if (si->si_best_path != NULL || si->si_best_major != -1)
175 continue;
176 #endif
177 } else {
178 char *cp;
179 /*
180 * We expect (demand!) that it be of the form
181 * "libfoo.so.<something>"
182 */
183 if (entry->d_name[si->si_namelen] != '.')
184 continue;
185 /*
186 * This file has a least a major number (well, maybe not if it
187 * has a name of "libfoo.so." but treat that as equivalent to 0.
188 * It had better match what we are looking for.
189 */
190 major = strtol(&entry->d_name[si->si_namelen+1], &cp, 10);
191 if (major < 0 || (cp[0] != '\0' && cp[0] != '.')
192 || &entry->d_name[si->si_namelen+1] == cp)
193 continue;
194 if (cp[0] == '.') {
195 char *cp2;
196 minor = strtol(&cp[1], &cp2, 10);
197 if (minor < 0 || cp2[0] != '\0' || cp == cp2)
198 continue;
199 } else {
200 minor = 0;
201 }
202 if (major != si->si_desired_major || minor <= si->si_best_minor)
203 continue;
204 }
205 /*
206 * We have a better candidate...
207 */
208 if (!_rtld_check_library(sp, entry->d_name, entry->d_namlen,
209 &si->si_best_fullpath))
210 continue;
211
212 si->si_best_name = &si->si_best_fullpath[sp->sp_pathlen + 1];
213 si->si_best_major = major;
214 si->si_best_minor = minor;
215 si->si_best_path = sp;
216
217 if (si->si_exact || si->si_best_minor == si->si_desired_minor)
218 result = Search_FoundExact;
219 else if (si->si_best_minor > si->si_desired_minor)
220 result = Search_FoundHigher;
221 else
222 result = Search_FoundLower;
223
224 /*
225 * We were looking for, and found, an exact match. We're done.
226 */
227 if (si->si_exact)
228 break;
229 }
230
231 dbg("found %s (%d.%d) match for %s (%d.%d) -> %s",
232 result == Search_FoundNothing ? "no"
233 : result == Search_FoundLower ? "lower"
234 : result == Search_FoundExact ? "exact" : "higher",
235 si->si_best_major, si->si_best_minor,
236 si->si_name,
237 si->si_desired_major, si->si_desired_minor,
238 si->si_best_fullpath ? si->si_best_fullpath : sp->sp_path);
239
240 closedir(dirp);
241 return result;
242 }
243
244 static char *
246 _rtld_search_library_paths(
247 const char *name,
248 Search_Path *paths,
249 const Search_Path *rpaths)
250 {
251 Search_Info info;
252 Search_Path *path;
253 const char *cp;
254 Search_Result result = Search_FoundNothing;
255
256 memset(&info, 0, sizeof(info));
257 info.si_name = name;
258 info.si_desired_major = -1;
259 info.si_desired_minor = -1;
260 info.si_best_major = -1;
261 info.si_best_minor = -1;
262
263 cp = strstr(name, ".so");
264 if (cp == NULL) {
265 info.si_exact = true;
266 } else {
267 cp += sizeof(".so") - 1;
268 info.si_namelen = cp - name;
269 if (cp[0] != '.') {
270 info.si_exact = true;
271 } else {
272 info.si_desired_major = atoi(&cp[1]);
273 if ((cp = strchr(&cp[1], '.')) != NULL) {
274 info.si_desired_minor = atoi(&cp[1]);
275 } else {
276 info.si_desired_minor = 0;
277 }
278 }
279 }
280
281 if (rpaths != NULL && result < Search_FoundHigher) { /* Exact? */
282 dbg(" checking rpaths..");
283 for (; rpaths != NULL; rpaths = rpaths->sp_next) {
284 dbg(" in \"%s\"", rpaths->sp_path);
285 result = _rtld_search_directory(rpaths, &info);
286 if (result >= Search_FoundHigher) /* Exact? */
287 break;
288 }
289 }
290 if (result < Search_FoundHigher) { /* Exact? */
291 dbg(" checking default paths..");
292 for (path = paths; path != NULL; path = path->sp_next) {
293 dbg(" in \"%s\"", path->sp_path);
294 result = _rtld_search_directory(path, &info);
295 if (result >= Search_FoundHigher) /* Exact? */
296 break;
297 }
298 }
299
300 if (result >= Search_FoundHigher)
301 return info.si_best_fullpath;
302
303 if (info.si_best_fullpath != NULL)
304 free(info.si_best_fullpath);
305 return NULL;
306 }
307
308 /*
309 * Find the library with the given name, and return its full pathname.
310 * The returned string is dynamically allocated. Generates an error
311 * message and returns NULL if the library cannot be found.
312 *
313 * If the second argument is non-NULL, then it refers to an already-
314 * loaded shared object, whose library search path will be searched.
315 */
316 char *
317 _rtld_find_library(
318 const char *name,
319 const Obj_Entry *refobj)
320 {
321 char *pathname;
322
323 if (strchr(name, '/') != NULL) { /* Hard coded pathname */
324 if (name[0] != '/' && !_rtld_trust) {
325 _rtld_error("Absolute pathname required for shared object \"%s\"",
326 name);
327 return NULL;
328 }
329 #ifdef SVR4_LIBDIR
330 if (strncmp(name, SVR4_LIBDIR, SVR4_LIBDIRLEN) == 0
331 && name[SVR4_LIBDIRLEN] == '/') { /* In "/usr/lib" */
332 /* Map hard-coded "/usr/lib" onto our ELF library directory. */
333 pathname = xmalloc(strlen(name) + LIBDIRLEN - SVR4_LIBDIRLEN + 1);
334 strcpy(pathname, LIBDIR);
335 strcpy(pathname + LIBDIRLEN, name + SVR4_LIBDIRLEN);
336 return pathname;
337 }
338 #endif /* SVR4_LIBDIR */
339 return xstrdup(name);
340 }
341
342 dbg(" Searching for \"%s\" (%p)", name, refobj);
343
344 pathname = _rtld_search_library_paths(name, _rtld_paths,
345 refobj ? refobj->rpaths : NULL);
346 if (pathname == NULL)
347 _rtld_error("Shared object \"%s\" not found", name);
348 return pathname;
349 }
350