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