Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "crazy_linker_elf_symbols.h"
      6 
      7 #include "crazy_linker_debug.h"
      8 #include "crazy_linker_elf_view.h"
      9 
     10 namespace crazy {
     11 
     12 namespace {
     13 
     14 // Compute the ELF hash of a given symbol.
     15 unsigned ElfHash(const char* name) {
     16   const uint8_t* ptr = reinterpret_cast<const uint8_t*>(name);
     17   unsigned h = 0;
     18   while (*ptr) {
     19     h = (h << 4) + *ptr++;
     20     unsigned g = h & 0xf0000000;
     21     h ^= g;
     22     h ^= g >> 24;
     23   }
     24   return h;
     25 }
     26 
     27 }  // namespace
     28 
     29 bool ElfSymbols::Init(const ElfView* view) {
     30   LOG("%s: Parsing dynamic table\n", __FUNCTION__);
     31   ElfView::DynamicIterator dyn(view);
     32   for (; dyn.HasNext(); dyn.GetNext()) {
     33     uintptr_t dyn_addr = dyn.GetAddress(view->load_bias());
     34     switch (dyn.GetTag()) {
     35       case DT_HASH:
     36         LOG("  DT_HASH addr=%p\n", dyn_addr);
     37         {
     38           ELF::Word* data = reinterpret_cast<ELF::Word*>(dyn_addr);
     39           hash_bucket_size_ = data[0];
     40           hash_chain_size_ = data[1];
     41           hash_bucket_ = data + 2;
     42           hash_chain_ = data + 2 + hash_bucket_size_;
     43         }
     44         break;
     45       case DT_STRTAB:
     46         LOG("  DT_STRTAB addr=%p\n", dyn_addr);
     47         string_table_ = reinterpret_cast<const char*>(dyn_addr);
     48         break;
     49       case DT_SYMTAB:
     50         LOG("  DT_SYMTAB addr=%p\n", dyn_addr);
     51         symbol_table_ = reinterpret_cast<const ELF::Sym*>(dyn_addr);
     52         break;
     53       default:
     54         ;
     55     }
     56   }
     57   if (symbol_table_ == NULL || string_table_ == NULL || hash_bucket_ == NULL)
     58     return false;
     59 
     60   return true;
     61 }
     62 
     63 const ELF::Sym* ElfSymbols::LookupByAddress(void* address,
     64                                             size_t load_bias) const {
     65   ELF::Addr elf_addr =
     66       reinterpret_cast<ELF::Addr>(address) - static_cast<ELF::Addr>(load_bias);
     67 
     68   for (size_t n = 0; n < hash_chain_size_; ++n) {
     69     const ELF::Sym* sym = &symbol_table_[n];
     70     if (sym->st_shndx != SHN_UNDEF && elf_addr >= sym->st_value &&
     71         elf_addr < sym->st_value + sym->st_size) {
     72       return sym;
     73     }
     74   }
     75   return NULL;
     76 }
     77 
     78 bool ElfSymbols::LookupNearestByAddress(void* address,
     79                                         size_t load_bias,
     80                                         const char** sym_name,
     81                                         void** sym_addr,
     82                                         size_t* sym_size) const {
     83   ELF::Addr elf_addr =
     84       reinterpret_cast<ELF::Addr>(address) - static_cast<ELF::Addr>(load_bias);
     85 
     86   const ELF::Sym* nearest_sym = NULL;
     87   size_t nearest_diff = ~size_t(0);
     88 
     89   for (size_t n = 0; n < hash_chain_size_; ++n) {
     90     const ELF::Sym* sym = &symbol_table_[n];
     91     if (sym->st_shndx == SHN_UNDEF)
     92       continue;
     93 
     94     if (elf_addr >= sym->st_value && elf_addr < sym->st_value + sym->st_size) {
     95       // This is a perfect match.
     96       nearest_sym = sym;
     97       break;
     98     }
     99 
    100     // Otherwise, compute distance.
    101     size_t diff;
    102     if (elf_addr < sym->st_value)
    103       diff = sym->st_value - elf_addr;
    104     else
    105       diff = elf_addr - sym->st_value - sym->st_size;
    106 
    107     if (diff < nearest_diff) {
    108       nearest_sym = sym;
    109       nearest_diff = diff;
    110     }
    111   }
    112 
    113   if (!nearest_sym)
    114     return false;
    115 
    116   *sym_name = string_table_ + nearest_sym->st_name;
    117   *sym_addr = reinterpret_cast<void*>(nearest_sym->st_value + load_bias);
    118   *sym_size = nearest_sym->st_size;
    119   return true;
    120 }
    121 
    122 const ELF::Sym* ElfSymbols::LookupByName(const char* symbol_name) const {
    123   unsigned hash = ElfHash(symbol_name);
    124 
    125   for (unsigned n = hash_bucket_[hash % hash_bucket_size_]; n != 0;
    126        n = hash_chain_[n]) {
    127     const ELF::Sym* symbol = &symbol_table_[n];
    128     // Check that the symbol has the appropriate name.
    129     if (strcmp(string_table_ + symbol->st_name, symbol_name))
    130       continue;
    131     // Ignore undefined symbols.
    132     if (symbol->st_shndx == SHN_UNDEF)
    133       continue;
    134     // Ignore anything that isn't a global or weak definition.
    135     switch (ELF_ST_BIND(symbol->st_info)) {
    136       case STB_GLOBAL:
    137       case STB_WEAK:
    138         return symbol;
    139       default:
    140         ;
    141     }
    142   }
    143   return NULL;
    144 }
    145 
    146 }  // namespace crazy
    147