#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 */
}