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 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * 4.x ld.so directory caching: run-time link-editor specific functions. 31 */ 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