Home | History | Annotate | Download | only in common
      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 *
     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 *
    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 *
    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 *
    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
    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
    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 *
    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
    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
    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
    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
    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