xref: /titanic_52/usr/src/cmd/sgs/rtld/common/cache_a.out.c (revision 0eb822a1c0c2bea495647510b75f77f0e57633eb)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * 4.x ld.so directory caching: run-time link-editor specific functions.
30  */
31 #include	"_synonyms.h"
32 
33 #include	<dirent.h>
34 #include	<string.h>
35 #include	<sys/stat.h>
36 #include	<fcntl.h>
37 #include	<unistd.h>
38 #include	"_a.out.h"
39 #include	"cache_a.out.h"
40 #include	"_rtld.h"
41 #include	"msg.h"
42 
43 static int	stol();
44 static int	rest_ok();
45 static int	verscmp();
46 static void	fix_lo();
47 static int	extract_name();
48 static int	hash();
49 
50 static struct link_object *get_lo();
51 static struct dbd *new_dbd();
52 static struct db *find_so();
53 
54 #define	SKIP_DOT(str)	((*str == '.')  ? ++str : str)
55 #define	EMPTY(str)	((str == NULL) || (*str == '\0'))
56 #define	isdigit(c)	(((c) >= '0') && ((c) <= '9') ? 1:0)
57 
58 static struct dbd *dbd_head = NULL;	/* head of data bases */
59 
60 
61 /*
62  * Given a db - find the highest shared versioned object. The
63  * highest versioned object is the .so  with a matching major number
64  * but the highest minor number
65  */
66 char *
67 ask_db(dbp, file)
68 	struct db *dbp;
69 	const char *file;
70 {
71 	char *libname, *n;
72 	char *mnp;
73 	char *mjp;
74 	int liblen;
75 	int major = 0;
76 	int to_min;
77 	struct dbe *ep;
78 	struct link_object *tlop;
79 	int index;
80 
81 	n = (char *)file;
82 	if ((liblen = extract_name(&n)) == -1)
83 		return (NULL);
84 	if ((libname = malloc(liblen + 1)) == 0)
85 		return (NULL);
86 	(void) strncpy(libname, n, liblen);
87 	libname[liblen] = NULL;
88 
89 	if (strncmp(MSG_ORIG(MSG_FIL_DOTSODOT), (n + liblen),
90 	    MSG_FIL_DOTSODOT_SIZE))
91 		return (NULL);
92 
93 	mnp = mjp = ((char *)file + MSG_FIL_LIB_SIZE + liblen +
94 	    MSG_FIL_DOTSODOT_SIZE);
95 	if (!(stol(mjp, '.', &mnp, &major) && (*mnp == '.') &&
96 	    rest_ok(mnp + 1)))
97 		return (NULL);
98 	to_min = mnp - file + 1;
99 
100 	/*
101 	 * Search appropriate hash bucket for a matching entry.
102 	 */
103 	index = hash(libname, liblen, major);
104 	for (ep = (struct dbe *)&(dbp->db_hash[index]); (ep && ep->dbe_lop);
105 	    ep = ep->dbe_next == 0 ? NULL :
106 	    /* LINTED */
107 	    (struct dbe *)&AP(dbp)[ep->dbe_next]) {
108 		/* LINTED */
109 		tlop = (struct link_object *)&AP(dbp)[ep->dbe_lop];
110 		if (tlop->lo_major == major)
111 			if (strcmp((char *)&AP(dbp)[tlop->lo_name],
112 			    libname) == 0)
113 				break;
114 	}
115 
116 	/*
117 	 * If no entry was found, we've lost.
118 	 */
119 	if (!(ep && ep->dbe_lop))
120 		return (NULL);
121 	if (verscmp(file + to_min,
122 	    &AP(dbp)[ep->dbe_name] + tlop->lo_minor) > 0)
123 		eprintf(&lml_main, ERR_WARNING, MSG_INTL(MSG_GEN_OLDREV),
124 		    &AP(dbp)[ep->dbe_name], file + to_min);
125 	return (&AP(dbp)[ep->dbe_name]);
126 }
127 
128 /*
129  * Given a directory name - give back a data base. The data base may have
130  * orginated from the mmapped file or temporarily created
131  */
132 struct db *
133 lo_cache(const char *ds)
134 {
135 	struct db *dbp;			/* database pointer */
136 	struct dbd *dbdp;		/* working database descriptor */
137 	struct dbd **dbdpp;		/* insertion pointer */
138 
139 	dbdpp = &dbd_head;
140 	for (dbdp = dbd_head; dbdp; dbdp = dbdp->dbd_next) {
141 		if (strcmp(ds, &AP(dbdp->dbd_db)[dbdp->dbd_db->db_name]) == 0)
142 			return (dbdp->dbd_db);
143 		dbdpp = &dbdp->dbd_next;
144 	}
145 	if (dbp = find_so(ds)) {
146 		(void) new_dbd(dbdpp, dbp);
147 	}
148 	return (dbp);
149 }
150 
151 /*
152  * Build a database for the directory "ds".
153  */
154 static struct db *
155 find_so(const char *ds)
156 {
157 	int fd;				/* descriptor on directory */
158 	int n;				/* bytes from getdents */
159 	char *cp;			/* working char * */
160 	struct stat sb;			/* buffer for stat'ing directory */
161 	struct db *dbp;			/* database */
162 	static caddr_t buf = NULL;	/* buffer for doing getdents */
163 	static long bs;			/* cached blocksize for getdents */
164 	struct link_object *tlop;	/* working link object ptr. */
165 	struct dirent *dp;		/* directory entry ptr. */
166 	struct dbe *ep;			/* working db_entry ptr. */
167 	char *mnp;			/* where minor version begins */
168 	char *mjp;			/* where major version begins */
169 	int m;				/* the major number */
170 	int to_min;			/* index into string of minor */
171 	int cplen;			/* length of X */
172 	int index;			/* the hash value */
173 
174 	/*
175 	 * Try to open directory.  Failing that, just return silently.
176 	 */
177 	if ((fd = open(ds, O_RDONLY)) == -1)
178 		return ((struct db *)NULL);
179 
180 	/*
181 	 * If we have not yet gotten a buffer for reading directories,
182 	 * allocate it now.  Size it according to the most efficient size
183 	 * for the first directory we open successfully.
184 	 */
185 	if (!buf) {
186 		if (fstat(fd, &sb) == -1) {
187 			(void) close(fd);
188 			return ((struct db *)NULL);
189 		}
190 		bs = sb.st_blksize;
191 		buf = calloc(bs, 1);
192 	}
193 
194 	/*
195 	 * Have a directory, have a buffer.  Allocate up a database
196 	 * and initialize it.
197 	 */
198 	dbp = calloc(sizeof (struct db), 1);
199 	dbp->db_name = RELPTR(dbp, calloc((strlen(ds) + 1), 1));
200 	(void) strcpy((char *)&AP(dbp)[dbp->db_name], ds);
201 
202 	/*
203 	 * Scan the directory looking for shared libraries.  getdents()
204 	 * failures are silently ignored and terminate the scan.
205 	 */
206 	/* LINTED */
207 	while ((n = getdents(fd, (struct dirent *)buf, bs)) > 0)
208 		/* LINTED */
209 		for (dp = (struct dirent *)buf;
210 		    /* LINTED */
211 		    dp && (dp < (struct dirent *)(buf + n));
212 		    /* LINTED */
213 		    dp = (struct dirent *)((dp->d_reclen == 0) ?
214 		    NULL : (char *)dp + dp->d_reclen)) {
215 
216 			/*
217 			 * If file starts with a "lib", then extract the X
218 			 * from libX.
219 			 */
220 			cp = dp->d_name;
221 			if ((cplen = extract_name(&cp)) == -1)
222 				continue;
223 
224 			/*
225 			 * Is the next component ".so."?
226 			 */
227 			if (strncmp(MSG_ORIG(MSG_FIL_DOTSODOT), (cp + cplen),
228 			    MSG_FIL_DOTSODOT_SIZE))
229 				continue;
230 
231 			/*
232 			 * Check if next component is the major number and
233 			 * whether following components are legal.
234 			 */
235 			mnp = mjp = (dp->d_name + MSG_FIL_LIB_SIZE + cplen +
236 			    MSG_FIL_DOTSODOT_SIZE);
237 			if (!(stol(mjp, '.', &mnp, &m) && (*mnp == '.') &&
238 			    rest_ok(mnp + 1)))
239 				continue;
240 			to_min = mnp - dp->d_name + 1;
241 
242 			/*
243 			 * Have libX.so.major.minor - attempt to add it to the
244 			 * cache. If there is another with the same major
245 			 * number then the chose the object with the highest
246 			 * minor number
247 			 */
248 			index = hash(cp, cplen, m);
249 			ep = &(dbp->db_hash[index]);
250 			if (ep->dbe_lop == NULL) {
251 				ep->dbe_lop = (long)get_lo(dbp, cp,
252 				    cplen, m, to_min);
253 				/* LINTED */
254 				tlop = (struct link_object *)
255 				    &AP(dbp)[ep->dbe_lop];
256 				(void) strcpy(&AP(dbp)[tlop->lo_next],
257 				    dp->d_name);
258 				continue;
259 			}
260 			for (ep = &(dbp->db_hash[index]); ep;
261 			    /* LINTED */
262 			    ep = (struct dbe *)&AP(dbp)[ep->dbe_next]) {
263 				/* LINTED */
264 				tlop = (struct link_object *)
265 				    &AP(dbp)[ep->dbe_lop];
266 
267 				/*
268 				 * Choose the highest minor version
269 				 */
270 				if ((tlop->lo_major == m) &&
271 				    (strncmp(&AP(dbp)[tlop->lo_name],
272 				    cp, cplen) == 0) &&
273 				    (*(&AP(dbp)[tlop->lo_name +
274 				    cplen]) == '\0')) {
275 					if (verscmp(dp->d_name + to_min,
276 					    (char *)(&AP(dbp)[tlop->lo_next]
277 					    + to_min)) > 0)
278 						(void) strcpy(&AP(dbp)
279 						    [tlop->lo_next],
280 						    dp->d_name);
281 					break;
282 				}
283 				if (ep->dbe_next == NULL) {
284 					ep->dbe_next = RELPTR(dbp,
285 					    calloc(sizeof (struct dbe), 1));
286 					/* LINTED */
287 					ep  = (struct dbe *)
288 					    &AP(dbp)[ep->dbe_next];
289 					ep->dbe_lop = (long)get_lo(dbp,
290 					    cp, cplen, m, to_min);
291 					/* LINTED */
292 					tlop = (struct link_object *)
293 					    &AP(dbp)[ep->dbe_lop];
294 					(void) strcpy(&AP(dbp)[tlop->lo_next],
295 					    dp->d_name);
296 					break;
297 				}
298 			}
299 		}
300 	fix_lo(dbp);
301 	(void) close(fd);
302 	return (dbp);
303 }
304 
305 /*
306  * Allocate and fill in the fields for a link_object
307  */
308 static struct link_object *
309 get_lo(dbp, cp, cplen, m, n)
310 	struct db *dbp;			/* data base */
311 	char *cp;			/* ptr. to X of libX */
312 	int cplen;			/* length of X */
313 	int m;				/* major version */
314 	int n;				/* index to minor version */
315 {
316 	struct link_object *lop;	/* link_object to be returned */
317 	struct link_object *tlop;	/* working copy of the above */
318 
319 	/*
320 	 * Allocate a link object prototype in the database heap.
321 	 * Store the numeric major (interface) number, but the minor
322 	 * number is stored in the database as an index to the string
323 	 * representing the minor version.  By keeping the minor version
324 	 * as a string, "subfields" (i.e., major.minor[.other.fields. etc.])
325 	 * are permitted.  Although not meaningful to the link editor, this
326 	 * permits run-time substitution of arbitrary customer revisions,
327 	 * although introducing the confusion of overloading the lo_minor
328 	 * field in the database (!)
329 	 */
330 	lop = (struct link_object *)RELPTR(dbp,
331 	    calloc(sizeof (struct link_object), 1));
332 	/* LINTED */
333 	tlop = (struct link_object *)&AP(dbp)[(long)lop];
334 	tlop->lo_major = m;
335 	tlop->lo_minor = n;
336 
337 	/*
338 	 * Allocate space for the complete path name on the host program's
339 	 * heap -- as we have to save it from the directory buffer which
340 	 * might otherwise get re-used on us.  Note that this space
341 	 * is wasted -- we can not assume that it can be reclaimed.
342 	 */
343 	tlop->lo_next = (long)RELPTR(dbp, calloc(MAXNAMLEN, 1));
344 
345 	/*
346 	 * Store the prototype name in the link object in the database.
347 	 */
348 	tlop->lo_name = (long)RELPTR(dbp, calloc((cplen + 1), 1));
349 	(void) strncpy((char *)&AP(dbp)[tlop->lo_name], cp, cplen);
350 	return (lop);
351 }
352 
353 /*
354  * Pull the "X" from libX, set name to X and return the
355  * length of X
356  */
357 static int
358 extract_name(name)
359 	char **name;
360 {
361 	char *ls;			/* string after LIB root */
362 	char *dp;			/* string before first delimiter */
363 
364 	if (strncmp(*name, MSG_ORIG(MSG_FIL_LIB), MSG_FIL_LIB_SIZE) == 0) {
365 		ls = *name + MSG_FIL_LIB_SIZE;
366 		if ((dp = (char *)strchr(ls, '.')) != (char *)0) {
367 			*name = ls;
368 			return (dp - ls);
369 		}
370 	}
371 	return (-1);
372 }
373 
374 /*
375  * Make a pass through the data base to set the dbe_name of a dbe.  This
376  * is necessary because there may be several revisions of a library
377  * but only one will be chosen.
378  */
379 static void
380 fix_lo(dbp)
381 	struct db *dbp;
382 {
383 	int i;				/* loop temporary */
384 	int dirlen = strlen(&AP(dbp)[dbp->db_name]);
385 					/* length of directory pathname */
386 	char *cp;			/* working temporary */
387 	char *tp;			/* working temporary */
388 	struct dbe *ep;			/* working copy of dbe */
389 	struct link_object *lop;	/* working copy of link_object */
390 
391 	for (i = 0; i < DB_HASH; i++) {
392 		for (ep = &(dbp->db_hash[i]); ep && ep->dbe_lop;
393 		    (ep = ep->dbe_next == 0 ? NULL :
394 		    /* LINTED */
395 		    (struct dbe *)&AP(dbp)[ep->dbe_next])) {
396 			/* LINTED */
397 			lop = (struct link_object *)&AP(dbp)[ep->dbe_lop];
398 			tp = &AP(dbp)[lop->lo_next];
399 			ep->dbe_name = RELPTR(dbp,
400 			    calloc((dirlen + strlen(tp) + 2), 1));
401 			lop->lo_minor += dirlen + 1;
402 			cp = strncpy(&AP(dbp)[ep->dbe_name],
403 			    &AP(dbp)[dbp->db_name], dirlen);
404 			cp = strncpy(cp + dirlen, MSG_ORIG(MSG_STR_SLASH),
405 			    MSG_STR_SLASH_SIZE);
406 			(void) strcpy(cp + 1, tp);
407 		}
408 	}
409 }
410 
411 /*
412  * Allocate a new dbd, append it after dbdpp and set the dbd_dbp to dbp.
413  */
414 static struct dbd *
415 new_dbd(dbdpp, dbp)
416 	struct dbd **dbdpp;		/* insertion point */
417 	struct db *dbp;			/* db associated with this dbd */
418 {
419 	struct dbd *dbdp;		/* working dbd ptr. */
420 
421 	dbdp = malloc(sizeof (struct dbd));
422 	dbdp->dbd_db = dbp;
423 	dbdp->dbd_next = NULL;
424 	*dbdpp = dbdp;
425 	return (dbdp);
426 }
427 
428 /*
429  * Calculate hash index for link object.
430  * This is based on X.major from libX.so.major.minor.
431  */
432 static int
433 hash(np, nchrs, m)
434 	char *np; 			/* X of libX */
435 	int nchrs;			/* no of chrs. to hash on */
436 	int m;				/* the major version */
437 {
438 	int h;				/* for loop counter */
439 	char *cp;			/* working (char *) ptr */
440 
441 	for (h = 0, cp = np; h < nchrs; h++, cp++)
442 		h = (h << 1) + *cp;
443 	h += (h << 1) + m;
444 	h = ((h & 0x7fffffff) % DB_HASH);
445 	return (h);
446 }
447 
448 /*
449  * Test whether the string is of digit[.digit]* format
450  */
451 static int
452 rest_ok(str)
453 	char *str;			/* input string */
454 {
455 	int dummy;			/* integer place holder */
456 	int legal = 1;			/* return flag */
457 
458 	while (!EMPTY(str)) {
459 		if (!stol(str, '.', &str, &dummy)) {
460 			legal = 0;
461 			break;
462 		}
463 		if (EMPTY(str))
464 			break;
465 		else
466 			/* LINTED */
467 			(SKIP_DOT(str));
468 	}
469 	return (legal);
470 }
471 
472 /*
473  * Compare 2 strings and test whether they are of the form digit[.digit]*.
474  * It will return -1, 0, or 1 depending on whether c1p is less, equal or
475  * greater than c2p
476  */
477 static int
478 verscmp(const char *c1p, const char *c2p)
479 {
480 	char	*l_c1p = (char *)c1p;	/* working copy of c1p */
481 	char	*l_c2p = (char *)c2p;	/* working copy of c2p */
482 	int	l_c1p_ok = 0;		/* is c1p a legal string */
483 	int	c2p_dig = 0;		/* int that c1p currently */
484 					/*	represents */
485 	int	c1p_dig = 0;		/* int that c2p currently */
486 					/*	represents */
487 
488 	while (((l_c1p_ok = stol(l_c1p, '.', &l_c1p, &c1p_dig)) == 1) &&
489 		stol(l_c2p, '.', &l_c2p, &c2p_dig) && (c2p_dig == c1p_dig)) {
490 		if (EMPTY(l_c1p) && EMPTY(l_c2p))
491 			return (0);
492 		else if (EMPTY(l_c1p) && !EMPTY(l_c2p) &&
493 		    rest_ok(SKIP_DOT(l_c2p)))
494 			return (-1);
495 		else if (EMPTY(l_c2p) && !EMPTY(l_c1p) &&
496 		    rest_ok(SKIP_DOT(l_c1p)))
497 			return (1);
498 		l_c1p++; l_c2p++;
499 	};
500 	if (!l_c1p_ok)
501 		return (-1);
502 	else if (c1p_dig < c2p_dig)
503 		return (-1);
504 	else if ((c1p_dig > c2p_dig) && rest_ok(SKIP_DOT(l_c1p)))
505 		return (1);
506 	else return (-1);
507 }
508 
509 /*
510  * "stol" attempts to interpret a collection of characters between delimiters
511  * as a decimal digit. It stops interpreting when it reaches a delimiter or
512  * when character does not represent a digit. In the first case it returns
513  * success and the latter failure.
514  */
515 static int
516 stol(cp, delimit, ptr, i)
517 	char *cp;			/* ptr to input string */
518 	char delimit;			/* delimiter */
519 	char **ptr;			/* left pointing to next del. or */
520 					/* illegal character */
521 	int *i;				/* digit that the string represents */
522 {
523 	int c = 0;			/* current char */
524 	int n = 0;			/* working copy of i */
525 	int neg = 0;			/* is number negative */
526 
527 	if (ptr != (char **)0)
528 		*ptr = cp; /* in case no number is formed */
529 
530 	if (EMPTY(cp))
531 		return (0);
532 
533 	if (!isdigit(c = *cp) && (c == '-')) {
534 		neg++;
535 		c = *++cp;
536 	};
537 	if (EMPTY(cp) || !isdigit(c))
538 		return (0);
539 
540 	while (isdigit(c = *cp) && (*cp++ != '\0')) {
541 		n *= 10;
542 		n += c - '0';
543 	};
544 	if (ptr != (char **)0)
545 		*ptr = cp;
546 
547 	if ((*cp == '\0') || (*cp == delimit)) {
548 		*i = neg ? -n : n;
549 		return (1);
550 	};
551 	return (0);
552 }
553