243 lines
8.6 KiB
C
243 lines
8.6 KiB
C
#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 */
|
|
}
|