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
55 int _dl_hinthash(char *cp, int vmajor, int vminor);
56 void _dl_maphints(void);
57
58 /*
59 * Populate sod struct for dlopen's call to map_object
60 */
61 void
_dl_build_sod(const char * name,struct sod * sodp)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
137 backout:
138 free((char *)sodp->sod_name);
139 sodp->sod_name = (long)strdup(name);
140 if (sodp->sod_name == 0)
141 exit(7);
142 }
143
144 static struct hints_header *hheader = NULL;
145 static struct hints_bucket *hbuckets;
146 static char *hstrtab;
147 char **_dl_hint_search_path = NULL;
148
149 #define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
150
151 void
_dl_maphints(void)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
188 bad_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
196 char *
_dl_findhint(char * name,int major,int minor,char * preferred_path)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
260 int
_dl_hinthash(char * cp,int vmajor,int vminor)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