1 /* $OpenBSD: library.c,v 1.2 2006/05/13 16:33:40 deraadt Exp $ */
2 /*
3 * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/syslimits.h>
21 #include <sys/param.h>
22 #include <sys/mman.h>
23 #include <fcntl.h>
24 #include <nlist.h>
25 #include <elf_abi.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <dirent.h>
31 #include "link.h"
32 #include "sod.h"
33 #include "resolve.h"
34 #include "prebind.h"
35 #include "prebind_struct.h"
36
37 /* TODO - library path from ldconfig */
38 #define DEFAULT_PATH "/usr/lib:/usr/X11R6/lib:/usr/local/qte/lib"
39
40 elf_object_t * elf_load_shlib_hint(struct sod *sod, struct sod *req_sod,
41 int ignore_hints, const char *libpath);
42 char * elf_find_shlib(struct sod *sodp, const char *searchpath, int nohints);
43 elf_object_t * elf_tryload_shlib(const char *libname);
44 int elf_match_file(struct sod *sodp, char *name, int namelen);
45
46 int
load_lib(const char * name,struct elf_object * parent)47 load_lib(const char *name, struct elf_object *parent)
48 {
49 struct sod sod, req_sod;
50 int ignore_hints, try_any_minor = 0;
51 struct elf_object *object = NULL;
52
53 #if 0
54 printf("load_lib %s\n", name);
55 #endif
56 ignore_hints = 0;
57
58 if(strchr(name, '/')) {
59 char *lpath, *lname;
60
61 lpath = strdup(name);
62 lname = strrchr(lpath, '/');
63 if (lname == NULL || lname[1] == '\0') {
64 free(lpath);
65 return (1); /* failed */
66 }
67 *lname = '\0';
68 lname++;
69
70 _dl_build_sod(lname, &sod);
71 req_sod = sod;
72
73 /* this code does not allow lower minors */
74 fullpathagain:
75 object = elf_load_shlib_hint(&sod, &req_sod,
76 ignore_hints, lpath);
77 if (object != NULL)
78 goto fullpathdone;
79
80 if (try_any_minor == 0) {
81 try_any_minor = 1;
82 ignore_hints = 1;
83 req_sod.sod_minor = -1;
84 goto fullpathagain;
85 }
86 /* ERR */
87 fullpathdone:
88 free(lpath);
89 free((char *)sod.sod_name);
90 return (object == NULL); /* failed */
91 }
92 _dl_build_sod(name, &sod);
93 req_sod = sod;
94
95 /* ignore LD_LIBRARY_PATH */
96
97 again:
98 if (parent->dyn.rpath != NULL) {
99 object = elf_load_shlib_hint(&sod, &req_sod,
100 ignore_hints, parent->dyn.rpath);
101 if (object != NULL)
102 goto done;
103 }
104 if (parent != load_object && load_object->dyn.rpath != NULL) {
105 object = elf_load_shlib_hint(&sod, &req_sod,
106 ignore_hints, load_object->dyn.rpath);
107 if (object != NULL)
108 goto done;
109 }
110 object = elf_load_shlib_hint(&sod, &req_sod, ignore_hints, NULL);
111
112 if (try_any_minor == 0) {
113 try_any_minor = 1;
114 ignore_hints = 1;
115 req_sod.sod_minor = -1;
116 goto again;
117 }
118 if (object == NULL)
119 printf ("unable to load %s\n", name);
120
121 done:
122 free((char *)sod.sod_name);
123
124 return (object == NULL);
125 }
126
127 /*
128 * attempt to locate and load a library based on libpath, sod info and
129 * if it needs to respect hints, passing type and flags to perform open
130 */
131 elf_object_t *
elf_load_shlib_hint(struct sod * sod,struct sod * req_sod,int ignore_hints,const char * libpath)132 elf_load_shlib_hint(struct sod *sod, struct sod *req_sod,
133 int ignore_hints, const char *libpath)
134 {
135 elf_object_t *object = NULL;
136 char *hint;
137
138 hint = elf_find_shlib(req_sod, libpath, ignore_hints);
139 if (hint != NULL) {
140 if (req_sod->sod_minor < sod->sod_minor)
141 printf("warning: lib%s.so.%d.%d: "
142 "minor version >= %d expected, "
143 "using it anyway\n",
144 (char *)sod->sod_name, sod->sod_major,
145 req_sod->sod_minor, sod->sod_minor);
146 object = elf_tryload_shlib(hint);
147 }
148 return object;
149 }
150
151 char elf_hint_store[MAXPATHLEN];
152
153 char *
elf_find_shlib(struct sod * sodp,const char * searchpath,int nohints)154 elf_find_shlib(struct sod *sodp, const char *searchpath, int nohints)
155 {
156 char *hint, lp[PATH_MAX + 10], *path;
157 struct sod tsod, bsod; /* transient and best sod */
158 struct dirent *dp;
159 const char *pp;
160 int match, len;
161 DIR *dd;
162
163 /* if we are to search default directories, and hints
164 * are not to be used, search the standard path from ldconfig
165 * (_dl_hint_search_path) or use the default path
166 */
167 if (nohints)
168 goto nohints;
169
170 if (searchpath == NULL) {
171 /* search 'standard' locations, find any match in the hints */
172 hint = _dl_findhint((char *)sodp->sod_name, sodp->sod_major,
173 sodp->sod_minor, NULL);
174 if (hint)
175 return hint;
176 } else {
177 /* search hints requesting matches for only
178 * the searchpath directories,
179 */
180 pp = searchpath;
181 while (pp) {
182 path = lp;
183 while (path < lp + PATH_MAX &&
184 *pp && *pp != ':' && *pp != ';')
185 *path++ = *pp++;
186 *path = 0;
187
188 /* interpret "" as curdir "." */
189 if (lp[0] == '\0') {
190 lp[0] = '.';
191 lp[1] = '\0';
192 }
193
194 hint = _dl_findhint((char *)sodp->sod_name,
195 sodp->sod_major, sodp->sod_minor, lp);
196 if (hint != NULL)
197 return hint;
198
199 if (*pp) /* Try curdir if ':' at end */
200 pp++;
201 else
202 pp = 0;
203 }
204 }
205
206 /*
207 * For each directory in the searchpath, read the directory
208 * entries looking for a match to sod. filename compare is
209 * done by _dl_match_file()
210 */
211 nohints:
212 if (searchpath == NULL) {
213 if (_dl_hint_search_path != NULL)
214 searchpath = _dl_hint_search_path;
215 else
216 searchpath = DEFAULT_PATH;
217 }
218 pp = searchpath;
219 while (pp) {
220 path = lp;
221 while (path < lp + PATH_MAX && *pp && *pp != ':' && *pp != ';')
222 *path++ = *pp++;
223 *path = 0;
224
225 /* interpret "" as curdir "." */
226 if (lp[0] == '\0') {
227 lp[0] = '.';
228 lp[1] = '\0';
229 }
230
231 if ((dd = opendir(lp)) != NULL) {
232 match = 0;
233 while ((dp = readdir(dd)) != NULL) {
234 tsod = *sodp;
235 if (elf_match_file(&tsod, dp->d_name,
236 dp->d_namlen)) {
237 /*
238 * When a match is found, tsod is
239 * updated with the major+minor found.
240 * This version is compared with the
241 * largest so far (kept in bsod),
242 * and saved if larger.
243 */
244 if (!match ||
245 tsod.sod_major == -1 ||
246 tsod.sod_major > bsod.sod_major ||
247 ((tsod.sod_major ==
248 bsod.sod_major) &&
249 tsod.sod_minor > bsod.sod_minor)) {
250 bsod = tsod;
251 match = 1;
252 len = strlcpy(
253 elf_hint_store, lp,
254 MAXPATHLEN);
255 if (lp[len-1] != '/') {
256 elf_hint_store[len] =
257 '/';
258 len++;
259 }
260 strlcpy(
261 &elf_hint_store[len],
262 dp->d_name,
263 MAXPATHLEN-len);
264 if (tsod.sod_major == -1)
265 break;
266 }
267 }
268 }
269 closedir(dd);
270 if (match) {
271 *sodp = bsod;
272 return (elf_hint_store);
273 }
274 }
275
276 if (*pp) /* Try curdir if ':' at end */
277 pp++;
278 else
279 pp = 0;
280 }
281 return NULL;
282 }
283
284 elf_object_t *
elf_tryload_shlib(const char * libname)285 elf_tryload_shlib(const char *libname)
286 {
287 struct elf_object *object;
288 object = elf_lookup_object(libname);
289
290 if (object == NULL)
291 object = load_file(libname, OBJTYPE_LIB);
292 if (object == NULL)
293 printf("tryload_shlib %s\n", libname);
294 return object;
295 }
296
297 /*
298 * elf_match_file()
299 *
300 * This fucntion determines if a given name matches what is specified
301 * in a struct sod. The major must match exactly, and the minor must
302 * be same or larger.
303 *
304 * sodp is updated with the minor if this matches.
305 */
306
307 int
elf_match_file(struct sod * sodp,char * name,int namelen)308 elf_match_file(struct sod *sodp, char *name, int namelen)
309 {
310 int match;
311 struct sod lsod;
312 char *lname;
313
314 lname = name;
315 if (sodp->sod_library) {
316 if (strncmp(name, "lib", 3))
317 return 0;
318 lname += 3;
319 }
320 if (strncmp(lname, (char *)sodp->sod_name,
321 strlen((char *)sodp->sod_name)))
322 return 0;
323
324 _dl_build_sod(name, &lsod);
325
326 match = 0;
327 if (strcmp((char *)lsod.sod_name, (char *)sodp->sod_name) == 0 &&
328 lsod.sod_library == sodp->sod_library &&
329 (sodp->sod_major == -1 || sodp->sod_major == lsod.sod_major) &&
330 (sodp->sod_minor == -1 || lsod.sod_minor >= sodp->sod_minor)) {
331 match = 1;
332
333 /* return version matched */
334 sodp->sod_major = lsod.sod_major;
335 sodp->sod_minor = lsod.sod_minor;
336 }
337 free((char *)lsod.sod_name);
338 return match;
339 }
340
341