crazy_linker_library_list.cpp revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
1// Copyright 2014 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_library_list.h"
6
7#include <dlfcn.h>
8
9#include "crazy_linker_debug.h"
10#include "crazy_linker_library_view.h"
11#include "crazy_linker_globals.h"
12#include "crazy_linker_rdebug.h"
13#include "crazy_linker_shared_library.h"
14#include "crazy_linker_system.h"
15
16namespace crazy {
17
18namespace {
19
20// A helper struct used when looking up symbols in libraries.
21struct SymbolLookupState {
22  void* found_addr;
23  void* weak_addr;
24  int weak_count;
25
26  SymbolLookupState() : found_addr(NULL), weak_addr(NULL), weak_count(0) {}
27
28  // Check a symbol entry.
29  bool CheckSymbol(const char* symbol, SharedLibrary* lib) {
30    const ELF::Sym* entry = lib->LookupSymbolEntry(symbol);
31    if (!entry)
32      return false;
33
34    void* address = reinterpret_cast<void*>(lib->load_bias() + entry->st_value);
35
36    // If this is a strong symbol, record it and return true.
37    if (ELF_ST_BIND(entry->st_info) == STB_GLOBAL) {
38      found_addr = address;
39      return true;
40    }
41    // If this is a weak symbol, record the first one and
42    // increment the weak_count.
43    if (++weak_count == 1)
44      weak_addr = address;
45
46    return false;
47  }
48};
49
50}  // namespace
51
52LibraryList::LibraryList() : head_(0), count_(0), has_error_(false) {
53  // Nothing for now
54}
55
56LibraryList::~LibraryList() {
57  // Invalidate crazy library list.
58  head_ = NULL;
59
60  // Destroy all known libraries.
61  while (!known_libraries_.IsEmpty()) {
62    LibraryView* wrap = known_libraries_.PopLast();
63    delete wrap;
64  }
65}
66
67LibraryView* LibraryList::FindLibraryByName(const char* lib_name) {
68  // Sanity check.
69  if (!lib_name)
70    return NULL;
71
72  for (size_t n = 0; n < known_libraries_.GetCount(); ++n) {
73    LibraryView* wrap = known_libraries_[n];
74    if (!strcmp(lib_name, wrap->GetName()))
75      return wrap;
76  }
77  return NULL;
78}
79
80void* LibraryList::FindSymbolFrom(const char* symbol_name, LibraryView* from) {
81  SymbolLookupState lookup_state;
82
83  if (!from)
84    return NULL;
85
86  // Use a work-queue and a set to ensure to perform a breadth-first
87  // search.
88  Vector<LibraryView*> work_queue;
89  Set<LibraryView*> visited_set;
90
91  work_queue.PushBack(from);
92
93  while (!work_queue.IsEmpty()) {
94    LibraryView* lib = work_queue.PopFirst();
95    if (lib->IsCrazy()) {
96      if (lookup_state.CheckSymbol(symbol_name, lib->GetCrazy()))
97        return lookup_state.found_addr;
98    } else if (lib->IsSystem()) {
99      // TODO(digit): Support weak symbols in system libraries.
100      // With the current code, all symbols in system libraries
101      // are assumed to be non-weak.
102      void* addr = lib->LookupSymbol(symbol_name);
103      if (addr)
104        return addr;
105    }
106
107    // If this is a crazy library, add non-visited dependencies
108    // to the work queue.
109    if (lib->IsCrazy()) {
110      SharedLibrary::DependencyIterator iter(lib->GetCrazy());
111      while (iter.GetNext()) {
112        LibraryView* dependency = FindKnownLibrary(iter.GetName());
113        if (dependency && !visited_set.Has(dependency)) {
114          work_queue.PushBack(dependency);
115          visited_set.Add(dependency);
116        }
117      }
118    }
119  }
120
121  if (lookup_state.weak_count >= 1) {
122    // There was at least a single weak symbol definition, so use
123    // the first one found in breadth-first search order.
124    return lookup_state.weak_addr;
125  }
126
127  // There was no symbol definition.
128  return NULL;
129}
130
131LibraryView* LibraryList::FindLibraryForAddress(void* address) {
132  // Linearly scan all libraries, looking for one that contains
133  // a given address. NOTE: This doesn't check that this falls
134  // inside one of the mapped library segments.
135  for (size_t n = 0; n < known_libraries_.GetCount(); ++n) {
136    LibraryView* wrap = known_libraries_[n];
137    // TODO(digit): Search addresses inside system libraries.
138    if (wrap->IsCrazy()) {
139      SharedLibrary* lib = wrap->GetCrazy();
140      if (lib->ContainsAddress(address))
141        return wrap;
142    }
143  }
144  return NULL;
145}
146
147#ifdef __arm__
148_Unwind_Ptr LibraryList::FindArmExIdx(void* pc, int* count) {
149  for (SharedLibrary* lib = head_; lib; lib = lib->list_next_) {
150    if (lib->ContainsAddress(pc)) {
151      *count = static_cast<int>(lib->arm_exidx_count_);
152      return reinterpret_cast<_Unwind_Ptr>(lib->arm_exidx_);
153    }
154  }
155  *count = 0;
156  return NULL;
157}
158#else  // !__arm__
159int LibraryList::IteratePhdr(PhdrIterationCallback callback, void* data) {
160  int result = 0;
161  for (SharedLibrary* lib = head_; lib; lib = lib->list_next_) {
162    dl_phdr_info info;
163    info.dlpi_addr = lib->link_map_.l_addr;
164    info.dlpi_name = lib->link_map_.l_name;
165    info.dlpi_phdr = lib->phdr();
166    info.dlpi_phnum = lib->phdr_count();
167    result = callback(&info, sizeof(info), data);
168    if (result)
169      break;
170  }
171  return result;
172}
173#endif  // !__arm__
174
175void LibraryList::UnloadLibrary(LibraryView* wrap) {
176  // Sanity check.
177  LOG("%s: for %s (ref_count=%d)\n",
178      __FUNCTION__,
179      wrap->GetName(),
180      wrap->ref_count());
181
182  if (!wrap->IsSystem() && !wrap->IsCrazy())
183    return;
184
185  if (!wrap->SafeDecrementRef())
186    return;
187
188  // If this is a crazy library, perform manual cleanup first.
189  if (wrap->IsCrazy()) {
190    SharedLibrary* lib = wrap->GetCrazy();
191
192    // Remove from internal list of crazy libraries.
193    if (lib->list_next_)
194      lib->list_next_->list_prev_ = lib->list_prev_;
195    if (lib->list_prev_)
196      lib->list_prev_->list_next_ = lib->list_next_;
197    if (lib == head_)
198      head_ = lib->list_next_;
199
200    // Call JNI_OnUnload, if necessary, then the destructors.
201    lib->CallJniOnUnload();
202    lib->CallDestructors();
203
204    // Unload the dependencies recursively.
205    SharedLibrary::DependencyIterator iter(lib);
206    while (iter.GetNext()) {
207      LibraryView* dependency = FindKnownLibrary(iter.GetName());
208      if (dependency)
209        UnloadLibrary(dependency);
210    }
211
212    // Tell GDB of this removal.
213    Globals::GetRDebug()->DelEntry(&lib->link_map_);
214  }
215
216  known_libraries_.Remove(wrap);
217
218  // Delete the wrapper, which will delete the crazy library, or
219  // dlclose() the system one.
220  delete wrap;
221}
222
223LibraryView* LibraryList::LoadLibrary(const char* lib_name,
224                                      int dlopen_mode,
225                                      uintptr_t load_address,
226                                      off_t file_offset,
227                                      SearchPathList* search_path_list,
228                                      Error* error) {
229
230  const char* base_name = GetBaseNamePtr(lib_name);
231
232  LOG("%s: lib_name='%s'\n", __FUNCTION__, lib_name);
233
234  // First check whether a library with the same base name was
235  // already loaded.
236  LibraryView* wrap = FindKnownLibrary(lib_name);
237  if (wrap) {
238    if (load_address) {
239      // Check that this is a crazy library and that is was loaded at
240      // the correct address.
241      if (!wrap->IsCrazy()) {
242        error->Format("System library can't be loaded at fixed address %08x",
243                      load_address);
244        return NULL;
245      }
246      uintptr_t actual_address = wrap->GetCrazy()->load_address();
247      if (actual_address != load_address) {
248        error->Format("Library already loaded at @%08x, can't load it at @%08x",
249                      actual_address,
250                      load_address);
251        return NULL;
252      }
253    }
254    wrap->AddRef();
255    return wrap;
256  }
257
258  if (IsSystemLibrary(lib_name)) {
259    // This is a system library, probably because we're loading the
260    // library as a dependency.
261    LOG("%s: Loading system library '%s'\n", __FUNCTION__, lib_name);
262    ::dlerror();
263    void* system_lib = dlopen(lib_name, dlopen_mode);
264    if (!system_lib) {
265      error->Format("Can't load system library %s: %s", lib_name, ::dlerror());
266      return NULL;
267    }
268
269    LibraryView* wrap = new LibraryView();
270    wrap->SetSystem(system_lib, lib_name);
271    known_libraries_.PushBack(wrap);
272
273    LOG("%s: System library %s loaded at %p\n", __FUNCTION__, lib_name, wrap);
274    LOG("  name=%s\n", wrap->GetName());
275    return wrap;
276  }
277
278  ScopedPtr<SharedLibrary> lib(new SharedLibrary());
279
280  // Find the full library path.
281  String full_path;
282
283  if (!strchr(lib_name, '/')) {
284    LOG("%s: Looking through the search path list\n", __FUNCTION__);
285    const char* path = search_path_list->FindFile(lib_name);
286    if (!path) {
287      error->Format("Can't find library file %s", lib_name);
288      return NULL;
289    }
290    full_path = path;
291  } else {
292    if (lib_name[0] != '/') {
293      // Need to transform this into a full path.
294      full_path = GetCurrentDirectory();
295      if (full_path.size() && full_path[full_path.size() - 1] != '/')
296        full_path += '/';
297      full_path += lib_name;
298    } else {
299      // Absolute path. Easy.
300      full_path = lib_name;
301    }
302    LOG("%s: Full library path: %s\n", __FUNCTION__, full_path.c_str());
303    if (!PathIsFile(full_path.c_str())) {
304      error->Format("Library file doesn't exist: %s", full_path.c_str());
305      return NULL;
306    }
307  }
308
309  // Load the library
310  if (!lib->Load(full_path.c_str(), load_address, file_offset, error))
311    return NULL;
312
313  // Load all dependendent libraries.
314  LOG("%s: Loading dependencies of %s\n", __FUNCTION__, base_name);
315  SharedLibrary::DependencyIterator iter(lib.Get());
316  Vector<LibraryView*> dependencies;
317  while (iter.GetNext()) {
318    Error dep_error;
319    LibraryView* dependency = LoadLibrary(iter.GetName(),
320                                          dlopen_mode,
321                                          0U /* load address */,
322                                          0U /* file offset */,
323                                          search_path_list,
324                                          &dep_error);
325    if (!dependency) {
326      error->Format("When loading %s: %s", base_name, dep_error.c_str());
327      return NULL;
328    }
329    dependencies.PushBack(dependency);
330  }
331  if (CRAZY_DEBUG) {
332    LOG("%s: Dependencies loaded for %s\n", __FUNCTION__, base_name);
333    for (size_t n = 0; n < dependencies.GetCount(); ++n)
334      LOG("  ... %p %s\n", dependencies[n], dependencies[n]->GetName());
335    LOG("    dependencies @%p\n", &dependencies);
336  }
337
338  // Relocate the library.
339  LOG("%s: Relocating %s\n", __FUNCTION__, base_name);
340  if (!lib->Relocate(this, &dependencies, error))
341    return NULL;
342
343  // Notify GDB of load.
344  lib->link_map_.l_addr = lib->load_address();
345  lib->link_map_.l_name = const_cast<char*>(lib->base_name_);
346  lib->link_map_.l_ld = reinterpret_cast<uintptr_t>(lib->view_.dynamic());
347  Globals::GetRDebug()->AddEntry(&lib->link_map_);
348
349  // The library was properly loaded, add it to the list of crazy
350  // libraries. IMPORTANT: Do this _before_ calling the constructors
351  // because these could call dlopen().
352  lib->list_next_ = head_;
353  lib->list_prev_ = NULL;
354  if (head_)
355    head_->list_prev_ = lib.Get();
356  head_ = lib.Get();
357
358  // Then create a new LibraryView for it.
359  wrap = new LibraryView();
360  wrap->SetCrazy(lib.Get(), lib_name);
361  known_libraries_.PushBack(wrap);
362
363  LOG("%s: Running constructors for %s\n", __FUNCTION__, base_name);
364
365  // Now run the constructors.
366  lib->CallConstructors();
367
368  LOG("%s: Done loading %s\n", __FUNCTION__, base_name);
369  lib.Release();
370
371  return wrap;
372}
373
374void LibraryList::AddLibrary(LibraryView* wrap) {
375  known_libraries_.PushBack(wrap);
376}
377
378LibraryView* LibraryList::FindKnownLibrary(const char* name) {
379  const char* base_name = GetBaseNamePtr(name);
380  for (size_t n = 0; n < known_libraries_.GetCount(); ++n) {
381    LibraryView* wrap = known_libraries_[n];
382    if (!strcmp(base_name, wrap->GetName()))
383      return wrap;
384  }
385  return NULL;
386}
387
388}  // namespace crazy
389