sod.c revision 1.2
1/*      $OpenBSD: sod.c,v 1.2 2001/05/11 16:21:11 art Exp $       */
2/*
3 * Copyright (c) 1993 Paul Kranenburg
4 * All rights reserved.
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 by Paul Kranenburg.
17 * 4. The name of the author may not be used to endorse or promote products
18 *    derived from this software without specific prior written permission
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 */
32
33#include <sys/types.h>
34#include <sys/syslimits.h>
35#include <stdio.h>
36#include <fcntl.h>
37#include <nlist.h>
38#include <link.h>
39#include <machine/exec.h>
40#include <sys/mman.h>
41#include <string.h>
42#include <stdlib.h>
43#include <unistd.h>
44#include <syscall.h>
45
46#define PAGSIZ	__LDPGSZ
47char * _dl_strdup(const char *);
48void _dl_free(void *);
49int _dl_hinthash(char *cp, int vmajor, int vminor);
50
51/*
52 * Populate sod struct for dlopen's call to map_object
53 */
54void
55_dl_build_sod(name, sodp)
56	const char	*name;
57	struct sod	*sodp;
58{
59	unsigned int	tuplet;
60	int		major, minor;
61	char		*realname, *tok, *etok, *cp;
62
63	/* default is an absolute or relative path */
64	sodp->sod_name = (long)_dl_strdup(name);    /* strtok is destructive */
65	sodp->sod_library = 0;
66	sodp->sod_major = sodp->sod_minor = 0;
67
68	/* does it look like /^lib/ ? */
69	if (strncmp((char *)sodp->sod_name, "lib", 3) != 0)
70		return;
71
72	/* is this a filename? */
73	if (strchr((char *)sodp->sod_name, '/'))
74		return;
75
76	/* skip over 'lib' */
77	cp = (char *)sodp->sod_name + 3;
78
79	/* dot guardian */
80	if ((strchr(cp, '.') == NULL) || (*(cp+strlen(cp)-1) == '.'))
81		return;
82
83	/* default */
84	major = minor = -1;
85
86	/* loop through name - parse skipping name */
87	for (tuplet = 0; (tok = strsep(&cp, ".")) != NULL; tuplet++) {
88		switch (tuplet) {
89		case 0:
90			/* removed 'lib' and extensions from name */
91			realname = tok;
92			break;
93		case 1:
94			/* 'so' extension */
95			if (strcmp(tok, "so") != 0)
96				goto backout;
97			break;
98		case 2:
99			/* major version extension */
100			major = strtol(tok, &etok, 10);
101			if (*tok == '\0' || *etok != '\0')
102				goto backout;
103			break;
104		case 3:
105			/* minor version extension */
106			minor = strtol(tok, &etok, 10);
107			if (*tok == '\0' || *etok != '\0')
108				goto backout;
109			break;
110		/* if we get here, it must be weird */
111		default:
112			goto backout;
113		}
114	}
115	cp = (char *)sodp->sod_name;
116	sodp->sod_name = (long)_dl_strdup(realname);
117	_dl_free(cp);
118	sodp->sod_library = 1;
119	sodp->sod_major = major;
120	sodp->sod_minor = minor;
121	return;
122
123backout:
124	_dl_free((char *)sodp->sod_name);
125	sodp->sod_name = (long)_dl_strdup(name);
126}
127
128static int			hfd;
129static long			hsize;
130static struct hints_header	*hheader = NULL;
131static struct hints_bucket	*hbuckets;
132static char			*hstrtab;
133static char			*hint_search_path = "";
134
135#define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
136
137void
138_dl_maphints()
139{
140	caddr_t		addr;
141
142	if ((hfd = _dl_open(_PATH_LD_HINTS, O_RDONLY)) == -1) {
143		hheader = (struct hints_header *)-1;
144		return;
145	}
146
147	hsize = PAGSIZ;
148	addr = (void *) _dl_mmap(0, hsize, PROT_READ, MAP_COPY, hfd, 0);
149
150	if (addr == MAP_FAILED) {
151		_dl_close(hfd);
152		hheader = (struct hints_header *)-1;
153		return;
154	}
155
156	hheader = (struct hints_header *)addr;
157	if (HH_BADMAG(*hheader)) {
158		_dl_munmap(addr, hsize);
159		_dl_close(hfd);
160		hheader = (struct hints_header *)-1;
161		return;
162	}
163
164	if (hheader->hh_version != LD_HINTS_VERSION_1 &&
165	    hheader->hh_version != LD_HINTS_VERSION_2) {
166		_dl_munmap(addr, hsize);
167		_dl_close(hfd);
168		hheader = (struct hints_header *)-1;
169		return;
170	}
171
172	if (hheader->hh_ehints > hsize) {
173		if ((caddr_t)_dl_mmap(addr+hsize, hheader->hh_ehints - hsize,
174				PROT_READ, MAP_COPY|MAP_FIXED,
175				hfd, hsize) != (caddr_t)(addr+hsize)) {
176
177			_dl_munmap((caddr_t)hheader, hsize);
178			_dl_close(hfd);
179			hheader = (struct hints_header *)-1;
180			return;
181		}
182	}
183
184	hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab);
185	hstrtab = (char *)(addr + hheader->hh_strtab);
186	if (hheader->hh_version >= LD_HINTS_VERSION_2)
187		hint_search_path = hstrtab + hheader->hh_dirlist;
188}
189
190void
191_dl_unmaphints()
192{
193
194	if (HINTS_VALID) {
195		_dl_munmap((caddr_t)hheader, hsize);
196		_dl_close(hfd);
197		hheader = (void *)-1;
198	}
199}
200
201char *
202_dl_findhint(name, major, minor, prefered_path)
203	char	*name;
204	int	major, minor;
205	char	*prefered_path;
206{
207	struct hints_bucket	*bp;
208
209	/* If not mapped, and we have not tried before, try to map the
210	 * hints, if previous attempts failed hheader is -1 and we
211	 * do not wish to retry it.
212	 */
213	if (hheader == NULL) {
214		_dl_maphints();
215	}
216
217	/* if it failed to map, return failure */
218	if (!(HINTS_VALID)) {
219		return NULL;
220	}
221
222	bp = hbuckets + (_dl_hinthash(name, major, minor) % hheader->hh_nbucket);
223
224	while (1) {
225		/* Sanity check */
226		if (bp->hi_namex >= hheader->hh_strtab_sz) {
227			_dl_printf("Bad name index: %#x\n", bp->hi_namex);
228			_dl_exit(7);
229			break;
230		}
231		if (bp->hi_pathx >= hheader->hh_strtab_sz) {
232			_dl_printf("Bad path index: %#x\n", bp->hi_pathx);
233			_dl_exit(7);
234			break;
235		}
236
237		if (strcmp(name, hstrtab + bp->hi_namex) == 0) {
238			/* It's `name', check version numbers */
239			if (bp->hi_major == major &&
240				(bp->hi_ndewey < 2 || bp->hi_minor >= minor)) {
241					if (prefered_path == NULL ||
242					    strncmp(prefered_path,
243						hstrtab + bp->hi_pathx,
244						strlen(prefered_path)) == 0) {
245						return hstrtab + bp->hi_pathx;
246					}
247			}
248		}
249
250		if (bp->hi_next == -1)
251			break;
252
253		/* Move on to next in bucket */
254		bp = &hbuckets[bp->hi_next];
255	}
256
257	/* No hints available for name */
258	return NULL;
259}
260int
261_dl_hinthash(cp, vmajor, vminor)
262	char	*cp;
263	int	vmajor, vminor;
264{
265	int	k = 0;
266
267	while (*cp)
268		k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
269
270	k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
271	if (hheader->hh_version == LD_HINTS_VERSION_1)
272		k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
273
274	return k;
275}
276