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