1#include <stdlib.h>
2#include <fcntl.h>
3#include <string.h>
4#include <sys/stat.h>
5#include <sys/mman.h>
6
7#include "symbol_table.h"
8#include "utility.h"
9
10#include <linux/elf.h>
11
12// Compare func for qsort
13static int qcompar(const void *a, const void *b)
14{
15    return ((struct symbol*)a)->addr - ((struct symbol*)b)->addr;
16}
17
18// Compare func for bsearch
19static int bcompar(const void *addr, const void *element)
20{
21    struct symbol *symbol = (struct symbol*)element;
22
23    if((unsigned int)addr < symbol->addr) {
24        return -1;
25    }
26
27    if((unsigned int)addr - symbol->addr >= symbol->size) {
28        return 1;
29    }
30
31    return 0;
32}
33
34/*
35 *  Create a symbol table from a given file
36 *
37 *  Parameters:
38 *      filename - Filename to process
39 *
40 *  Returns:
41 *      A newly-allocated SymbolTable structure, or NULL if error.
42 *      Free symbol table with symbol_table_free()
43 */
44struct symbol_table *symbol_table_create(const char *filename)
45{
46    struct symbol_table *table = NULL;
47
48    // Open the file, and map it into memory
49    struct stat sb;
50    int length;
51    char *base;
52
53    XLOG2("Creating symbol table for %s\n", filename);
54    int fd = open(filename, O_RDONLY);
55
56    if(fd < 0) {
57        goto out;
58    }
59
60    fstat(fd, &sb);
61    length = sb.st_size;
62
63    base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
64
65    if(!base) {
66        goto out_close;
67    }
68
69    // Parse the file header
70    Elf32_Ehdr *hdr = (Elf32_Ehdr*)base;
71    Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff);
72
73    // Search for the dynamic symbols section
74    int sym_idx = -1;
75    int dynsym_idx = -1;
76    int i;
77
78    for(i = 0; i < hdr->e_shnum; i++) {
79        if(shdr[i].sh_type == SHT_SYMTAB ) {
80            sym_idx = i;
81        }
82        if(shdr[i].sh_type == SHT_DYNSYM ) {
83            dynsym_idx = i;
84        }
85    }
86    if ((dynsym_idx == -1) && (sym_idx == -1)) {
87        goto out_unmap;
88    }
89
90    table = malloc(sizeof(struct symbol_table));
91    if(!table) {
92        goto out_unmap;
93    }
94    table->name = strdup(filename);
95    table->num_symbols = 0;
96
97    Elf32_Sym *dynsyms = NULL;
98    Elf32_Sym *syms = NULL;
99    int dynnumsyms = 0;
100    int numsyms = 0;
101    char *dynstr = NULL;
102    char *str = NULL;
103
104    if (dynsym_idx != -1) {
105        dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset);
106        dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize;
107        int dynstr_idx = shdr[dynsym_idx].sh_link;
108        dynstr = base + shdr[dynstr_idx].sh_offset;
109    }
110
111    if (sym_idx != -1) {
112        syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset);
113        numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize;
114        int str_idx = shdr[sym_idx].sh_link;
115        str = base + shdr[str_idx].sh_offset;
116    }
117
118    int symbol_count = 0;
119    int dynsymbol_count = 0;
120
121    if (dynsym_idx != -1) {
122        // Iterate through the dynamic symbol table, and count how many symbols
123        // are actually defined
124        for(i = 0; i < dynnumsyms; i++) {
125            if(dynsyms[i].st_shndx != SHN_UNDEF) {
126                dynsymbol_count++;
127            }
128        }
129        XLOG2("Dynamic Symbol count: %d\n", dynsymbol_count);
130    }
131
132    if (sym_idx != -1) {
133        // Iterate through the symbol table, and count how many symbols
134        // are actually defined
135        for(i = 0; i < numsyms; i++) {
136            if((syms[i].st_shndx != SHN_UNDEF) &&
137                (strlen(str+syms[i].st_name)) &&
138                (syms[i].st_value != 0) && (syms[i].st_size != 0)) {
139                symbol_count++;
140            }
141        }
142        XLOG2("Symbol count: %d\n", symbol_count);
143    }
144
145    // Now, create an entry in our symbol table structure for each symbol...
146    table->num_symbols += symbol_count + dynsymbol_count;
147    table->symbols = malloc(table->num_symbols * sizeof(struct symbol));
148    if(!table->symbols) {
149        free(table);
150        table = NULL;
151        goto out_unmap;
152    }
153
154
155    int j = 0;
156    if (dynsym_idx != -1) {
157        // ...and populate them
158        for(i = 0; i < dynnumsyms; i++) {
159            if(dynsyms[i].st_shndx != SHN_UNDEF) {
160                table->symbols[j].name = strdup(dynstr + dynsyms[i].st_name);
161                table->symbols[j].addr = dynsyms[i].st_value;
162                table->symbols[j].size = dynsyms[i].st_size;
163                XLOG2("name: %s, addr: %x, size: %x\n",
164                    table->symbols[j].name, table->symbols[j].addr, table->symbols[j].size);
165                j++;
166            }
167        }
168    }
169
170    if (sym_idx != -1) {
171        // ...and populate them
172        for(i = 0; i < numsyms; i++) {
173            if((syms[i].st_shndx != SHN_UNDEF) &&
174                (strlen(str+syms[i].st_name)) &&
175                (syms[i].st_value != 0) && (syms[i].st_size != 0)) {
176                table->symbols[j].name = strdup(str + syms[i].st_name);
177                table->symbols[j].addr = syms[i].st_value;
178                table->symbols[j].size = syms[i].st_size;
179                XLOG2("name: %s, addr: %x, size: %x\n",
180                    table->symbols[j].name, table->symbols[j].addr, table->symbols[j].size);
181                j++;
182            }
183        }
184    }
185
186    // Sort the symbol table entries, so they can be bsearched later
187    qsort(table->symbols, table->num_symbols, sizeof(struct symbol), qcompar);
188
189out_unmap:
190    munmap(base, length);
191
192out_close:
193    close(fd);
194
195out:
196    return table;
197}
198
199/*
200 * Free a symbol table
201 *
202 * Parameters:
203 *     table - Table to free
204 */
205void symbol_table_free(struct symbol_table *table)
206{
207    int i;
208
209    if(!table) {
210        return;
211    }
212
213    for(i=0; i<table->num_symbols; i++) {
214        free(table->symbols[i].name);
215    }
216
217    free(table->symbols);
218    free(table);
219}
220
221/*
222 * Search for an address in the symbol table
223 *
224 * Parameters:
225 *      table - Table to search in
226 *      addr - Address to search for.
227 *
228 * Returns:
229 *      A pointer to the Symbol structure corresponding to the
230 *      symbol which contains this address, or NULL if no symbol
231 *      contains it.
232 */
233const struct symbol *symbol_table_lookup(struct symbol_table *table, unsigned int addr)
234{
235    if(!table) {
236        return NULL;
237    }
238
239    return bsearch((void*)addr, table->symbols, table->num_symbols, sizeof(struct symbol), bcompar);
240}
241