Home | History | Annotate | Download | only in soslim
      1 /* TODO:
      2    1. check the ARM EABI version--this works for versions 1 and 2.
      3    2. use a more-intelligent approach to finding the symbol table, symbol-string
      4       table, and the .dynamic section.
      5    3. fix the determination of the host and ELF-file endianness
      6    4. write the help screen
      7 */
      8 
      9 #include <stdio.h>
     10 #include <common.h>
     11 #include <debug.h>
     12 #include <hash.h>
     13 #include <libelf.h>
     14 #include <elf.h>
     15 #include <gelf.h>
     16 #include <cmdline.h>
     17 #include <string.h>
     18 #include <errno.h>
     19 #include <string.h>
     20 #include <sys/types.h>
     21 #include <sys/stat.h>
     22 #include <fcntl.h>
     23 #include <unistd.h>
     24 #include <soslim.h>
     25 #include <symfilter.h>
     26 #ifdef SUPPORT_ANDROID_PRELINK_TAGS
     27 #include <prelink_info.h>
     28 #endif
     29 
     30 /* Flag set by --verbose.  This variable is global as it is accessed by the
     31    macro INFO() in multiple compilation unites. */
     32 int verbose_flag = 0;
     33 /* Flag set by --quiet.  This variable is global as it is accessed by the
     34    macro PRINT() in multiple compilation unites. */
     35 int quiet_flag = 0;
     36 static void print_dynamic_symbols(Elf *elf, const char *symtab_name);
     37 
     38 int main(int argc, char **argv)
     39 {
     40     int elf_fd = -1, newelf_fd = -1;
     41     Elf *elf = NULL, *newelf = NULL;
     42     char *infile = NULL;
     43     char *outfile = NULL;
     44     char *symsfile_name = NULL;
     45     int print_symtab = 0;
     46     int shady = 0;
     47     int dry_run = 0;
     48     int strip_debug = 0;
     49 
     50     /* Do not issue INFO() statements before you call get_options() to set
     51        the verbose flag as necessary.
     52     */
     53 
     54     int first = get_options(argc, argv,
     55                             &outfile,
     56                             &symsfile_name,
     57                             &print_symtab,
     58                             &verbose_flag,
     59                             &quiet_flag,
     60                             &shady,
     61                             &dry_run,
     62                             &strip_debug);
     63 
     64     if ((print_symtab && (first == argc)) ||
     65         (!print_symtab && first + 1 != argc)) {
     66         print_help();
     67         FAILIF(1,  "You must specify an input ELF file!\n");
     68     }
     69     FAILIF(print_symtab && (outfile || symsfile_name || shady),
     70            "You cannot provide --print and --outfile, --filter options, or "
     71            "--shady simultaneously!\n");
     72     FAILIF(dry_run && outfile,
     73            "You cannot have a dry run and output a file at the same time.");
     74 
     75     /* Check to see whether the ELF library is current. */
     76     FAILIF (elf_version(EV_CURRENT) == EV_NONE, "libelf is out of date!\n");
     77 
     78     if (print_symtab) {
     79 
     80         while (first < argc) {
     81             infile = argv[first++];
     82 
     83             INFO("Opening %s...\n", infile);
     84             elf_fd = open(infile, O_RDONLY);
     85             FAILIF(elf_fd < 0, "open(%s): %s (%d)\n",
     86                    infile,
     87                    strerror(errno),
     88                    errno);
     89             INFO("Calling elf_begin(%s)...\n", infile);
     90             elf = elf_begin(elf_fd, ELF_C_READ, NULL);
     91             FAILIF_LIBELF(elf == NULL, elf_begin);
     92 
     93             /* libelf can recognize COFF and A.OUT formats, but we handle only
     94                ELF. */
     95             FAILIF(elf_kind(elf) != ELF_K_ELF,
     96                    "Input file %s is not in ELF format!\n",
     97                    infile);
     98 
     99             /* Make sure this is a shared library or an executable. */
    100             {
    101                 GElf_Ehdr elf_hdr;
    102                 INFO("Making sure %s is a shared library or an executable.\n",
    103                      infile);
    104                 FAILIF_LIBELF(0 == gelf_getehdr(elf, &elf_hdr), gelf_getehdr);
    105                 FAILIF(elf_hdr.e_type != ET_DYN &&
    106                        elf_hdr.e_type != ET_EXEC,
    107                        "%s must be a shared library or an executable "
    108                        "(elf type is %d).\n",
    109                        infile,
    110                        elf_hdr.e_type);
    111             }
    112 
    113             print_dynamic_symbols(elf, infile);
    114 
    115             FAILIF_LIBELF(elf_end(elf), elf_end);
    116             FAILIF(close(elf_fd) < 0, "Could not close file %s: %s (%d)!\n",
    117                    infile, strerror(errno), errno);
    118         }
    119     }
    120     else {
    121         int elf_fd = -1;
    122         Elf *elf = NULL;
    123         infile = argv[first];
    124 
    125         INFO("Opening %s...\n", infile);
    126         elf_fd = open(infile, ((outfile == NULL && dry_run == 0) ? O_RDWR : O_RDONLY));
    127         FAILIF(elf_fd < 0, "open(%s): %s (%d)\n",
    128                infile,
    129                strerror(errno),
    130                errno);
    131         INFO("Calling elf_begin(%s)...\n", infile);
    132         elf = elf_begin(elf_fd,
    133                         ((outfile == NULL && dry_run == 0) ? ELF_C_RDWR : ELF_C_READ),
    134                         NULL);
    135         FAILIF_LIBELF(elf == NULL, elf_begin);
    136 
    137         /* libelf can recognize COFF and A.OUT formats, but we handle only ELF. */
    138         FAILIF(elf_kind(elf) != ELF_K_ELF,
    139                "Input file %s is not in ELF format!\n",
    140                infile);
    141 
    142         /* We run a better check in adjust_elf() itself.  It is permissible to call adjust_elf()
    143            on an executable if we are only stripping sections from the executable, not rearranging
    144            or moving sections.
    145         */
    146         if (0) {
    147             /* Make sure this is a shared library. */
    148             GElf_Ehdr elf_hdr;
    149             INFO("Making sure %s is a shared library...\n", infile);
    150             FAILIF_LIBELF(0 == gelf_getehdr(elf, &elf_hdr), gelf_getehdr);
    151             FAILIF(elf_hdr.e_type != ET_DYN,
    152                    "%s must be a shared library (elf type is %d, expecting %d).\n",
    153                    infile,
    154                    elf_hdr.e_type,
    155                    ET_DYN);
    156         }
    157 
    158         if (outfile != NULL) {
    159             ASSERT(!dry_run);
    160             struct stat st;
    161             FAILIF(fstat (elf_fd, &st) != 0,
    162                    "Cannot stat input file %s: %s (%d)!\n",
    163                    infile, strerror(errno), errno);
    164             newelf_fd = open (outfile, O_RDWR | O_CREAT | O_TRUNC,
    165                     st.st_mode & ACCESSPERMS);
    166             FAILIF(newelf_fd < 0, "Cannot create file %s: %s (%d)!\n",
    167                    outfile, strerror(errno), errno);
    168             INFO("Output file is [%s].\n", outfile);
    169             newelf = elf_begin(newelf_fd, ELF_C_WRITE_MMAP, NULL);
    170         } else {
    171             INFO("Modifying [%s] in-place.\n", infile);
    172             newelf = elf_clone(elf, ELF_C_EMPTY);
    173         }
    174 
    175         symfilter_t symfilter;
    176 
    177         symfilter.symbols_to_keep = NULL;
    178         symfilter.num_symbols_to_keep = 0;
    179         if (symsfile_name) {
    180             /* Make sure that the file is not empty. */
    181             struct stat s;
    182             FAILIF(stat(symsfile_name, &s) < 0,
    183                    "Cannot stat file %s.\n", symsfile_name);
    184             if (s.st_size) {
    185                 INFO("Building symbol filter.\n");
    186                 build_symfilter(symsfile_name, elf, &symfilter, s.st_size);
    187             }
    188             else INFO("Not building symbol filter, filter file is empty.\n");
    189         }
    190 #ifdef SUPPORT_ANDROID_PRELINK_TAGS
    191         int prelinked = 0;
    192         int elf_little; /* valid if prelinked != 0 */
    193         long prelink_addr; /* valid if prelinked != 0 */
    194 #endif
    195         clone_elf(elf, newelf,
    196                   infile, outfile,
    197                   symfilter.symbols_to_keep,
    198                   symfilter.num_symbols_to_keep,
    199                   shady
    200 #ifdef SUPPORT_ANDROID_PRELINK_TAGS
    201                   , &prelinked,
    202                   &elf_little,
    203                   &prelink_addr
    204 #endif
    205                   ,
    206                   true, /* rebuild the section-header-strings table */
    207                   strip_debug,
    208                   dry_run);
    209 
    210         if (symsfile_name && symfilter.symbols_to_keep != NULL) {
    211             destroy_symfilter(&symfilter);
    212         }
    213 
    214         if (outfile != NULL) INFO("Closing %s...\n", outfile);
    215         FAILIF_LIBELF(elf_end (newelf) != 0, elf_end);
    216         FAILIF(newelf_fd >= 0 && close(newelf_fd) < 0,
    217                "Could not close file %s: %s (%d)!\n",
    218                outfile, strerror(errno), errno);
    219 
    220         INFO("Closing %s...\n", infile);
    221         FAILIF_LIBELF(elf_end(elf), elf_end);
    222         FAILIF(close(elf_fd) < 0, "Could not close file %s: %s (%d)!\n",
    223                infile, strerror(errno), errno);
    224 
    225 #ifdef SUPPORT_ANDROID_PRELINK_TAGS
    226         if (prelinked) {
    227             INFO("File is prelinked, putting prelink TAG back in place.\n");
    228             setup_prelink_info(outfile != NULL ? outfile : infile,
    229                                elf_little,
    230                                prelink_addr);
    231         }
    232 #endif
    233     }
    234 
    235     FREEIF(outfile);
    236     return 0;
    237 }
    238 
    239 static void print_dynamic_symbols(Elf *elf, const char *file)
    240 {
    241     Elf_Scn *scn = NULL;
    242     GElf_Shdr shdr;
    243 
    244     GElf_Ehdr ehdr;
    245     FAILIF_LIBELF(0 == gelf_getehdr(elf, &ehdr), gelf_getehdr);
    246     while ((scn = elf_nextscn (elf, scn)) != NULL) {
    247         FAILIF_LIBELF(NULL == gelf_getshdr(scn, &shdr), gelf_getshdr);
    248         if (SHT_DYNSYM == shdr.sh_type) {
    249             /* This failure is too restrictive.  There is no reason why
    250                the symbol table couldn't be called something else, but
    251                there is a standard name, and chances are that if we don't
    252                see it, there's something wrong.
    253             */
    254             size_t shstrndx;
    255             FAILIF_LIBELF(elf_getshstrndx(elf, &shstrndx) < 0,
    256                           elf_getshstrndx);
    257             /* Now print the symbols. */
    258             {
    259                 Elf_Data *symdata;
    260                 size_t elsize;
    261                 symdata = elf_getdata (scn, NULL); /* get the symbol data */
    262                 FAILIF_LIBELF(NULL == symdata, elf_getdata);
    263                 /* Get the number of section.  We need to compare agains this
    264                    value for symbols that have special info in their section
    265                    references */
    266                 size_t shnum;
    267                 FAILIF_LIBELF(elf_getshnum (elf, &shnum) < 0, elf_getshnum);
    268                 /* Retrieve the size of a symbol entry */
    269                 elsize = gelf_fsize(elf, ELF_T_SYM, 1, ehdr.e_version);
    270 
    271                 size_t index;
    272                 for (index = 0; index < symdata->d_size / elsize; index++) {
    273                     GElf_Sym sym_mem;
    274                     GElf_Sym *sym;
    275                     /* Get the symbol. */
    276                     sym = gelf_getsymshndx (symdata, NULL,
    277                                             index, &sym_mem, NULL);
    278                     FAILIF_LIBELF(sym == NULL, gelf_getsymshndx);
    279                     /* Print the symbol. */
    280                     char bind = '?';
    281                     switch(ELF32_ST_BIND(sym->st_info))
    282                     {
    283                     case STB_LOCAL: bind = 'l'; break;
    284                     case STB_GLOBAL: bind = 'g'; break;
    285                     case STB_WEAK: bind = 'w'; break;
    286                     default: break;
    287                     }
    288                     char type = '?';
    289                     switch(ELF32_ST_TYPE(sym->st_info))
    290                     {
    291                     case STT_NOTYPE: /* Symbol type is unspecified */
    292                         type = '?';
    293                         break;
    294                     case STT_OBJECT: /* Symbol is a data object */
    295                         type = 'o';
    296                         break;
    297                     case STT_FUNC: /* Symbol is a code object */
    298                         type = 'f';
    299                         break;
    300                     case STT_SECTION:/* Symbol associated with a section */
    301                         type = 's';
    302                         break;
    303                     case STT_FILE: /* Symbol's name is file name */
    304                         type = 'f';
    305                         break;
    306                     case STT_COMMON: /* Symbol is a common data object */
    307                         type = 'c';
    308                         break;
    309                     case STT_TLS: /* Symbol is thread-local data object*/
    310                         type = 't';
    311                         break;
    312                     }
    313                     {
    314                         int till_lineno;
    315                         int lineno;
    316                         const char *section_name = "(unknown)";
    317                         FAILIF(sym->st_shndx == SHN_XINDEX,
    318                                "Can't handle symbol's st_shndx == SHN_XINDEX!\n");
    319                         if (sym->st_shndx != SHN_UNDEF &&
    320                             sym->st_shndx < shnum) {
    321                             Elf_Scn *symscn = elf_getscn(elf, sym->st_shndx);
    322                             FAILIF_LIBELF(NULL == symscn, elf_getscn);
    323                             GElf_Shdr symscn_shdr;
    324                             FAILIF_LIBELF(NULL == gelf_getshdr(symscn,
    325                                                                &symscn_shdr),
    326                                           gelf_getshdr);
    327                             section_name = elf_strptr(elf, shstrndx,
    328                                                       symscn_shdr.sh_name);
    329                         }
    330                         else if (sym->st_shndx == SHN_ABS) {
    331                             section_name = "SHN_ABS";
    332                         }
    333                         else if (sym->st_shndx == SHN_COMMON) {
    334                             section_name = "SHN_COMMON";
    335                         }
    336                         else if (sym->st_shndx == SHN_UNDEF) {
    337                             section_name = "(undefined)";
    338                         }
    339                         /* value size binding type section symname */
    340                         PRINT("%-15s %8zd: %08llx %08llx %c%c %5d %n%s%n",
    341                               file,
    342                               index,
    343                               sym->st_value, sym->st_size, bind, type,
    344                               sym->st_shndx,
    345                               &till_lineno,
    346                               section_name,
    347                               &lineno);
    348                         lineno -= till_lineno;
    349                         /* Create padding for section names of 15 chars.
    350                            This limit is somewhat arbitratry. */
    351                         while (lineno++ < 15) PRINT(" ");
    352                         PRINT("(%d) %s\n",
    353                               sym->st_name,
    354                               elf_strptr(elf, shdr.sh_link, sym->st_name));
    355                     }
    356                 }
    357             }
    358         } /* if (shdr.sh_type = SHT_DYNSYM) */
    359     } /* while ((scn = elf_nextscn (elf, scn)) != NULL) */
    360 }
    361