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