Home | History | Annotate | Download | only in soslim
      1 #include <debug.h>
      2 #include <common.h>
      3 #include <symfilter.h>
      4 #include <hash.h>
      5 #include <stdio.h>
      6 #include <errno.h>
      7 #include <string.h>
      8 #include <unistd.h>
      9 #include <libelf.h>
     10 #include <gelf.h>
     11 #include <ctype.h>
     12 
     13 #include <sys/mman.h>
     14 #include <sys/types.h>
     15 #include <sys/stat.h>
     16 #include <fcntl.h>
     17 
     18 static int match_hash_table_section(Elf *elf, Elf_Scn *sect, void *data);
     19 static int match_dynsym_section(Elf *elf, Elf_Scn *sect, void *data);
     20 
     21 void build_symfilter(const char *name, Elf *elf, symfilter_t *filter,
     22                      off_t fsize)
     23 {
     24     char *line = NULL;
     25     symfilter_list_t *symbol;
     26 
     27     FAILIF(NULL == name,
     28            "You must provide a list of symbols to filter on!\n");
     29 
     30     filter->num_symbols = 0;
     31     filter->total_name_length = 0;
     32 
     33     /* Open the file. */
     34     INFO("Opening symbol-filter file %s...\n", name);
     35     filter->fd = open(name, O_RDONLY);
     36     FAILIF(filter->fd < 0, "open(%s): %s (%d)\n",
     37            name,
     38            strerror(errno),
     39            errno);
     40 
     41     INFO("Symbol-filter file %s is %zd bytes long...\n",
     42          name,
     43          (size_t)fsize);
     44     filter->fsize = fsize;
     45 
     46     /* mmap the symbols file */
     47     filter->mmap = mmap(NULL, fsize,
     48                         PROT_READ | PROT_WRITE, MAP_PRIVATE,
     49                         filter->fd, 0);
     50     FAILIF(MAP_FAILED == filter->mmap,
     51            "mmap(NULL, %zd, PROT_READ, MAP_PRIVATE, %d, 0): %s (%d)\n",
     52            (size_t)fsize,
     53            filter->fd,
     54            strerror(errno),
     55            errno);
     56     INFO("Memory-mapped symbol-filter file at %p\n", filter->mmap);
     57 
     58     /* Make sure that the ELF file has a hash table.  We will use the hash
     59        table to look up symbols quickly.  If the library does not have a hash-
     60        table section, we can still do a linear scan, but the code for that is
     61        not written, as practically every shared library has a hash table.
     62     */
     63 
     64     filter->symtab.sect = NULL;
     65     map_over_sections(elf, match_dynsym_section, filter);
     66     FAILIF(NULL == filter->symtab.sect,
     67            "There is no dynamic-symbol table in this library.\n");
     68     filter->hash.sect = NULL;
     69     map_over_sections(elf, match_hash_table_section, filter);
     70     FAILIF(NULL == filter->hash.sect,
     71            "There is no hash table in this library.\n");
     72     INFO("Hash table size 0x%lx, data size 0x%lx.\n",
     73          (unsigned long)filter->hash.hdr->sh_size,
     74          (unsigned long)filter->hash.data->d_size);
     75 
     76     INFO("Hash table file offset: 0x%x\n", filter->hash.hdr->sh_offset);
     77 
     78     GElf_Ehdr *ehdr, ehdr_mem;
     79     ehdr = gelf_getehdr(elf, &ehdr_mem);
     80     size_t symsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version);
     81     ASSERT(symsize);
     82     filter->num_symbols_to_keep = filter->symtab.data->d_size / symsize;
     83     filter->symbols_to_keep = (bool *)CALLOC(filter->num_symbols_to_keep,
     84                                              sizeof(bool));
     85 
     86     /* Build the symbol-name chain. */
     87     INFO("Building symbol list...\n");
     88 
     89     line = (char *)filter->mmap;
     90 
     91     filter->symbols = NULL;
     92 #define NOT_DONE ((off_t)(line - (char *)filter->mmap) < fsize)
     93     do {
     94         char *name = line;
     95 
     96         /* Advance to the next line.  We seek out spaces or new lines.  At the
     97            first space or newline character we find, we place a '\0', and
     98            continue till we've consumed the line.  For new lines, we scan both
     99            '\r' and '\n'.  For spaces, we look for ' ', '\t', and '\f'
    100         */
    101 
    102         while (NOT_DONE && !isspace(*line)) line++;
    103         if (likely(NOT_DONE)) {
    104             *line++ = '\0';
    105             if (line - name > 1) {
    106                 /* Add the entry to the symbol-filter list */
    107                 symbol = (symfilter_list_t *)MALLOC(sizeof(symfilter_list_t));
    108                 symbol->next = filter->symbols;
    109                 symbol->name = name;
    110                 filter->symbols = symbol;
    111 
    112 #if 0
    113                 /* SLOW!  For debugging only! */
    114                 {
    115                     size_t idx;
    116                     size_t elsize = gelf_fsize(elf, ELF_T_SYM, 1,
    117                                                ehdr->e_version);
    118                     symbol->index = SHN_UNDEF;
    119                     for (idx = 0; idx < filter->symtab.data->d_size / elsize;
    120                          idx++) {
    121                         GElf_Sym sym_mem;
    122                         GElf_Sym *sym;
    123                         const char *symname;
    124                         sym = gelf_getsymshndx (filter->symtab.data, NULL,
    125                                                 idx, &sym_mem, NULL);
    126                         ASSERT(sym);
    127 
    128                         symname = elf_strptr(elf,
    129                                              filter->symtab.hdr->sh_link,
    130                                              sym->st_name);
    131                         if(!strcmp(symname, symbol->name)) {
    132                             symbol->index = idx;
    133                             break;
    134                         }
    135                     }
    136                 }
    137 #else
    138                 /* Look up the symbol in the ELF file and associate it with the
    139                    entry in the filter. */
    140                 symbol->index = hash_lookup(elf,
    141                                             &filter->hash,
    142                                             &filter->symtab,
    143                                             symbol->name,
    144                                             &symbol->symbol);
    145 #endif
    146                 symbol->len = line - name - 1;
    147                 ASSERT(symbol->len == strlen(symbol->name));
    148 
    149                 /* If we didn't find the symbol, then it's not in the library.
    150                  */
    151 
    152                 if(STN_UNDEF == symbol->index) {
    153                     PRINT("%s: symbol was not found!\n", symbol->name);
    154                 }
    155                 else {
    156                     /* If we found the symbol but it's an undefined symbol, then
    157                        it's not in the library as well. */
    158                     GElf_Sym sym_mem;
    159                     GElf_Sym *sym;
    160                     sym = gelf_getsymshndx (filter->symtab.data, NULL,
    161                                             symbol->index, &sym_mem, NULL);
    162                     FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
    163                     /* Make sure the hash lookup worked. */
    164                     ASSERT(!strcmp(elf_strptr(elf,
    165                                               filter->symtab.hdr->sh_link,
    166                                               sym->st_name),
    167                                    symbol->name));
    168                     if (sym->st_shndx == SHN_UNDEF) {
    169                         PRINT("%s: symbol was not found (undefined)!\n", symbol->name);
    170                     }
    171                     else {
    172                         filter->num_symbols++;
    173                         /* Total count includes null terminators */
    174                         filter->total_name_length += symbol->len + 1;
    175 
    176                         /* Set the flag in the symbols_to_keep[] array.  This indicates
    177                            to function copy_elf() that we want to keep the symbol.
    178                         */
    179                         filter->symbols_to_keep[symbol->index] = true;
    180                         INFO("FILTER-SYMBOL: [%s] [%d bytes]\n",
    181                              symbol->name,
    182                              symbol->len);
    183                     }
    184                 }
    185             }
    186         }
    187     } while (NOT_DONE);
    188 #undef NOT_DONE
    189 }
    190 
    191 void destroy_symfilter(symfilter_t *filter)
    192 {
    193     symfilter_list_t *old;
    194     INFO("Destroying symbol list...\n");
    195     while ((old = filter->symbols)) {
    196         filter->symbols = old->next;
    197         FREE(old);
    198     }
    199     munmap(filter->mmap, filter->fsize);
    200     close(filter->fd);
    201 }
    202 
    203 static int match_hash_table_section(Elf *elf, Elf_Scn *sect, void *data)
    204 {
    205     (void)elf; // unused argument
    206 
    207     symfilter_t *filter = (symfilter_t *)data;
    208     Elf32_Shdr *shdr;
    209 
    210     ASSERT(filter);
    211     ASSERT(sect);
    212     shdr = elf32_getshdr(sect);
    213 
    214     /* The section must be marked both as a SHT_HASH, and it's sh_link field
    215        must contain the index of our symbol table (per ELF-file spec).
    216     */
    217     if (shdr->sh_type == SHT_HASH)
    218     {
    219         FAILIF(filter->hash.sect != NULL,
    220                "There is more than one hash table!\n");
    221         get_section_info(sect, &filter->hash);
    222     }
    223 
    224     return 0; /* keep looking */
    225 }
    226 
    227 static int match_dynsym_section(Elf *elf, Elf_Scn *sect, void *data)
    228 {
    229     (void)elf; // unused argument
    230 
    231     symfilter_t *filter = (symfilter_t *)data;
    232     Elf32_Shdr *shdr;
    233 
    234     ASSERT(filter);
    235     ASSERT(sect);
    236     shdr = elf32_getshdr(sect);
    237 
    238     if (shdr->sh_type == SHT_DYNSYM)
    239     {
    240         FAILIF(filter->symtab.sect != NULL,
    241                "There is more than one dynamic symbol table!\n");
    242         get_section_info(sect, &filter->symtab);
    243     }
    244 
    245     return 0; /* keep looking */
    246 }
    247