Home | History | Annotate | Download | only in libcorkscrew
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "Corkscrew"
     18 //#define LOG_NDEBUG 0
     19 
     20 #include <corkscrew/symbol_table.h>
     21 
     22 #include <stdlib.h>
     23 #include <fcntl.h>
     24 #include <string.h>
     25 #include <sys/stat.h>
     26 #include <sys/mman.h>
     27 #include <sys/exec_elf.h>
     28 #include <cutils/log.h>
     29 
     30 // Compare function for qsort
     31 static int qcompar(const void *a, const void *b) {
     32     const symbol_t* asym = (const symbol_t*)a;
     33     const symbol_t* bsym = (const symbol_t*)b;
     34     if (asym->start > bsym->start) return 1;
     35     if (asym->start < bsym->start) return -1;
     36     return 0;
     37 }
     38 
     39 // Compare function for bsearch
     40 static int bcompar(const void *key, const void *element) {
     41     uintptr_t addr = *(const uintptr_t*)key;
     42     const symbol_t* symbol = (const symbol_t*)element;
     43     if (addr < symbol->start) return -1;
     44     if (addr >= symbol->end) return 1;
     45     return 0;
     46 }
     47 
     48 symbol_table_t* load_symbol_table(const char *filename) {
     49     symbol_table_t* table = NULL;
     50     ALOGV("Loading symbol table from '%s'.", filename);
     51 
     52     int fd = open(filename, O_RDONLY);
     53     if (fd < 0) {
     54         goto out;
     55     }
     56 
     57     struct stat sb;
     58     if (fstat(fd, &sb)) {
     59         goto out_close;
     60     }
     61 
     62     size_t length = sb.st_size;
     63     char* base = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
     64     if (base == MAP_FAILED) {
     65         goto out_close;
     66     }
     67 
     68     // Parse the file header
     69     Elf32_Ehdr *hdr = (Elf32_Ehdr*)base;
     70     if (!IS_ELF(*hdr)) {
     71         goto out_close;
     72     }
     73     Elf32_Shdr *shdr = (Elf32_Shdr*)(base + hdr->e_shoff);
     74 
     75     // Search for the dynamic symbols section
     76     int sym_idx = -1;
     77     int dynsym_idx = -1;
     78     for (Elf32_Half 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(symbol_table_t));
     91     if(!table) {
     92         goto out_unmap;
     93     }
     94     table->num_symbols = 0;
     95 
     96     Elf32_Sym *dynsyms = NULL;
     97     int dynnumsyms = 0;
     98     char *dynstr = NULL;
     99     if (dynsym_idx != -1) {
    100         dynsyms = (Elf32_Sym*)(base + shdr[dynsym_idx].sh_offset);
    101         dynnumsyms = shdr[dynsym_idx].sh_size / shdr[dynsym_idx].sh_entsize;
    102         int dynstr_idx = shdr[dynsym_idx].sh_link;
    103         dynstr = base + shdr[dynstr_idx].sh_offset;
    104     }
    105 
    106     Elf32_Sym *syms = NULL;
    107     int numsyms = 0;
    108     char *str = NULL;
    109     if (sym_idx != -1) {
    110         syms = (Elf32_Sym*)(base + shdr[sym_idx].sh_offset);
    111         numsyms = shdr[sym_idx].sh_size / shdr[sym_idx].sh_entsize;
    112         int str_idx = shdr[sym_idx].sh_link;
    113         str = base + shdr[str_idx].sh_offset;
    114     }
    115 
    116     int dynsymbol_count = 0;
    117     if (dynsym_idx != -1) {
    118         // Iterate through the dynamic symbol table, and count how many symbols
    119         // are actually defined
    120         for (int i = 0; i < dynnumsyms; i++) {
    121             if (dynsyms[i].st_shndx != SHN_UNDEF) {
    122                 dynsymbol_count++;
    123             }
    124         }
    125     }
    126 
    127     size_t symbol_count = 0;
    128     if (sym_idx != -1) {
    129         // Iterate through the symbol table, and count how many symbols
    130         // are actually defined
    131         for (int i = 0; i < numsyms; i++) {
    132             if (syms[i].st_shndx != SHN_UNDEF
    133                     && str[syms[i].st_name]
    134                     && syms[i].st_value
    135                     && syms[i].st_size) {
    136                 symbol_count++;
    137             }
    138         }
    139     }
    140 
    141     // Now, create an entry in our symbol table structure for each symbol...
    142     table->num_symbols += symbol_count + dynsymbol_count;
    143     table->symbols = malloc(table->num_symbols * sizeof(symbol_t));
    144     if (!table->symbols) {
    145         free(table);
    146         table = NULL;
    147         goto out_unmap;
    148     }
    149 
    150     size_t symbol_index = 0;
    151     if (dynsym_idx != -1) {
    152         // ...and populate them
    153         for (int i = 0; i < dynnumsyms; i++) {
    154             if (dynsyms[i].st_shndx != SHN_UNDEF) {
    155                 table->symbols[symbol_index].name = strdup(dynstr + dynsyms[i].st_name);
    156                 table->symbols[symbol_index].start = dynsyms[i].st_value;
    157                 table->symbols[symbol_index].end = dynsyms[i].st_value + dynsyms[i].st_size;
    158                 ALOGV("  [%d] '%s' 0x%08x-0x%08x (DYNAMIC)",
    159                         symbol_index, table->symbols[symbol_index].name,
    160                         table->symbols[symbol_index].start, table->symbols[symbol_index].end);
    161                 symbol_index += 1;
    162             }
    163         }
    164     }
    165 
    166     if (sym_idx != -1) {
    167         // ...and populate them
    168         for (int i = 0; i < numsyms; i++) {
    169             if (syms[i].st_shndx != SHN_UNDEF
    170                     && str[syms[i].st_name]
    171                     && syms[i].st_value
    172                     && syms[i].st_size) {
    173                 table->symbols[symbol_index].name = strdup(str + syms[i].st_name);
    174                 table->symbols[symbol_index].start = syms[i].st_value;
    175                 table->symbols[symbol_index].end = syms[i].st_value + syms[i].st_size;
    176                 ALOGV("  [%d] '%s' 0x%08x-0x%08x",
    177                         symbol_index, table->symbols[symbol_index].name,
    178                         table->symbols[symbol_index].start, table->symbols[symbol_index].end);
    179                 symbol_index += 1;
    180             }
    181         }
    182     }
    183 
    184     // Sort the symbol table entries, so they can be bsearched later
    185     qsort(table->symbols, table->num_symbols, sizeof(symbol_t), qcompar);
    186 
    187 out_unmap:
    188     munmap(base, length);
    189 
    190 out_close:
    191     close(fd);
    192 
    193 out:
    194     return table;
    195 }
    196 
    197 void free_symbol_table(symbol_table_t* table) {
    198     if (table) {
    199         for (size_t i = 0; i < table->num_symbols; i++) {
    200             free(table->symbols[i].name);
    201         }
    202         free(table->symbols);
    203         free(table);
    204     }
    205 }
    206 
    207 const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr) {
    208     if (!table) return NULL;
    209     return (const symbol_t*)bsearch(&addr, table->symbols, table->num_symbols,
    210             sizeof(symbol_t), bcompar);
    211 }
    212