xref: /freebsd/sbin/ldconfig/ldconfig.c (revision a316b26e50bbed7cf655fbba726ab87d8ab7599d)
1 /*
2  * Copyright (c) 1993 Paul Kranenburg
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Paul Kranenburg.
16  * 4. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  *	$Id: ldconfig.c,v 1.7 1994/06/15 22:40:56 rich Exp $
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 
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 
76 static void	enter __P((char *, char *, char *, int *, int));
77 static int	dodir __P((char *, int));
78 static int	build_hints __P((void));
79 static int	listhints __P((void));
80 
81 int
82 main(argc, argv)
83 int	argc;
84 char	*argv[];
85 {
86 	int		i, c;
87 	int		rval = 0;
88 
89 	while ((c = getopt(argc, argv, "rsv")) != EOF) {
90 		switch (c) {
91 		case 'v':
92 			verbose = 1;
93 			break;
94 		case 's':
95 			nostd = 1;
96 			break;
97 		case 'r':
98 			justread = 1;
99 			break;
100 		default:
101 			fprintf(stderr, "Usage: %s [-r][-s][-v][dir ...]\n",
102 				__progname);
103 			exit(1);
104 			break;
105 		}
106 	}
107 
108 	if (justread)
109 		return listhints();
110 
111 	if (!nostd)
112 		std_search_path();
113 
114 	for (i = 0; i < n_search_dirs; i++)
115 		rval |= dodir(search_dirs[i], 1);
116 
117 	for (i = optind; i < argc; i++)
118 		rval |= dodir(argv[i], 0);
119 
120 	rval |= build_hints();
121 
122 	return rval;
123 }
124 
125 int
126 dodir(dir, silent)
127 char	*dir;
128 int	silent;
129 {
130 	DIR		*dd;
131 	struct dirent	*dp;
132 	char		name[MAXPATHLEN], rest[MAXPATHLEN];
133 	int		dewey[MAXDEWEY], ndewey;
134 
135 	if ((dd = opendir(dir)) == NULL) {
136 		if (!silent || errno != ENOENT)
137 			perror(dir);
138 		return -1;
139 	}
140 
141 	while ((dp = readdir(dd)) != NULL) {
142 		int	n;
143 
144 		name[0] = rest[0] = '\0';
145 
146 		n = sscanf(dp->d_name, "lib%[^.].so.%s",
147 					name, rest);
148 
149 		if (n < 2 || rest[0] == '\0')
150 			continue;
151 
152 		ndewey = getdewey(dewey, rest);
153 		enter(dir, dp->d_name, name, dewey, ndewey);
154 	}
155 
156 	return 0;
157 }
158 
159 static void
160 enter(dir, file, name, dewey, ndewey)
161 char	*dir, *file, *name;
162 int	dewey[], ndewey;
163 {
164 	struct shlib_list	*shp;
165 
166 	for (shp = shlib_head; shp; shp = shp->next) {
167 		if (strcmp(name, shp->name) != 0 || major != shp->major)
168 			continue;
169 
170 		/* Name matches existing entry */
171 		if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) {
172 
173 			/* Update this entry with higher versioned lib */
174 			if (verbose)
175 				printf("Updating lib%s.%d.%d to %s/%s\n",
176 					shp->name, shp->major, shp->minor,
177 					dir, file);
178 
179 			free(shp->name);
180 			shp->name = strdup(name);
181 			free(shp->path);
182 			shp->path = concat(dir, "/", file);
183 			bcopy(dewey, shp->dewey, sizeof(shp->dewey));
184 			shp->ndewey = ndewey;
185 		}
186 		break;
187 	}
188 
189 	if (shp)
190 		/* Name exists: older version or just updated */
191 		return;
192 
193 	/* Allocate new list element */
194 	if (verbose)
195 		printf("Adding %s/%s\n", dir, file);
196 
197 	shp = (struct shlib_list *)xmalloc(sizeof *shp);
198 	shp->name = strdup(name);
199 	shp->path = concat(dir, "/", file);
200 	bcopy(dewey, shp->dewey, MAXDEWEY);
201 	shp->ndewey = ndewey;
202 	shp->next = NULL;
203 
204 	*shlib_tail = shp;
205 	shlib_tail = &shp->next;
206 }
207 
208 
209 #if DEBUG
210 /* test */
211 #undef _PATH_LD_HINTS
212 #define _PATH_LD_HINTS		"./ld.so.hints"
213 #endif
214 
215 int
216 hinthash(cp, vmajor, vminor)
217 char	*cp;
218 int	vmajor, vminor;
219 {
220 	int	k = 0;
221 
222 	while (*cp)
223 		k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
224 
225 	k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
226 	k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
227 
228 	return k;
229 }
230 
231 int
232 build_hints()
233 {
234 	struct hints_header	hdr;
235 	struct hints_bucket	*blist;
236 	struct shlib_list	*shp;
237 	char			*strtab;
238 	int			i, n, str_index = 0;
239 	int			strtab_sz = 0;	/* Total length of strings */
240 	int			nhints = 0;	/* Total number of hints */
241 	int			fd;
242 	char			*tmpfile;
243 
244 	for (shp = shlib_head; shp; shp = shp->next) {
245 		strtab_sz += 1 + strlen(shp->name);
246 		strtab_sz += 1 + strlen(shp->path);
247 		nhints++;
248 	}
249 
250 	/* Fill hints file header */
251 	hdr.hh_magic = HH_MAGIC;
252 	hdr.hh_version = LD_HINTS_VERSION_1;
253 	hdr.hh_nbucket = 1 * nhints;
254 	n = hdr.hh_nbucket * sizeof(struct hints_bucket);
255 	hdr.hh_hashtab = sizeof(struct hints_header);
256 	hdr.hh_strtab = hdr.hh_hashtab + n;
257 	hdr.hh_strtab_sz = strtab_sz;
258 	hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz;
259 
260 	if (verbose)
261 		printf("Totals: entries %d, buckets %d, string size %d\n",
262 					nhints, hdr.hh_nbucket, strtab_sz);
263 
264 	/* Allocate buckets and string table */
265 	blist = (struct hints_bucket *)xmalloc(n);
266 	bzero((char *)blist, n);
267 	for (i = 0; i < hdr.hh_nbucket; i++)
268 		/* Empty all buckets */
269 		blist[i].hi_next = -1;
270 
271 	strtab = (char *)xmalloc(strtab_sz);
272 
273 	/* Enter all */
274 	for (shp = shlib_head; shp; shp = shp->next) {
275 		struct hints_bucket	*bp;
276 
277 		bp = blist +
278 		  (hinthash(shp->name, shp->major, shp->minor) % hdr.hh_nbucket);
279 
280 		if (bp->hi_pathx) {
281 			int	i;
282 
283 			for (i = 0; i < hdr.hh_nbucket; i++) {
284 				if (blist[i].hi_pathx == 0)
285 					break;
286 			}
287 			if (i == hdr.hh_nbucket) {
288 				fprintf(stderr, "Bummer!\n");
289 				return -1;
290 			}
291 			while (bp->hi_next != -1)
292 				bp = &blist[bp->hi_next];
293 			bp->hi_next = i;
294 			bp = blist + i;
295 		}
296 
297 		/* Insert strings in string table */
298 		bp->hi_namex = str_index;
299 		strcpy(strtab + str_index, shp->name);
300 		str_index += 1 + strlen(shp->name);
301 
302 		bp->hi_pathx = str_index;
303 		strcpy(strtab + str_index, shp->path);
304 		str_index += 1 + strlen(shp->path);
305 
306 		/* Copy versions */
307 		bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey));
308 		bp->hi_ndewey = shp->ndewey;
309 	}
310 
311 	tmpfile = concat(_PATH_LD_HINTS, "+", "");
312 	if ((fd = open(tmpfile, O_RDWR|O_CREAT|O_TRUNC, 0444)) == -1) {
313 		perror(_PATH_LD_HINTS);
314 		return -1;
315 	}
316 
317 	if (write(fd, &hdr, sizeof(struct hints_header)) !=
318 						sizeof(struct hints_header)) {
319 		perror(_PATH_LD_HINTS);
320 		return -1;
321 	}
322 	if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) !=
323 				hdr.hh_nbucket * sizeof(struct hints_bucket)) {
324 		perror(_PATH_LD_HINTS);
325 		return -1;
326 	}
327 	if (write(fd, strtab, strtab_sz) != strtab_sz) {
328 		perror(_PATH_LD_HINTS);
329 		return -1;
330 	}
331 	if (close(fd) != 0) {
332 		perror(_PATH_LD_HINTS);
333 		return -1;
334 	}
335 
336 	/* Install it */
337 	if (unlink(_PATH_LD_HINTS) != 0 && errno != ENOENT) {
338 		perror(_PATH_LD_HINTS);
339 		return -1;
340 	}
341 
342 	if (rename(tmpfile, _PATH_LD_HINTS) != 0) {
343 		perror(_PATH_LD_HINTS);
344 		return -1;
345 	}
346 
347 	return 0;
348 }
349 
350 static int
351 listhints()
352 {
353 	int			fd;
354 	caddr_t			addr;
355 	long			msize;
356 	struct hints_header	*hdr;
357 	struct hints_bucket	*blist;
358 	char			*strtab;
359 	int			i;
360 
361 	if ((fd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) {
362 		perror(_PATH_LD_HINTS);
363 		return -1;
364 	}
365 
366 	msize = PAGSIZ;
367 	addr = mmap(0, msize, PROT_READ, MAP_COPY, fd, 0);
368 
369 	if (addr == (caddr_t)-1) {
370 		perror(_PATH_LD_HINTS);
371 		return -1;
372 	}
373 
374 	hdr = (struct hints_header *)addr;
375 	if (HH_BADMAG(*hdr)) {
376 		fprintf(stderr, "%s: Bad magic: %o\n",
377 			_PATH_LD_HINTS, hdr->hh_magic);
378 		return -1;
379 	}
380 
381 	if (hdr->hh_version != LD_HINTS_VERSION_1) {
382 		fprintf(stderr, "Unsupported version: %d\n", hdr->hh_version);
383 		return -1;
384 	}
385 
386 	if (hdr->hh_ehints > msize) {
387 		if (mmap(addr+msize, hdr->hh_ehints - msize,
388 				PROT_READ, MAP_COPY|MAP_FIXED,
389 				fd, msize) != (caddr_t)(addr+msize)) {
390 
391 			perror(_PATH_LD_HINTS);
392 			return -1;
393 		}
394 	}
395 	close(fd);
396 
397 	blist = (struct hints_bucket *)(addr + hdr->hh_hashtab);
398 	strtab = (char *)(addr + hdr->hh_strtab);
399 
400 	printf("%s:\n", _PATH_LD_HINTS);
401 	for (i = 0; i < hdr->hh_nbucket; i++) {
402 		struct hints_bucket	*bp = &blist[i];
403 
404 		/* Sanity check */
405 		if (bp->hi_namex >= hdr->hh_strtab_sz) {
406 			fprintf(stderr, "Bad name index: %#x\n", bp->hi_namex);
407 			return -1;
408 		}
409 		if (bp->hi_pathx >= hdr->hh_strtab_sz) {
410 			fprintf(stderr, "Bad path index: %#x\n", bp->hi_pathx);
411 			return -1;
412 		}
413 
414 		printf("\t%d:-l%s.%d.%d => %s (%d -> %d)\n",
415 			i,
416 			strtab + bp->hi_namex, bp->hi_major, bp->hi_minor,
417 			strtab + bp->hi_pathx,
418 			hinthash(strtab+bp->hi_namex, bp->hi_major, bp->hi_minor)
419 					% hdr->hh_nbucket,
420 			bp->hi_next);
421 	}
422 
423 	return 0;
424 }
425 
426