summaryrefslogtreecommitdiffstats
path: root/tools/soslim/symfilter.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/soslim/symfilter.c')
-rw-r--r--tools/soslim/symfilter.c242
1 files changed, 242 insertions, 0 deletions
diff --git a/tools/soslim/symfilter.c b/tools/soslim/symfilter.c
new file mode 100644
index 0000000..c21ab2e
--- /dev/null
+++ b/tools/soslim/symfilter.c
@@ -0,0 +1,242 @@
+#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 */
+}