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 */