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