1 /*	$OpenBSD: sod.c,v 1.22 2004/10/17 03:56:49 drahn Exp $	*/
2 
3 /*
4  * Copyright (c) 1993 Paul Kranenburg
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 Paul Kranenburg.
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 #include <sys/types.h>
35 #include <sys/syslimits.h>
36 #include <stdio.h>
37 #include <fcntl.h>
38 #include <nlist.h>
39 #include <link.h>
40 #include <limits.h>
41 #include <machine/exec.h>
42 #include <sys/mman.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 
47 #include "syscall.h"
48 #include "archdep.h"
49 #include "util.h"
50 #include "sod.h"
51 
52 int _dl_hinthash(char *cp, int vmajor, int vminor);
53 void _dl_maphints(void);
54 
55 /*
56  * Populate sod struct for dlopen's call to map_object
57  */
58 void
_dl_build_sod(const char * name,struct sod * sodp)59 _dl_build_sod(const char *name, struct sod *sodp)
60 {
61 	unsigned int	tuplet;
62 	int		major, minor;
63 	char		*realname, *tok, *etok, *cp;
64 
65 	/* default is an absolute or relative path */
66 	sodp->sod_name = (long)_dl_strdup(name);    /* strtok is destructive */
67 	sodp->sod_library = 0;
68 	sodp->sod_major = sodp->sod_minor = 0;
69 
70 	/* does it look like /^lib/ ? */
71 	if (_dl_strncmp((char *)sodp->sod_name, "lib", 3) != 0)
72 		goto backout;
73 
74 	/* is this a filename? */
75 	if (_dl_strchr((char *)sodp->sod_name, '/'))
76 		goto backout;
77 
78 	/* skip over 'lib' */
79 	cp = (char *)sodp->sod_name + 3;
80 
81 	realname = cp;
82 
83 	/* dot guardian */
84 	if ((_dl_strchr(cp, '.') == NULL) || (*(cp+_dl_strlen(cp)-1) == '.'))
85 		goto backout;
86 
87 	cp = _dl_strstr(cp, ".so");
88 	if (cp == NULL)
89 		goto backout;
90 
91 	/* default */
92 	major = minor = -1;
93 
94 	/* loop through name - parse skipping name */
95 	for (tuplet = 0; (tok = strsep(&cp, ".")) != NULL; tuplet++) {
96 		switch (tuplet) {
97 		case 0:
98 			/* empty tok, we already skipped to "\.so.*" */
99 			break;
100 		case 1:
101 			/* 'so' extension */
102 			break;
103 		case 2:
104 			/* major version extension */
105 			major = _dl_strtol(tok, &etok, 10);
106 			if (*tok == '\0' || *etok != '\0')
107 				goto backout;
108 			break;
109 		case 3:
110 			/* minor version extension */
111 			minor = _dl_strtol(tok, &etok, 10);
112 			if (*tok == '\0' || *etok != '\0')
113 				goto backout;
114 			break;
115 		/* if we get here, it must be weird */
116 		default:
117 			goto backout;
118 		}
119 	}
120 	if (realname == NULL)
121 		goto backout;
122 	cp = (char *)sodp->sod_name;
123 	sodp->sod_name = (long)_dl_strdup(realname);
124 	_dl_free(cp);
125 	sodp->sod_library = 1;
126 	sodp->sod_major = major;
127 	sodp->sod_minor = minor;
128 	return;
129 
130 backout:
131 	_dl_free((char *)sodp->sod_name);
132 	sodp->sod_name = (long)_dl_strdup(name);
133 }
134 
135 static struct hints_header	*hheader = NULL;
136 static struct hints_bucket	*hbuckets;
137 static char			*hstrtab;
138 char				*_dl_hint_search_path = NULL;
139 
140 #define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
141 
142 void
_dl_maphints(void)143 _dl_maphints(void)
144 {
145 	struct stat	sb;
146 	caddr_t		addr = MAP_FAILED;
147 	long		hsize = 0;
148 	int		hfd;
149 
150 	if ((hfd = _dl_open(_PATH_LD_HINTS, O_RDONLY)) < 0)
151 		goto bad_hints;
152 
153 	if (_dl_fstat(hfd, &sb) != 0 || !S_ISREG(sb.st_mode) ||
154 	    sb.st_size < sizeof(struct hints_header) || sb.st_size > LONG_MAX)
155 		goto bad_hints;
156 
157 	hsize = (long)sb.st_size;
158 	addr = (void *)_dl_mmap(0, hsize, PROT_READ, MAP_PRIVATE, hfd, 0);
159 	if (addr == MAP_FAILED)
160 		goto bad_hints;
161 
162 	hheader = (struct hints_header *)addr;
163 	if (HH_BADMAG(*hheader) || hheader->hh_ehints > hsize)
164 		goto bad_hints;
165 
166 	if (hheader->hh_version != LD_HINTS_VERSION_1 &&
167 	    hheader->hh_version != LD_HINTS_VERSION_2)
168 		goto bad_hints;
169 
170 	hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab);
171 	hstrtab = (char *)(addr + hheader->hh_strtab);
172 	if (hheader->hh_version >= LD_HINTS_VERSION_2)
173 		_dl_hint_search_path = hstrtab + hheader->hh_dirlist;
174 
175 	/* close the file descriptor, leaving the hints mapped */
176 	_dl_close(hfd);
177 
178 	return;
179 
180 bad_hints:
181 	if (addr != MAP_FAILED)
182 		_dl_munmap(addr, hsize);
183 	if (hfd != -1)
184 		_dl_close(hfd);
185 	hheader = (struct hints_header *)-1;
186 }
187 
188 char *
_dl_findhint(char * name,int major,int minor,char * preferred_path)189 _dl_findhint(char *name, int major, int minor, char *preferred_path)
190 {
191 	struct hints_bucket	*bp;
192 
193 	/*
194 	 * If not mapped, and we have not tried before, try to map the
195 	 * hints, if previous attempts failed hheader is -1 and we
196 	 * do not wish to retry it.
197 	 */
198 	if (hheader == NULL)
199 		_dl_maphints();
200 
201 	/* if it failed to map, return failure */
202 	if (!(HINTS_VALID))
203 		return NULL;
204 
205 	bp = hbuckets + (_dl_hinthash(name, major, minor) % hheader->hh_nbucket);
206 
207 	while (1) {
208 		/* Sanity check */
209 		if (bp->hi_namex >= hheader->hh_strtab_sz) {
210 			_dl_printf("Bad name index: %#x\n", bp->hi_namex);
211 			_dl_exit(7);
212 			break;
213 		}
214 		if (bp->hi_pathx >= hheader->hh_strtab_sz) {
215 			_dl_printf("Bad path index: %#x\n", bp->hi_pathx);
216 			_dl_exit(7);
217 			break;
218 		}
219 
220 		if (_dl_strcmp(name, hstrtab + bp->hi_namex) == 0) {
221 			/* It's `name', check version numbers */
222 			if (bp->hi_major == major &&
223 			    (bp->hi_ndewey < 2 || bp->hi_minor >= minor)) {
224 				if (preferred_path == NULL) {
225 					return hstrtab + bp->hi_pathx;
226 				} else {
227 					char *path = hstrtab + bp->hi_pathx;
228 					char *edir = _dl_strrchr(path, '/');
229 
230 					if ((_dl_strncmp(preferred_path, path,
231 					    (edir - path)) == 0) &&
232 					    (preferred_path[edir - path] == '\0'))
233 						return path;
234 				}
235 			}
236 		}
237 
238 		if (bp->hi_next == -1)
239 			break;
240 
241 		/* Move on to next in bucket */
242 		bp = &hbuckets[bp->hi_next];
243 	}
244 
245 	/* No hints available for name */
246 	return NULL;
247 }
248 
249 int
_dl_hinthash(char * cp,int vmajor,int vminor)250 _dl_hinthash(char *cp, int vmajor, int vminor)
251 {
252 	int	k = 0;
253 
254 	while (*cp)
255 		k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
256 
257 	k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
258 	if (hheader->hh_version == LD_HINTS_VERSION_1)
259 		k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
260 
261 	return k;
262 }
263