1 /* $OpenBSD: ldconfig.c,v 1.39 2021/10/24 21:24:20 deraadt 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
usage(void)84 usage(void)
85 {
86 fprintf(stderr,
87 "usage: %s [-mRrsUv] [path ...]\n", __progname);
88 exit(1);
89 }
90
91 int
main(int argc,char * argv[])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
dodir(char * dir,int silent)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
enter(char * dir,char * file,char * name,int dewey[],int ndewey)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
hinthash(char * cp,int vmajor,int vminor)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
buildhints(void)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
readhints(void)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)) == -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
listhints(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