| #include <debug.h> |
| #include <common.h> |
| #include <symfilter.h> |
| #include <hash.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <libelf.h> |
| #include <gelf.h> |
| #include <ctype.h> |
| |
| #include <sys/mman.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| static int match_hash_table_section(Elf *elf, Elf_Scn *sect, void *data); |
| static int match_dynsym_section(Elf *elf, Elf_Scn *sect, void *data); |
| |
| void build_symfilter(const char *name, Elf *elf, symfilter_t *filter, |
| off_t fsize) |
| { |
| char *line = NULL; |
| symfilter_list_t *symbol; |
| |
| FAILIF(NULL == name, |
| "You must provide a list of symbols to filter on!\n"); |
| |
| filter->num_symbols = 0; |
| filter->total_name_length = 0; |
| |
| /* Open the file. */ |
| INFO("Opening symbol-filter file %s...\n", name); |
| filter->fd = open(name, O_RDONLY); |
| FAILIF(filter->fd < 0, "open(%s): %s (%d)\n", |
| name, |
| strerror(errno), |
| errno); |
| |
| INFO("Symbol-filter file %s is %ld bytes long...\n", |
| name, |
| fsize); |
| filter->fsize = fsize; |
| |
| /* mmap the symbols file */ |
| filter->mmap = mmap(NULL, fsize, |
| PROT_READ | PROT_WRITE, MAP_PRIVATE, |
| filter->fd, 0); |
| FAILIF(MAP_FAILED == filter->mmap, |
| "mmap(NULL, %ld, PROT_READ, MAP_PRIVATE, %d, 0): %s (%d)\n", |
| fsize, |
| filter->fd, |
| strerror(errno), |
| errno); |
| INFO("Memory-mapped symbol-filter file at %p\n", filter->mmap); |
| |
| /* Make sure that the ELF file has a hash table. We will use the hash |
| table to look up symbols quickly. If the library does not have a hash- |
| table section, we can still do a linear scan, but the code for that is |
| not written, as practically every shared library has a hash table. |
| */ |
| |
| filter->symtab.sect = NULL; |
| map_over_sections(elf, match_dynsym_section, filter); |
| FAILIF(NULL == filter->symtab.sect, |
| "There is no dynamic-symbol table in this library.\n"); |
| filter->hash.sect = NULL; |
| map_over_sections(elf, match_hash_table_section, filter); |
| FAILIF(NULL == filter->hash.sect, |
| "There is no hash table in this library.\n"); |
| INFO("Hash table size 0x%lx, data size 0x%lx.\n", |
| (unsigned long)filter->hash.hdr->sh_size, |
| (unsigned long)filter->hash.data->d_size); |
| |
| INFO("Hash table file offset: 0x%x\n", filter->hash.hdr->sh_offset); |
| |
| GElf_Ehdr *ehdr, ehdr_mem; |
| ehdr = gelf_getehdr(elf, &ehdr_mem); |
| size_t symsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version); |
| ASSERT(symsize); |
| filter->num_symbols_to_keep = filter->symtab.data->d_size / symsize; |
| filter->symbols_to_keep = (bool *)CALLOC(filter->num_symbols_to_keep, |
| sizeof(bool)); |
| |
| /* Build the symbol-name chain. */ |
| INFO("Building symbol list...\n"); |
| |
| line = (char *)filter->mmap; |
| |
| filter->symbols = NULL; |
| #define NOT_DONE ((off_t)(line - (char *)filter->mmap) < fsize) |
| do { |
| char *name = line; |
| |
| /* Advance to the next line. We seek out spaces or new lines. At the |
| first space or newline character we find, we place a '\0', and |
| continue till we've consumed the line. For new lines, we scan both |
| '\r' and '\n'. For spaces, we look for ' ', '\t', and '\f' |
| */ |
| |
| while (NOT_DONE && !isspace(*line)) line++; |
| if (likely(NOT_DONE)) { |
| *line++ = '\0'; |
| if (line - name > 1) { |
| /* Add the entry to the symbol-filter list */ |
| symbol = (symfilter_list_t *)MALLOC(sizeof(symfilter_list_t)); |
| symbol->next = filter->symbols; |
| symbol->name = name; |
| filter->symbols = symbol; |
| |
| #if 0 |
| /* SLOW! For debugging only! */ |
| { |
| size_t idx; |
| size_t elsize = gelf_fsize(elf, ELF_T_SYM, 1, |
| ehdr->e_version); |
| symbol->index = SHN_UNDEF; |
| for (idx = 0; idx < filter->symtab.data->d_size / elsize; |
| idx++) { |
| GElf_Sym sym_mem; |
| GElf_Sym *sym; |
| const char *symname; |
| sym = gelf_getsymshndx (filter->symtab.data, NULL, |
| idx, &sym_mem, NULL); |
| ASSERT(sym); |
| |
| symname = elf_strptr(elf, |
| filter->symtab.hdr->sh_link, |
| sym->st_name); |
| if(!strcmp(symname, symbol->name)) { |
| symbol->index = idx; |
| break; |
| } |
| } |
| } |
| #else |
| /* Look up the symbol in the ELF file and associate it with the |
| entry in the filter. */ |
| symbol->index = hash_lookup(elf, |
| &filter->hash, |
| &filter->symtab, |
| symbol->name, |
| &symbol->symbol); |
| #endif |
| symbol->len = line - name - 1; |
| ASSERT(symbol->len == strlen(symbol->name)); |
| |
| /* If we didn't find the symbol, then it's not in the library. |
| */ |
| |
| if(STN_UNDEF == symbol->index) { |
| PRINT("%s: symbol was not found!\n", symbol->name); |
| } |
| else { |
| /* If we found the symbol but it's an undefined symbol, then |
| it's not in the library as well. */ |
| GElf_Sym sym_mem; |
| GElf_Sym *sym; |
| sym = gelf_getsymshndx (filter->symtab.data, NULL, |
| symbol->index, &sym_mem, NULL); |
| FAILIF_LIBELF(NULL == sym, gelf_getsymshndx); |
| /* Make sure the hash lookup worked. */ |
| ASSERT(!strcmp(elf_strptr(elf, |
| filter->symtab.hdr->sh_link, |
| sym->st_name), |
| symbol->name)); |
| if (sym->st_shndx == SHN_UNDEF) { |
| PRINT("%s: symbol was not found (undefined)!\n", symbol->name); |
| } |
| else { |
| filter->num_symbols++; |
| /* Total count includes null terminators */ |
| filter->total_name_length += symbol->len + 1; |
| |
| /* Set the flag in the symbols_to_keep[] array. This indicates |
| to function copy_elf() that we want to keep the symbol. |
| */ |
| filter->symbols_to_keep[symbol->index] = true; |
| INFO("FILTER-SYMBOL: [%s] [%d bytes]\n", |
| symbol->name, |
| symbol->len); |
| } |
| } |
| } |
| } |
| } while (NOT_DONE); |
| #undef NOT_DONE |
| } |
| |
| void destroy_symfilter(symfilter_t *filter) |
| { |
| symfilter_list_t *old; |
| INFO("Destroying symbol list...\n"); |
| while ((old = filter->symbols)) { |
| filter->symbols = old->next; |
| FREE(old); |
| } |
| munmap(filter->mmap, filter->fsize); |
| close(filter->fd); |
| } |
| |
| static int match_hash_table_section(Elf *elf, Elf_Scn *sect, void *data) |
| { |
| symfilter_t *filter = (symfilter_t *)data; |
| Elf32_Shdr *shdr; |
| |
| ASSERT(filter); |
| ASSERT(sect); |
| shdr = elf32_getshdr(sect); |
| |
| /* The section must be marked both as a SHT_HASH, and it's sh_link field |
| must contain the index of our symbol table (per ELF-file spec). |
| */ |
| if (shdr->sh_type == SHT_HASH) |
| { |
| FAILIF(filter->hash.sect != NULL, |
| "There is more than one hash table!\n"); |
| get_section_info(sect, &filter->hash); |
| } |
| |
| return 0; /* keep looking */ |
| } |
| |
| static int match_dynsym_section(Elf *elf, Elf_Scn *sect, void *data) |
| { |
| symfilter_t *filter = (symfilter_t *)data; |
| Elf32_Shdr *shdr; |
| |
| ASSERT(filter); |
| ASSERT(sect); |
| shdr = elf32_getshdr(sect); |
| |
| if (shdr->sh_type == SHT_DYNSYM) |
| { |
| FAILIF(filter->symtab.sect != NULL, |
| "There is more than one dynamic symbol table!\n"); |
| get_section_info(sect, &filter->symtab); |
| } |
| |
| return 0; /* keep looking */ |
| } |