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