xref: /openbsd/libexec/ld.so/ldconfig/ldconfig.c (revision 274d7c50)
1 /*	$OpenBSD: ldconfig.c,v 1.38 2018/06/08 19:24:46 cheloha 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/mman.h>
36 
37 #include <dirent.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <ar.h>
42 #include <ranlib.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <limits.h>
48 
49 #include "ld.h"
50 
51 #undef major
52 #undef minor
53 
54 extern char			*__progname;
55 
56 int				verbose;
57 static int			nostd;
58 static int			justread;
59 int				merge;
60 static int			rescan;
61 static int			unconfig;
62 
63 struct shlib_list {
64 	/* Internal list of shared libraries found */
65 	char			*name;
66 	char			*path;
67 	int			dewey[MAXDEWEY];
68 	int			ndewey;
69 #define major dewey[0]
70 #define minor dewey[1]
71 	struct shlib_list	*next;
72 };
73 
74 static struct shlib_list	*shlib_head = NULL, **shlib_tail = &shlib_head;
75 static char			*dir_list;
76 
77 static void	enter(char *, char *, char *, int *, int);
78 static int	dodir(char *, int);
79 static int	buildhints(void);
80 static int	readhints(void);
81 static void	listhints(void);
82 
83 static void
84 usage(void)
85 {
86 	fprintf(stderr,
87 	    "usage: %s [-mRrsUv] [path ...]\n", __progname);
88 	exit(1);
89 }
90 
91 int
92 main(int argc, char *argv[])
93 {
94 	int i, c;
95 	int rval = 0;
96 
97 	if (pledge("stdio rpath wpath cpath tmppath fattr", NULL) == -1)
98 		err(1, "pledge");
99 
100 	while ((c = getopt(argc, argv, "DmPrRsSUv")) != -1) {
101 		switch (c) {
102 		case 'R':
103 			rescan = 1;
104 			break;
105 		case 'U':
106 			rescan = unconfig = 1;
107 			break;
108 		case 'm':
109 			merge = 1;
110 			break;
111 		case 'r':
112 			justread = 1;
113 			break;
114 		case 's':
115 			nostd = 1;
116 			break;
117 		case 'v':
118 			verbose = 1;
119 			break;
120 		default:
121 			usage();
122 			break;
123 		}
124 	}
125 
126 	if (unconfig && merge)
127 		errx(1, "cannot use -U with -m");
128 
129 	dir_list = xmalloc(1);
130 	*dir_list = '\0';
131 
132 	if (justread || merge || rescan) {
133 		if ((rval = readhints()) != 0)
134 			return rval;
135 		if (justread) {
136 			listhints();
137 			return 0;
138 		}
139 		add_search_path(dir_list);
140 		dir_list = xrealloc(dir_list, 1);
141 		*dir_list = '\0';
142 	} else if (!nostd)
143 		std_search_path();
144 
145 	if (unconfig) {
146 		if (optind < argc)
147 			for (i = optind; i < argc; i++)
148 				remove_search_dir(argv[i]);
149 		else {
150 			i = 0;
151 			while (i < n_search_dirs) {
152 				if (access(search_dirs[i], R_OK) < 0)
153 					remove_search_dir(search_dirs[i]);
154 				else
155 					i++;
156 			}
157 		}
158 	} else
159 		for (i = optind; i < argc; i++)
160 			add_search_dir(argv[i]);
161 
162 	for (i = 0; i < n_search_dirs; i++) {
163 		char *cp = concat(dir_list, *dir_list?":":"", search_dirs[i]);
164 
165 		free(dir_list);
166 		dir_list = cp;
167 		rval |= dodir(search_dirs[i], 0);
168 	}
169 
170 	rval |= buildhints();
171 
172 	return rval;
173 }
174 
175 int
176 dodir(char *dir, int silent)
177 {
178 	DIR		*dd;
179 	struct dirent	*dp;
180 	char		name[PATH_MAX];
181 	int		dewey[MAXDEWEY], ndewey;
182 
183 	if ((dd = opendir(dir)) == NULL) {
184 		if (!silent || errno != ENOENT)
185 			warn("%s", dir);
186 		return -1;
187 	}
188 
189 	while ((dp = readdir(dd)) != NULL) {
190 		size_t n;
191 		char *cp;
192 
193 		/* Check for `lib' prefix */
194 		if (dp->d_name[0] != 'l' ||
195 		    dp->d_name[1] != 'i' ||
196 		    dp->d_name[2] != 'b')
197 			continue;
198 
199 		/* Copy the entry minus prefix */
200 		(void)strlcpy(name, dp->d_name + 3, sizeof name);
201 		n = strlen(name);
202 		if (n < 4)
203 			continue;
204 
205 		/* Find ".so." in name */
206 		for (cp = name + n - 4; cp > name; --cp) {
207 			if (cp[0] == '.' &&
208 			    cp[1] == 's' &&
209 			    cp[2] == 'o' &&
210 			    cp[3] == '.')
211 				break;
212 		}
213 		if (cp <= name)
214 			continue;
215 
216 		*cp = '\0';
217 
218 		bzero((caddr_t)dewey, sizeof(dewey));
219 		ndewey = getdewey(dewey, cp + 3);
220 		if (ndewey > 0)
221 			enter(dir, dp->d_name, name, dewey, ndewey);
222 	}
223 	closedir(dd);
224 	return 0;
225 }
226 
227 static void
228 enter(char *dir, char *file, char *name, int dewey[], int ndewey)
229 {
230 	struct shlib_list	*shp;
231 
232 	for (shp = shlib_head; shp; shp = shp->next) {
233 		if (strcmp(name, shp->name) != 0 || major != shp->major)
234 			continue;
235 
236 		/* Name matches existing entry */
237 		if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) {
238 
239 			/* Update this entry with higher versioned lib */
240 			if (verbose)
241 				printf("Updating lib%s.%d.%d to %s/%s\n",
242 				    shp->name, shp->major, shp->minor,
243 				    dir, file);
244 
245 			free(shp->name);
246 			shp->name = xstrdup(name);
247 			free(shp->path);
248 			shp->path = concat(dir, "/", file);
249 			bcopy(dewey, shp->dewey, sizeof(shp->dewey));
250 			shp->ndewey = ndewey;
251 		}
252 		break;
253 	}
254 
255 	if (shp)
256 		/* Name exists: older version or just updated */
257 		return;
258 
259 	/* Allocate new list element */
260 	if (verbose)
261 		printf("Adding %s/%s\n", dir, file);
262 
263 	shp = (struct shlib_list *)xmalloc(sizeof *shp);
264 	shp->name = xstrdup(name);
265 	shp->path = concat(dir, "/", file);
266 	bcopy(dewey, shp->dewey, sizeof(shp->dewey));
267 	shp->ndewey = ndewey;
268 	shp->next = NULL;
269 
270 	*shlib_tail = shp;
271 	shlib_tail = &shp->next;
272 }
273 
274 
275 #if DEBUG
276 /* test */
277 #undef _PATH_LD_HINTS
278 #define _PATH_LD_HINTS		"./ld.so.hints"
279 #endif
280 
281 static int
282 hinthash(char *cp, int vmajor, int vminor)
283 {
284 	int	k = 0;
285 
286 	while (*cp)
287 		k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
288 
289 	k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
290 #if 0
291 	k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
292 #endif
293 
294 	return k;
295 }
296 
297 int
298 buildhints(void)
299 {
300 	int strtab_sz = 0, nhints = 0, fd = -1, i, ret = -1, str_index = 0;
301 	struct hints_bucket *blist;
302 	struct hints_header hdr;
303 	struct shlib_list *shp;
304 	char *strtab, *tmpfilenam;
305 	size_t n;
306 
307 	for (shp = shlib_head; shp; shp = shp->next) {
308 		strtab_sz += 1 + strlen(shp->name);
309 		strtab_sz += 1 + strlen(shp->path);
310 		nhints++;
311 	}
312 
313 	/* Fill hints file header */
314 	hdr.hh_magic = HH_MAGIC;
315 	hdr.hh_version = LD_HINTS_VERSION_2;
316 	hdr.hh_nbucket = 1 * nhints;
317 	n = hdr.hh_nbucket * sizeof(struct hints_bucket);
318 	hdr.hh_hashtab = sizeof(struct hints_header);
319 	hdr.hh_strtab = hdr.hh_hashtab + n;
320 	hdr.hh_dirlist = strtab_sz;
321 	strtab_sz += 1 + strlen(dir_list);
322 	hdr.hh_strtab_sz = strtab_sz;
323 	hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz;
324 
325 	if (verbose)
326 		printf("Totals: entries %d, buckets %ld, string size %d\n",
327 		    nhints, hdr.hh_nbucket, strtab_sz);
328 
329 	/* Allocate buckets and string table */
330 	blist = (struct hints_bucket *)xmalloc(n);
331 	bzero(blist, n);
332 	for (i = 0; i < hdr.hh_nbucket; i++)
333 		/* Empty all buckets */
334 		blist[i].hi_next = -1;
335 
336 	strtab = xmalloc(strtab_sz);
337 
338 	/* Enter all */
339 	for (shp = shlib_head; shp; shp = shp->next) {
340 		struct hints_bucket	*bp;
341 
342 		bp = blist + (hinthash(shp->name, shp->major, shp->minor) %
343 		    hdr.hh_nbucket);
344 
345 		if (bp->hi_pathx) {
346 			int	j;
347 
348 			for (j = 0; j < hdr.hh_nbucket; j++) {
349 				if (blist[j].hi_pathx == 0)
350 					break;
351 			}
352 			if (j == hdr.hh_nbucket) {
353 				warnx("Bummer!");
354 				goto out;
355 			}
356 			while (bp->hi_next != -1)
357 				bp = &blist[bp->hi_next];
358 			bp->hi_next = j;
359 			bp = blist + j;
360 		}
361 
362 		/* Insert strings in string table */
363 		bp->hi_namex = str_index;
364 		strlcpy(strtab + str_index, shp->name, strtab_sz - str_index);
365 		str_index += 1 + strlen(shp->name);
366 
367 		bp->hi_pathx = str_index;
368 		strlcpy(strtab + str_index, shp->path, strtab_sz - str_index);
369 		str_index += 1 + strlen(shp->path);
370 
371 		/* Copy versions */
372 		bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey));
373 		bp->hi_ndewey = shp->ndewey;
374 	}
375 
376 	/* Copy search directories */
377 	strlcpy(strtab + str_index, dir_list, strtab_sz - str_index);
378 	str_index += 1 + strlen(dir_list);
379 
380 	/* Sanity check */
381 	if (str_index != strtab_sz)
382 		errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz);
383 
384 	tmpfilenam = concat(_PATH_LD_HINTS, ".XXXXXXXXXX", "");
385 	if ((fd = mkstemp(tmpfilenam)) == -1) {
386 		warn("%s", tmpfilenam);
387 		goto out;
388 	}
389 	if (fchmod(fd, 0444) == -1) {
390 		warn("%s: failed to change mode", tmpfilenam);
391 		goto out;
392 	}
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