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