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 %ld bytes long...\n", 42 name, 43 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, %ld, PROT_READ, MAP_PRIVATE, %d, 0): %s (%d)\n", 52 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 symfilter_t *filter = (symfilter_t *)data; 206 Elf32_Shdr *shdr; 207 208 ASSERT(filter); 209 ASSERT(sect); 210 shdr = elf32_getshdr(sect); 211 212 /* The section must be marked both as a SHT_HASH, and it's sh_link field 213 must contain the index of our symbol table (per ELF-file spec). 214 */ 215 if (shdr->sh_type == SHT_HASH) 216 { 217 FAILIF(filter->hash.sect != NULL, 218 "There is more than one hash table!\n"); 219 get_section_info(sect, &filter->hash); 220 } 221 222 return 0; /* keep looking */ 223 } 224 225 static int match_dynsym_section(Elf *elf, Elf_Scn *sect, void *data) 226 { 227 symfilter_t *filter = (symfilter_t *)data; 228 Elf32_Shdr *shdr; 229 230 ASSERT(filter); 231 ASSERT(sect); 232 shdr = elf32_getshdr(sect); 233 234 if (shdr->sh_type == SHT_DYNSYM) 235 { 236 FAILIF(filter->symtab.sect != NULL, 237 "There is more than one dynamic symbol table!\n"); 238 get_section_info(sect, &filter->symtab); 239 } 240 241 return 0; /* keep looking */ 242 } 243