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