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