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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1996-1997, by Sun Microsystems, Inc. 24 * All Rights Reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <locale.h> 30 #include <sys/types.h> 31 #include <sys/stat.h> 32 #include <sys/param.h> 33 #include <stdio.h> 34 #include <fcntl.h> 35 #include <link.h> 36 #include <string.h> 37 #include <stdlib.h> 38 #include <dirent.h> 39 #include <search.h> 40 41 #include "libelf.h" 42 #include "elfrd.h" 43 44 extern int verbose; 45 extern char *mstrdup(const char *); 46 extern void *mmalloc(size_t size); 47 48 /* 49 * Given the name of an executable and a function call the function for 50 * all shared objects needed to link the executable. The function will only 51 * be called once. A list of filenames for which the function has been called 52 * is maintained, this is used to exclude filenames. 53 */ 54 void 55 process_executable(char *pathname, int (*func)(char *, char *, DIR *, int)) 56 { 57 struct sobj *get_share_obj(char *, struct libpath *, int); 58 struct sobj *sop; 59 struct sobj *psop; 60 61 #ifdef DEBUG 62 printf("process_executable: pathname = %s\n", pathname); 63 fflush(stdout); 64 #endif /* debug */ 65 sop = get_share_obj(pathname, &libp_hd, GSO_ADDEXCLD); 66 #ifdef DEBUG 67 printf("process_executable: sop = %x\n", sop); 68 fflush(stdout); 69 #endif /* debug */ 70 if (verbose) { 71 if ((int)sop < 0) { 72 fprintf(stderr, 73 gettext( 74 "cachefspack: unable to get shared objects - %s\n"), 75 pathname); 76 } 77 } 78 if ((int)sop > 0) { 79 while (sop->so_next != (struct sobj *)0) { 80 #ifdef DEBUG 81 printf("process_executable: sop->so_name = %s\n", 82 sop->so_name); 83 fflush(stdout); 84 #endif /* DEBUG */ 85 func_dir_path(sop->so_name, func); 86 87 psop = sop; 88 sop = sop->so_next; 89 free(psop->so_name); 90 free(psop); 91 } 92 } 93 } 94 95 /* 96 * Given the name of an executable, a list of directories to use in the 97 * library search and a list of library names to exclude, return all 98 * shared object needed by the executable. 99 * 100 * RETURNS: A pointer to a list of shared objects 101 */ 102 struct sobj * 103 get_share_obj(char *fpath, struct libpath *libpath, int flag) 104 { 105 static int name_cnt = 0; 106 static struct sobj *so, *hd_so; 107 static int depth = 0; 108 static struct libpath *rpath, hd_rpath; 109 int found_file = 0; 110 int error; 111 int fd; 112 Elf *elfp; 113 Elf32_Ehdr *Ehdr; 114 Elf_Scn *scn; 115 Elf32_Shdr *shdr; 116 Elf32_Dyn *dyn; 117 size_t dynsz; 118 char *name; 119 void * get_scndata(); 120 struct sobj *alloc_sobj(); 121 struct sobj *add_so(); 122 char pathtmp[MAXPATHLEN]; 123 ENTRY hitem, *hitemp; 124 int fileopn; 125 int elfbgn = 0; 126 Elf_Kind file_type; 127 int buf; 128 129 /* 130 * Open a file and perform necessary operations to find the sections 131 * in an elf format file. If the specified file is not elf format 132 * return an error. 133 */ 134 depth++; 135 if (depth == 1) { 136 /* 137 * Find the ending exclude shared object element. 138 */ 139 rpath = &hd_rpath; 140 #ifdef DEBUG 141 printf("dbg: rpath = %x\n", rpath); 142 #endif /* DEBUG */ 143 rpath->lp_path = " "; 144 rpath->lp_next = (struct libpath *)0; 145 rpath->lp_level = 0; 146 } 147 148 fileopn = 0; 149 error = ERR_NOERROR; 150 fd = open(fpath, O_RDONLY); 151 if (fd < 0) { 152 error = ERR_NOFILE; 153 goto out; 154 } 155 fileopn = 1; 156 /* work around */ 157 /* 158 * elf_begin() core dumps when passed a file descriptor for a file 159 * which does not have read permission, but was opened RDONLY because the 160 * user doing the open was root. To avoid this problem, make sure we can 161 * read the first byte of the file. If we can't, skip the file. This is a 162 * temporary workaround until elf_begin() is fixed. 163 */ 164 if (read(fd, &buf, sizeof (buf)) < 0) { 165 #ifdef DEBUG 166 printf("read failed\n"); 167 fflush(stdout); 168 #endif /* DEBUG */ 169 error = ERR_NOFILE; 170 goto out; 171 } 172 lseek(fd, 0, SEEK_SET); 173 /* work around end */ 174 if (elf_version(EV_CURRENT) == EV_NONE) { 175 error = ERR_NOELFVER; 176 goto out; 177 } 178 elfbgn = 0; 179 if ((elfp = elf_begin(fd, ELF_C_READ, (Elf *)0)) == NULL) { 180 error = ERR_NOELFBEG; 181 goto out; 182 } 183 elfbgn = 1; 184 file_type = elf_kind(elfp); 185 #ifdef DEBUG 186 printf("file_type = %x\n", file_type); 187 fflush(stdout); 188 #endif /* DEBUG */ 189 190 if (file_type != ELF_K_ELF) { 191 goto out; 192 } 193 if ((Ehdr = elf32_getehdr(elfp)) == NULL) { 194 error = ERR_NOELFBEG; 195 goto out; 196 } 197 #ifdef DEBUG 198 printf("dbg: depth = %d\n", depth); 199 #endif /* DEBUG */ 200 /* 201 * Scan all sections of the elf file to locate the dynamic section 202 */ 203 scn = 0; 204 while ((scn = elf_nextscn(elfp, scn)) != 0) { 205 if ((shdr = elf32_getshdr(scn)) == NULL) { 206 error = ERR_NOELFSHD; 207 goto out; 208 } 209 if (shdr->sh_type != SHT_DYNAMIC) { 210 continue; 211 } 212 /* 213 * The first pass of the dynamic section locates all 214 * directories specified by "ld -R..". A stack is created 215 * for the search, this allows shared libraries, dependant 216 * on other shared libraries, built with "ld -R" to work 217 * properly. 218 * 219 */ 220 if ((dyn = (Elf32_Dyn *)get_scndata(scn, &dynsz)) == 0) { 221 error = ERR_NOELFSDT; 222 goto out; 223 } 224 while (dyn->d_tag != DT_NULL) { 225 if (dyn->d_tag == DT_RPATH) { 226 name = (char *)elf_strptr(elfp, 227 (size_t)shdr->sh_link, dyn->d_un.d_ptr); 228 #ifdef DEBUG 229 printf("DT_RPATH: name = %s\n", name); 230 #endif /* DEBUG */ 231 rpath = stk_libpath(rpath, name, depth); 232 } 233 dyn++; 234 } 235 /* 236 * Find all needed shared objects. Do this recursively 237 * so libraries dependant on other libraries are found. 238 * Also, try a list of libraries to exclude. Since 239 * this routine is used by cachefspack, it is only neccessary 240 * to pack a library once. For example, libc is used by lots 241 * of commands, we need not return its name to cachefspack 242 * except the first time we find it. 243 */ 244 if ((dyn = (Elf32_Dyn *)get_scndata(scn, &dynsz)) == 0) { 245 error = ERR_NOELFSDT; 246 goto out; 247 } 248 for (; dyn->d_tag != DT_NULL; dyn++) { 249 if (dyn->d_tag == DT_NEEDED) { 250 name = (char *)elf_strptr(elfp, 251 (size_t)shdr->sh_link, dyn->d_un.d_ptr); 252 if (name != 0) { 253 #ifdef DEBUG 254 printf("chk: %s\n", name); 255 fflush(stdout); 256 #endif /* DEBUG */ 257 found_file = libsrch(name, libpath, 258 pathtmp); 259 #ifdef DEBUG 260 printf("dbg: 1 found_file = %d\n", 261 found_file); 262 fflush(stdout); 263 #endif /* DEBUG */ 264 if (!found_file) { 265 found_file = libsrch(name, 266 rpath, pathtmp); 267 } 268 #ifdef DEBUG 269 printf("dbg: 2 found_file = %d\n", 270 found_file); 271 fflush(stdout); 272 #endif /* DEBUG */ 273 if (!found_file) { 274 continue; 275 } 276 if (name_cnt == 0) { 277 so = alloc_sobj(); 278 hd_so = so; 279 } 280 /* 281 * See if file already in list 282 */ 283 hitem.key = mstrdup(pathtmp); 284 hitem.data = 0; 285 hitemp = hsearch(hitem, FIND); 286 if (hitemp != NULL) { 287 #ifdef DEBUG 288 printf("found so: %s\n", 289 pathtmp); 290 printf("hitemp.key = %s\n", 291 hitemp->key); 292 #endif /* DEBUG */ 293 continue; 294 } 295 #ifdef DEBUG 296 printf("do : %s\n", pathtmp); 297 fflush(stdout); 298 #endif /* DEBUG */ 299 name_cnt++; 300 so = add_so(so, pathtmp); 301 if (flag & GSO_ADDEXCLD) { 302 #ifdef DEBUG 303 printf("adding so: %s\n", 304 pathtmp); 305 #endif /* DEBUG */ 306 hitem.key = mstrdup(pathtmp); 307 hitem.data = 0; 308 if (hsearch(hitem, ENTER) == 309 NULL) { 310 error = ERR_HASHFULL; 311 goto out; 312 } 313 } 314 get_share_obj(pathtmp, libpath, flag); 315 } else { 316 if (name_cnt > 0) { 317 goto out; 318 } else { 319 error = ERR_NOELFNAM; 320 goto out; 321 } 322 } 323 } 324 } 325 } 326 327 out: 328 #ifdef DEBUG 329 printf("error = %x\n", error); 330 fflush(stdout); 331 #endif /* DEBUG */ 332 depth--; 333 #ifdef DEBUG 334 printf("ret: depth = %d\n", depth); 335 fflush(stdout); 336 #endif /* DEBUG */ 337 if (fileopn) { 338 close(fd); 339 if (elfbgn) { 340 if ((error != ERR_NOFILE) && (error != ERR_NOELFVER)) { 341 elf_end(elfp); 342 } 343 } 344 } 345 if (name_cnt == 0) { 346 return ((struct sobj *)ERR_NOERROR); 347 } 348 while (rpath->lp_level > depth) { 349 #ifdef DEBUG 350 printf("ret: rpath->lp_level = %d\n", rpath->lp_level); 351 fflush(stdout); 352 #endif /* DEBUG */ 353 rpath = pop_libpath(rpath); 354 } 355 if (depth == 0) { 356 name_cnt = 0; 357 } 358 if (error == ERR_NOERROR) { 359 return (hd_so); 360 } else { 361 return ((struct sobj *)error); 362 } 363 } 364 365 366 /* 367 * Get the section descriptor and set the size of the 368 * data returned. Data is byte-order converted. 369 */ 370 371 void * 372 get_scndata(fd_scn, size) 373 Elf_Scn *fd_scn; 374 size_t *size; 375 { 376 Elf_Data *p_data; 377 378 p_data = 0; 379 if ((p_data = elf_getdata(fd_scn, p_data)) == 0 || 380 p_data->d_size == 0) 381 { 382 return (NULL); 383 } 384 385 *size = p_data->d_size; 386 return (p_data->d_buf); 387 } 388 389 /* 390 * Allocate a shared object structure 391 * 392 * RETURNS: A pointer to the allocated structure 393 */ 394 struct sobj * 395 alloc_sobj() 396 { 397 struct sobj *so; 398 so = (struct sobj *)mmalloc(sizeof (struct sobj)); 399 so->so_name = " "; 400 so->so_next = (struct sobj *)0; 401 return (so); 402 } 403 404 405 /* 406 * Add an object to a shared object list 407 * 408 * RETURNS: The tail of the shared object list 409 */ 410 struct sobj * 411 add_so(struct sobj *so, char *path) 412 { 413 if (so == (struct sobj *)0) { 414 so = alloc_sobj(); 415 } 416 so->so_name = mstrdup(path); 417 so->so_next = alloc_sobj(); 418 so = so->so_next; 419 return (so); 420 } 421 422 /* 423 * Determine if name concatenated with a library directory path yields 424 * a file name that exists. 425 * 426 * RETURNS: True(1) or False(0) 427 * if true - fullpath arg contains a pointer to the full path name 428 * of the file 429 */ 430 int 431 libsrch(char *name, struct libpath *libpath, char *fullpath) 432 { 433 struct stat64 statbuf; 434 struct libpath *lp; 435 436 #ifdef DEBUG 437 printf("libsrch: libpath = %x\n", libpath); 438 fflush(stdout); 439 #endif /* DEBUG */ 440 lp = libpath; 441 if (lp == NULL) { 442 return (0); 443 } 444 #ifdef DEBUG 445 printf("libsrch: 1 lp->lp_next = %x\n", lp->lp_next); 446 fflush(stdout); 447 #endif /* DEBUG */ 448 while (lp->lp_next != (struct libpath *)0) { 449 strcpy(fullpath, lp->lp_path); 450 strcat(fullpath, "/"); 451 strcat(fullpath, name); 452 lp = lp->lp_next; 453 #ifdef DEBUG 454 printf("libsrch: 2 lp->lp_next = %x\n", lp->lp_next); 455 fflush(stdout); 456 #endif /* DEBUG */ 457 /* 458 * stat - if file break 459 */ 460 if (stat64(fullpath, &statbuf) 461 == 0) { 462 #ifdef DEBUG 463 printf("libsrch: found - %s\n", fullpath); 464 fflush(stdout); 465 #endif /* DEBUG */ 466 return (1); 467 } 468 } 469 #ifdef DEBUG 470 printf("libsrch: NOT found - %s\n", name); 471 fflush(stdout); 472 #endif /* DEBUG */ 473 return (0); 474 } 475 476 /* 477 * Add path to the libpath list(add at the tail of the list). 478 * 479 * RETURNS: The new tail of the list 480 */ 481 struct libpath * 482 add_libpath(struct libpath *lp, char *path, int level) 483 { 484 char *s; 485 486 lp->lp_level = level; 487 s = mstrdup(path); 488 if (s != (char *)0) { 489 lp->lp_path = s; 490 } 491 lp->lp_next = (struct libpath *)mmalloc(sizeof (struct libpath)); 492 lp = lp->lp_next; 493 lp->lp_next = (struct libpath *)0; 494 lp->lp_level = 0; 495 lp->lp_path = " "; 496 return (lp); 497 } 498 499 /* 500 * Add directory/directories in name to libpath stack(as head of the stack) 501 * at the level specified. 502 * 503 * RETURNS: the new head of the stack 504 */ 505 struct libpath * 506 stk_libpath(struct libpath *hd, char *name, int level) 507 { 508 struct libpath *lp, *prev_lp; 509 char *s, *t; 510 char *tok; 511 char *freeit; 512 513 #ifdef DEBUG 514 printf("stk_libpath: name = %s\n", name); 515 fflush(stdout); 516 #endif /* DEBUG */ 517 s = mstrdup(name); 518 freeit = s; 519 prev_lp = hd; 520 while (1) { 521 tok = strtok(s, ":"); 522 if (tok == (char *)NULL) 523 break; 524 s = (char *)0; 525 lp = (struct libpath *)mmalloc(sizeof (struct libpath)); 526 lp->lp_level = level; 527 t = mstrdup(tok); 528 lp->lp_path = t; 529 lp->lp_next = prev_lp; 530 prev_lp = lp; 531 } 532 #ifdef DEBUG 533 printf("stk_libpath: lp = %x\n", lp); 534 fflush(stdout); 535 #endif /* DEBUG */ 536 free(freeit); 537 return (lp); 538 } 539 540 /* 541 * Free up a libpath stack entry. 542 * 543 * RETURNS: the new head of the stack 544 */ 545 struct libpath * 546 pop_libpath(struct libpath *lp) 547 { 548 struct libpath *tlp; 549 550 tlp = lp; 551 lp = lp->lp_next; 552 free(tlp->lp_path); 553 free(tlp); 554 return (lp); 555 } 556 557 /* 558 * Crack the LD_LIBRARY_PATH environment variable. Make a list of libraries 559 * to search. 560 */ 561 void 562 get_libsrch_path(struct libpath *libhd) 563 { 564 char *s; 565 char *tok = (char *) 1; 566 struct libpath *lp; 567 568 lp = libhd; 569 s = getenv("LD_LIBRARY_PATH"); 570 if (s != (char *)NULL) { 571 while (1) { 572 tok = strtok(s, ":"); 573 if (tok == (char *) NULL) 574 break; 575 s = (char *) 0; 576 lp = add_libpath(lp, tok, 0); 577 } 578 } 579 add_libpath(lp, "/usr/lib", 0); 580 } 581 582 583 #ifdef DEBUG 584 prt_sop_lst(struct sobj *sop, char * str) 585 { 586 printf("\n\n\n%s - sop = %x\n", str, sop); 587 fflush(stdout); 588 if ((int)sop < 0) { 589 fprintf(stderr, "get_share_obj: failed\n"); 590 exit(1); 591 } 592 593 if ((int)sop > 0) { 594 while (sop->so_next != (struct sobj *) 0) { 595 printf("sop->so_name = %s\n", sop->so_name); 596 sop = sop->so_next; 597 } 598 } 599 } 600 #endif /* DEBUG */