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