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.
ElfHash(const char * name)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 
Init(const ElfView * view)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 
LookupByAddress(void * address,size_t load_bias) const63 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 
LookupNearestByAddress(void * address,size_t load_bias,const char ** sym_name,void ** sym_addr,size_t * sym_size) const78 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 
LookupByName(const char * symbol_name) const122 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