xref: /openbsd-src/libexec/ld.so/library.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: library.c,v 1.8 2001/08/06 15:09:58 drahn Exp $ */
2 
3 /*
4  * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed under OpenBSD by
17  *	Per Fogelstrom, Opsycon AB, Sweden.
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
22  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  */
34 
35 #define _DYN_LOADER
36 
37 #include <sys/types.h>
38 #include <sys/syslimits.h>
39 #include <fcntl.h>
40 #include <nlist.h>
41 #include <link.h>
42 #include <sys/mman.h>
43 
44 #include "syscall.h"
45 #include "archdep.h"
46 #include "resolve.h"
47 
48 #define PFLAGS(X) ((((X) & PF_R) ? PROT_READ : 0) | \
49 		   (((X) & PF_W) ? PROT_WRITE : 0) | \
50 		   (((X) & PF_X) ? PROT_EXEC : 0))
51 
52 elf_object_t * _dl_tryload_shlib(const char *libname, int type);
53 void _dl_build_sod(const char *name, struct sod *sodp);
54 char * _dl_findhint(char *name, int major, int minor, char *prefered_path);
55 
56 /*
57  *  Load a shared object. Search order is:
58  *	If the name contains a '/' use the name exactly as is.
59  *	Otherwise first check DT_RPATH paths,
60  *	then try the LD_LIBRARY_PATH specification and
61  *	last look in /usr/lib.
62  */
63 
64 elf_object_t *
65 _dl_load_shlib(const char *libname, elf_object_t *parent, int type)
66 {
67 	char	lp[PATH_MAX + 10];
68 	char	*path = lp;
69 	const char *pp;
70 	elf_object_t *object;
71 	struct sod sodp;
72 	char *hint;
73 
74 	if(_dl_strchr(libname, '/')) {
75 		object = _dl_tryload_shlib(libname, type);
76 		return(object);
77 	}
78 
79 	/*
80 	 *  No '/' in name. Scan the known places, LD_LIBRARY_PATH first.
81 	 */
82 	pp = _dl_libpath;
83 	while(pp) {
84 		const char *ln = libname;
85 
86 		path = lp;
87 		while(path < lp + PATH_MAX && *pp && *pp != ':' && *pp != ';') {
88 			*path++ = *pp++;
89 		}
90 		if(path != lp && *(path - 1) != '/') {	/* Insert '/' */
91 			*path++ = '/';
92 		}
93 		while(path < lp + PATH_MAX && (*path++ = *ln++)) {};
94 		if(path < lp + PATH_MAX) {
95 			object = _dl_tryload_shlib(lp, type);
96 			if(object) {
97 				return(object);
98 			}
99 		}
100 		if(*pp) {	/* Try curdir if ':' at end */
101 			pp++;
102 		}
103 		else {
104 			pp = 0;
105 		}
106 	}
107 
108 	/*
109 	 *  Check DT_RPATH.
110 	 */
111 	pp = parent->dyn.rpath;
112 	while(pp) {
113 		const char *ln = libname;
114 
115 		path = lp;
116 		while(path < lp + PATH_MAX && *pp && *pp != ':') {
117 			*path++ = *pp++;
118 		}
119 		if(*(path - 1) != '/') {/* Make sure '/' after dir path */
120 			*path++ = '/';
121 		}
122 		if(*pp) {		/* ':' if not end. skip over. */
123 			pp++;
124 		}
125 		while(path < lp + PATH_MAX && (*path++ = *ln++)) {};
126 		if(path < lp + PATH_MAX) {
127 			object = _dl_tryload_shlib(lp, type);
128 			if(object) {
129 				return(object);
130 			}
131 		}
132 		if(*pp) {	/* Try curdir if ':' at end */
133 			pp++;
134 		}
135 		else {
136 			pp = 0;
137 		}
138 	}
139 
140 	_dl_build_sod(libname, &sodp);
141 	if ((hint = _dl_findhint((char *)sodp.sod_name, sodp.sod_major,
142 		sodp.sod_minor, NULL)) != NULL)
143 	{
144 		object = _dl_tryload_shlib(hint, type);
145 		return(object);
146 
147 	}
148 
149 
150 	/*
151 	 *  Check '/usr/lib'
152 	 */
153 
154 	_dl_strcpy(lp, "/usr/lib/");
155 	path = lp + sizeof("/usr/lib/") - 1;
156 	while(path < lp + PATH_MAX && (*path++ = *libname++)) {};
157 	if(path < lp + PATH_MAX) {
158 		object = _dl_tryload_shlib(lp, type);
159 		if(object) {
160 			return(object);
161 		}
162 	}
163 	_dl_errno = DL_NOT_FOUND;
164 	return(0);
165 }
166 
167 void
168 _dl_load_list_free(load_list_t *load_list)
169 {
170 	load_list_t *next;
171 
172 	while(load_list != NULL) {
173 		next = load_list->next;
174 		_dl_free(load_list);
175 		load_list = next;
176 	}
177 }
178 
179 void
180 _dl_unload_shlib(elf_object_t *object)
181 {
182 	if(--object->refcount == 0) {
183 		_dl_load_list_free(object->load_list);
184 		_dl_munmap((void *)object->load_addr, object->load_size);
185 		_dl_remove_object(object);
186 	}
187 }
188 
189 
190 elf_object_t *
191 _dl_tryload_shlib(const char *libname, int type)
192 {
193 	int	libfile;
194 	int	i;
195 	char 	hbuf[4096];
196 	Elf_Ehdr *ehdr;
197 	Elf_Phdr *phdp;
198 	Elf_Dyn  *dynp = 0;
199 	Elf_Addr maxva = 0;
200 	Elf_Addr minva = 0x7fffffff;	/* XXX Correct for 64bit? */
201 	Elf_Addr libaddr;
202 	Elf_Addr loff;
203 	int	align = _dl_pagesz - 1;
204 	elf_object_t *object;
205 	load_list_t *next_load, *load_list = NULL;
206 
207 	object = _dl_lookup_object(libname);
208 	if(object) {
209 		object->refcount++;
210 		return(object);		/* Already loaded */
211 	}
212 
213 	libfile = _dl_open(libname, O_RDONLY);
214 	if(libfile < 0) {
215 		_dl_errno = DL_CANT_OPEN;
216 		return(0);
217 	}
218 
219 	_dl_read(libfile, hbuf, sizeof(hbuf));
220 	ehdr = (Elf_Ehdr *)hbuf;
221 	if(_dl_strncmp(ehdr->e_ident, ELFMAG, SELFMAG) ||
222 	   ehdr->e_type != ET_DYN || ehdr->e_machine != MACHID) {
223 		_dl_close(libfile);
224 		_dl_errno = DL_NOT_ELF;
225 		return(0);
226 	}
227 
228 	/*
229 	 *  Alright, we might have a winner!
230 	 *  Figure out how much VM space we need.
231 	 */
232 
233 	phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff);
234 	for(i = 0; i < ehdr->e_phnum; i++, phdp++) {
235 		switch(phdp->p_type) {
236 		case PT_LOAD:
237 			if(phdp->p_vaddr < minva) {
238 				minva = phdp->p_vaddr;
239 			}
240 			if(phdp->p_vaddr + phdp->p_memsz > maxva) {
241 				maxva = phdp->p_vaddr + phdp->p_memsz;
242 			}
243 			break;
244 
245 		case PT_DYNAMIC:
246 			dynp = (Elf_Dyn *)phdp->p_vaddr;
247 			break;
248 
249 		default:
250 			break;
251 		}
252 	}
253 	minva &= ~align;
254 	maxva = (maxva + align) & ~(align);
255 
256 	/*
257 	 *  We map the entire area to see that we can get the VM
258 	 *  space required. Map it unaccessible to start with.
259 	 */
260 	libaddr = (Elf_Addr)_dl_mmap(0, maxva - minva, PROT_NONE,
261 					MAP_PRIVATE|MAP_ANON, -1, 0);
262 	if(_dl_check_error(libaddr)) {
263 		_dl_printf("%s: rtld mmap failed mapping %s.\n",
264 				_dl_progname, libname);
265 		_dl_close(libfile);
266 		_dl_errno = DL_CANT_MMAP;
267 		return(0);
268 	}
269 
270 	loff = libaddr - minva;
271 	phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff);
272 
273 	for(i = 0; i < ehdr->e_phnum; i++, phdp++) {
274 		if(phdp->p_type == PT_LOAD) {
275 			int res;
276 			char *start = (char *)(phdp->p_vaddr & ~align) + loff;
277 			int size  = (phdp->p_vaddr & align) + phdp->p_filesz;
278 			res = _dl_mmap(start, size, PFLAGS(phdp->p_flags),
279 					MAP_FIXED|MAP_PRIVATE, libfile,
280 					phdp->p_offset & ~align);
281 			next_load = (load_list_t *)_dl_malloc(
282 					sizeof(load_list_t));
283 			next_load->next = load_list;
284 			load_list = next_load;
285 			next_load->start = start;
286 			next_load->size = size;
287 			next_load->prot = PFLAGS(phdp->p_flags);
288 			if(_dl_check_error(res)) {
289 				_dl_printf("%s: rtld mmap failed mapping %s.\n",
290 						_dl_progname, libname);
291 				_dl_close(libfile);
292 				_dl_errno = DL_CANT_MMAP;
293 				_dl_munmap((void *)libaddr, maxva - minva);
294 				_dl_load_list_free(load_list);
295 				return(0);
296 			}
297 			if (phdp->p_flags & PF_W) {
298 				if(size & align) {
299 					_dl_memset(start + size, 0,
300 						_dl_pagesz - (size & align));
301 				}
302 				start = start + ((size + align) & ~align);
303 				size  = size - (phdp->p_vaddr & align);
304 				size  = phdp->p_memsz - size;
305 				res = _dl_mmap(start, size,
306 					       PFLAGS(phdp->p_flags),
307 					       MAP_FIXED|MAP_PRIVATE|MAP_ANON,
308 						-1, 0);
309 				if(_dl_check_error(res)) {
310 					_dl_printf("%s: rtld mmap failed mapping %s.\n",
311 							_dl_progname, libname);
312 					_dl_close(libfile);
313 					_dl_errno = DL_CANT_MMAP;
314 					_dl_munmap((void *)libaddr, maxva - minva);
315 					_dl_load_list_free(load_list);
316 					return(0);
317 				}
318 			}
319 		}
320 	}
321 	_dl_close(libfile);
322 
323 	dynp = (Elf_Dyn *)((unsigned long)dynp + loff);
324 	object = _dl_add_object(libname, dynp, 0, type, libaddr, loff);
325 	if(object) {
326 		object->load_size = maxva - minva;	/*XXX*/
327 		object->load_list = load_list;
328 	} else {
329 		_dl_munmap((void *)libaddr, maxva - minva);
330 		_dl_load_list_free(load_list);
331 	}
332 	return(object);
333 }
334