xref: /openbsd/libexec/ld.so/ldconfig/ldconfig.c (revision fc61954a)
1 /*	$OpenBSD: ldconfig.c,v 1.36 2016/07/04 20:56:50 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 1993,1995 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 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/file.h>
36 #include <sys/time.h>
37 #include <sys/mman.h>
38 #include <sys/resource.h>
39 #include <ctype.h>
40 #include <dirent.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <ar.h>
45 #include <ranlib.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <limits.h>
51 
52 #include "ld.h"
53 
54 #undef major
55 #undef minor
56 
57 extern char			*__progname;
58 
59 int				verbose;
60 static int			nostd;
61 static int			justread;
62 int				merge;
63 static int			rescan;
64 static int			unconfig;
65 
66 struct shlib_list {
67 	/* Internal list of shared libraries found */
68 	char			*name;
69 	char			*path;
70 	int			dewey[MAXDEWEY];
71 	int			ndewey;
72 #define major dewey[0]
73 #define minor dewey[1]
74 	struct shlib_list	*next;
75 };
76 
77 static struct shlib_list	*shlib_head = NULL, **shlib_tail = &shlib_head;
78 static char			*dir_list;
79 
80 static void	enter(char *, char *, char *, int *, int);
81 static int	dodir(char *, int);
82 static int	buildhints(void);
83 static int	readhints(void);
84 static void	listhints(void);
85 
86 static void
87 usage(void)
88 {
89 	fprintf(stderr,
90 	    "usage: %s [-mRrsUv] [path ...]\n", __progname);
91 	exit(1);
92 }
93 
94 int
95 main(int argc, char *argv[])
96 {
97 	int i, c;
98 	int rval = 0;
99 
100 	if (pledge("stdio rpath wpath cpath tmppath fattr", NULL) == -1)
101 		err(1, "pledge");
102 
103 	while ((c = getopt(argc, argv, "DmPrRsSUv")) != -1) {
104 		switch (c) {
105 		case 'R':
106 			rescan = 1;
107 			break;
108 		case 'U':
109 			rescan = unconfig = 1;
110 			break;
111 		case 'm':
112 			merge = 1;
113 			break;
114 		case 'r':
115 			justread = 1;
116 			break;
117 		case 's':
118 			nostd = 1;
119 			break;
120 		case 'v':
121 			verbose = 1;
122 			break;
123 		default:
124 			usage();
125 			break;
126 		}
127 	}
128 
129 	if (unconfig && merge)
130 		errx(1, "cannot use -U with -m");
131 
132 	dir_list = xmalloc(1);
133 	*dir_list = '\0';
134 
135 	if (justread || merge || rescan) {
136 		if ((rval = readhints()) != 0)
137 			return rval;
138 		if (justread) {
139 			listhints();
140 			return 0;
141 		}
142 		add_search_path(dir_list);
143 		dir_list = xrealloc(dir_list, 1);
144 		*dir_list = '\0';
145 	} else if (!nostd)
146 		std_search_path();
147 
148 	if (unconfig) {
149 		if (optind < argc)
150 			for (i = optind; i < argc; i++)
151 				remove_search_dir(argv[i]);
152 		else {
153 			i = 0;
154 			while (i < n_search_dirs) {
155 				if (access(search_dirs[i], R_OK) < 0)
156 					remove_search_dir(search_dirs[i]);
157 				else
158 					i++;
159 			}
160 		}
161 	} else
162 		for (i = optind; i < argc; i++)
163 			add_search_dir(argv[i]);
164 
165 	for (i = 0; i < n_search_dirs; i++) {
166 		char *cp = concat(dir_list, *dir_list?":":"", search_dirs[i]);
167 
168 		free(dir_list);
169 		dir_list = cp;
170 		rval |= dodir(search_dirs[i], 0);
171 	}
172 
173 	rval |= buildhints();
174 
175 	return rval;
176 }
177 
178 int
179 dodir(char *dir, int silent)
180 {
181 	DIR		*dd;
182 	struct dirent	*dp;
183 	char		name[PATH_MAX];
184 	int		dewey[MAXDEWEY], ndewey;
185 
186 	if ((dd = opendir(dir)) == NULL) {
187 		if (!silent || errno != ENOENT)
188 			warn("%s", dir);
189 		return -1;
190 	}
191 
192 	while ((dp = readdir(dd)) != NULL) {
193 		size_t n;
194 		char *cp;
195 
196 		/* Check for `lib' prefix */
197 		if (dp->d_name[0] != 'l' ||
198 		    dp->d_name[1] != 'i' ||
199 		    dp->d_name[2] != 'b')
200 			continue;
201 
202 		/* Copy the entry minus prefix */
203 		(void)strlcpy(name, dp->d_name + 3, sizeof name);
204 		n = strlen(name);
205 		if (n < 4)
206 			continue;
207 
208 		/* Find ".so." in name */
209 		for (cp = name + n - 4; cp > name; --cp) {
210 			if (cp[0] == '.' &&
211 			    cp[1] == 's' &&
212 			    cp[2] == 'o' &&
213 			    cp[3] == '.')
214 				break;
215 		}
216 		if (cp <= name)
217 			continue;
218 
219 		*cp = '\0';
220 
221 		bzero((caddr_t)dewey, sizeof(dewey));
222 		ndewey = getdewey(dewey, cp + 3);
223 		if (ndewey > 0)
224 			enter(dir, dp->d_name, name, dewey, ndewey);
225 	}
226 	closedir(dd);
227 	return 0;
228 }
229 
230 static void
231 enter(char *dir, char *file, char *name, int dewey[], int ndewey)
232 {
233 	struct shlib_list	*shp;
234 
235 	for (shp = shlib_head; shp; shp = shp->next) {
236 		if (strcmp(name, shp->name) != 0 || major != shp->major)
237 			continue;
238 
239 		/* Name matches existing entry */
240 		if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) {
241 
242 			/* Update this entry with higher versioned lib */
243 			if (verbose)
244 				printf("Updating lib%s.%d.%d to %s/%s\n",
245 				    shp->name, shp->major, shp->minor,
246 				    dir, file);
247 
248 			free(shp->name);
249 			shp->name = xstrdup(name);
250 			free(shp->path);
251 			shp->path = concat(dir, "/", file);
252 			bcopy(dewey, shp->dewey, sizeof(shp->dewey));
253 			shp->ndewey = ndewey;
254 		}
255 		break;
256 	}
257 
258 	if (shp)
259 		/* Name exists: older version or just updated */
260 		return;
261 
262 	/* Allocate new list element */
263 	if (verbose)
264 		printf("Adding %s/%s\n", dir, file);
265 
266 	shp = (struct shlib_list *)xmalloc(sizeof *shp);
267 	shp->name = xstrdup(name);
268 	shp->path = concat(dir, "/", file);
269 	bcopy(dewey, shp->dewey, sizeof(shp->dewey));
270 	shp->ndewey = ndewey;
271 	shp->next = NULL;
272 
273 	*shlib_tail = shp;
274 	shlib_tail = &shp->next;
275 }
276 
277 
278 #if DEBUG
279 /* test */
280 #undef _PATH_LD_HINTS
281 #define _PATH_LD_HINTS		"./ld.so.hints"
282 #endif
283 
284 static int
285 hinthash(char *cp, int vmajor, int vminor)
286 {
287 	int	k = 0;
288 
289 	while (*cp)
290 		k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
291 
292 	k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
293 #if 0
294 	k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
295 #endif
296 
297 	return k;
298 }
299 
300 int
301 buildhints(void)
302 {
303 	int strtab_sz = 0, nhints = 0, fd = -1, i, ret = -1, str_index = 0;
304 	struct hints_bucket *blist;
305 	struct hints_header hdr;
306 	struct shlib_list *shp;
307 	char *strtab, *tmpfilenam;
308 	size_t n;
309 
310 	for (shp = shlib_head; shp; shp = shp->next) {
311 		strtab_sz += 1 + strlen(shp->name);
312 		strtab_sz += 1 + strlen(shp->path);
313 		nhints++;
314 	}
315 
316 	/* Fill hints file header */
317 	hdr.hh_magic = HH_MAGIC;
318 	hdr.hh_version = LD_HINTS_VERSION_2;
319 	hdr.hh_nbucket = 1 * nhints;
320 	n = hdr.hh_nbucket * sizeof(struct hints_bucket);
321 	hdr.hh_hashtab = sizeof(struct hints_header);
322 	hdr.hh_strtab = hdr.hh_hashtab + n;
323 	hdr.hh_dirlist = strtab_sz;
324 	strtab_sz += 1 + strlen(dir_list);
325 	hdr.hh_strtab_sz = strtab_sz;
326 	hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz;
327 
328 	if (verbose)
329 		printf("Totals: entries %d, buckets %ld, string size %d\n",
330 		    nhints, hdr.hh_nbucket, strtab_sz);
331 
332 	/* Allocate buckets and string table */
333 	blist = (struct hints_bucket *)xmalloc(n);
334 	bzero(blist, n);
335 	for (i = 0; i < hdr.hh_nbucket; i++)
336 		/* Empty all buckets */
337 		blist[i].hi_next = -1;
338 
339 	strtab = xmalloc(strtab_sz);
340 
341 	/* Enter all */
342 	for (shp = shlib_head; shp; shp = shp->next) {
343 		struct hints_bucket	*bp;
344 
345 		bp = blist + (hinthash(shp->name, shp->major, shp->minor) %
346 		    hdr.hh_nbucket);
347 
348 		if (bp->hi_pathx) {
349 			int	j;
350 
351 			for (j = 0; j < hdr.hh_nbucket; j++) {
352 				if (blist[j].hi_pathx == 0)
353 					break;
354 			}
355 			if (j == hdr.hh_nbucket) {
356 				warnx("Bummer!");
357 				goto out;
358 			}
359 			while (bp->hi_next != -1)
360 				bp = &blist[bp->hi_next];
361 			bp->hi_next = j;
362 			bp = blist + j;
363 		}
364 
365 		/* Insert strings in string table */
366 		bp->hi_namex = str_index;
367 		strlcpy(strtab + str_index, shp->name, strtab_sz - str_index);
368 		str_index += 1 + strlen(shp->name);
369 
370 		bp->hi_pathx = str_index;
371 		strlcpy(strtab + str_index, shp->path, strtab_sz - str_index);
372 		str_index += 1 + strlen(shp->path);
373 
374 		/* Copy versions */
375 		bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey));
376 		bp->hi_ndewey = shp->ndewey;
377 	}
378 
379 	/* Copy search directories */
380 	strlcpy(strtab + str_index, dir_list, strtab_sz - str_index);
381 	str_index += 1 + strlen(dir_list);
382 
383 	/* Sanity check */
384 	if (str_index != strtab_sz)
385 		errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz);
386 
387 	tmpfilenam = concat(_PATH_LD_HINTS, ".XXXXXXXXXX", "");
388 	if ((fd = mkstemp(tmpfilenam)) == -1) {
389 		warn("%s", tmpfilenam);
390 		goto out;
391 	}
392 	fchmod(fd, 0444);
393 
394 	if (write(fd, &hdr, sizeof(struct hints_header)) !=
395 	    sizeof(struct hints_header)) {
396 		warn("%s", _PATH_LD_HINTS);
397 		goto out;
398 	}
399 	if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) !=
400 	    hdr.hh_nbucket * sizeof(struct hints_bucket)) {
401 		warn("%s", _PATH_LD_HINTS);
402 		goto out;
403 	}
404 	if (write(fd, strtab, strtab_sz) != strtab_sz) {
405 		warn("%s", _PATH_LD_HINTS);
406 		goto out;
407 	}
408 
409 	if (rename(tmpfilenam, _PATH_LD_HINTS) != 0) {
410 		warn("%s", _PATH_LD_HINTS);
411 		goto out;
412 	}
413 
414 	ret = 0;
415 out:
416 	if (fd != -1)
417 		close(fd);
418 	free(blist);
419 	free(strtab);
420 	return (ret);
421 }
422 
423 static int
424 readhints(void)
425 {
426 	struct stat sb;
427 	struct hints_bucket *blist;
428 	struct hints_header *hdr;
429 	struct shlib_list *shp;
430 	caddr_t addr;
431 	char *strtab;
432 	long msize;
433 	int fd, i;
434 
435 	if ((fd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) {
436 		warn("%s", _PATH_LD_HINTS);
437 		return -1;
438 	}
439 	if (fstat(fd, &sb) != 0 || !S_ISREG(sb.st_mode) ||
440 	    sb.st_size < sizeof(struct hints_header) || sb.st_size > LONG_MAX) {
441 		warn("%s", _PATH_LD_HINTS);
442 		return -1;
443 	}
444 
445 	msize = (long)sb.st_size;
446 	addr = mmap(0, msize, PROT_READ, MAP_PRIVATE, fd, 0);
447 
448 	if (addr == MAP_FAILED) {
449 		warn("%s", _PATH_LD_HINTS);
450 		return -1;
451 	}
452 
453 	hdr = (struct hints_header *)addr;
454 	if (HH_BADMAG(*hdr)) {
455 		warnx("%s: Bad magic: %lo",
456 		    _PATH_LD_HINTS, hdr->hh_magic);
457 		return -1;
458 	}
459 
460 	if (hdr->hh_ehints > msize) {
461 		warnx("%s: hintsize greater than filesize: 0x%lx > 0x%lx ",
462 		    _PATH_LD_HINTS, hdr->hh_ehints, msize);
463 		    return -1;
464 	}
465 
466 	if (hdr->hh_version != LD_HINTS_VERSION_2) {
467 		warnx("Unsupported version: %ld", hdr->hh_version);
468 		return -1;
469 	}
470 
471 	close(fd);
472 
473 	blist = (struct hints_bucket *)(addr + hdr->hh_hashtab);
474 	strtab = (char *)(addr + hdr->hh_strtab);
475 
476 	dir_list = xstrdup(strtab + hdr->hh_dirlist);
477 
478 	if (rescan)
479 		return (0);
480 
481 	for (i = 0; i < hdr->hh_nbucket; i++) {
482 		struct hints_bucket	*bp = &blist[i];
483 
484 		/* Sanity check */
485 		if (bp->hi_namex >= hdr->hh_strtab_sz) {
486 			warnx("Bad name index: %#x", bp->hi_namex);
487 			return -1;
488 		}
489 		if (bp->hi_pathx >= hdr->hh_strtab_sz) {
490 			warnx("Bad path index: %#x", bp->hi_pathx);
491 			return -1;
492 		}
493 
494 		/* Allocate new list element */
495 		shp = (struct shlib_list *)xmalloc(sizeof *shp);
496 		shp->name = xstrdup(strtab + bp->hi_namex);
497 		shp->path = xstrdup(strtab + bp->hi_pathx);
498 		bcopy(bp->hi_dewey, shp->dewey, sizeof(shp->dewey));
499 		shp->ndewey = bp->hi_ndewey;
500 		shp->next = NULL;
501 
502 		*shlib_tail = shp;
503 		shlib_tail = &shp->next;
504 	}
505 	return 0;
506 }
507 
508 static void
509 listhints(void)
510 {
511 	struct shlib_list *shp;
512 	int i;
513 
514 	printf("%s:\n", _PATH_LD_HINTS);
515 	printf("\tsearch directories: %s\n", dir_list);
516 
517 	for (i = 0, shp = shlib_head; shp; i++, shp = shp->next)
518 		printf("\t%d:-l%s.%d.%d => %s\n",
519 		    i, shp->name, shp->major, shp->minor, shp->path);
520 }
521