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