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 10namespace crazy { 11 12namespace { 13 14// Compute the ELF hash of a given symbol. 15unsigned 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 29bool 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 63const 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 78bool 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 122const 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