• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
build_symfilter(const char * name,Elf * elf,symfilter_t * filter,off_t fsize)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 
destroy_symfilter(symfilter_t * filter)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 
match_hash_table_section(Elf * elf,Elf_Scn * sect,void * data)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 
match_dynsym_section(Elf * elf,Elf_Scn * sect,void * data)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