Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(617)

Unified Diff: third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp

Issue 322433006: Fork of the Android NDK crazy linker. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add a required license header to a cpp module, missing in the original. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..406346e6696f0d6ed35d4ec783d47429969522f8
--- /dev/null
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_shared_library.cpp
@@ -0,0 +1,451 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crazy_linker_shared_library.h"
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <elf.h>
+#include <sys/exec_elf.h>
+
+#include "crazy_linker_ashmem.h"
+#include "crazy_linker_debug.h"
+#include "crazy_linker_elf_loader.h"
+#include "crazy_linker_elf_relocations.h"
+#include "crazy_linker_library_list.h"
+#include "crazy_linker_library_view.h"
+#include "crazy_linker_globals.h"
+#include "crazy_linker_memory_mapping.h"
+#include "crazy_linker_thread.h"
+#include "crazy_linker_util.h"
+#include "crazy_linker_wrappers.h"
+#include "linker_phdr.h"
+
+#ifndef DF_SYMBOLIC
+#define DF_SYMBOLIC 2
+#endif
+
+#ifndef DF_TEXTREL
+#define DF_TEXTREL 4
+#endif
+
+#ifndef DT_INIT_ARRAY
+#define DT_INIT_ARRAY 25
+#endif
+
+#ifndef DT_INIT_ARRAYSZ
+#define DT_INIT_ARRAYSZ 27
+#endif
+
+#ifndef DT_FINI_ARRAY
+#define DT_FINI_ARRAY 26
+#endif
+
+#ifndef DT_FINI_ARRAYSZ
+#define DT_FINI_ARRAYSZ 28
+#endif
+
+#ifndef DT_FLAGS
+#define DT_FLAGS 30
+#endif
+
+#ifndef DT_PREINIT_ARRAY
+#define DT_PREINIT_ARRAY 32
+#endif
+
+#ifndef DT_PREINIT_ARRAYSZ
+#define DT_PREINIT_ARRAYSZ 33
+#endif
+
+namespace crazy {
+
+namespace {
+
+typedef SharedLibrary::linker_function_t linker_function_t;
+typedef int (*JNI_OnLoadFunctionPtr)(void* vm, void* reserved);
+typedef void (*JNI_OnUnloadFunctionPtr)(void* vm, void* reserved);
+
+// Call a constructor or destructor function pointer. Ignore
+// NULL and -1 values intentionally. They correspond to markers
+// in the tables, or deleted values.
+// |func_type| corresponds to the type of the function, and is only
+// used for debugging (examples are "DT_INIT", "DT_INIT_ARRAY", etc...).
+void CallFunction(linker_function_t func, const char* func_type) {
+ uintptr_t func_address = reinterpret_cast<uintptr_t>(func);
+
+ LOG("%s: %p %s\n", __FUNCTION__, func, func_type);
+ if (func_address != 0 && func_address != uintptr_t(-1))
+ func();
+}
+
+// An instance of ElfRelocator::SymbolResolver that can be used
+// to resolve symbols in a shared library being loaded by
+// LibraryList::LoadLibrary.
+class SharedLibraryResolver : public ElfRelocations::SymbolResolver {
+ public:
+ SharedLibraryResolver(SharedLibrary* lib,
+ LibraryList* lib_list,
+ Vector<LibraryView*>* dependencies)
+ : lib_(lib), dependencies_(dependencies) {}
+
+ virtual void* Lookup(const char* symbol_name) {
+ // TODO(digit): Add the ability to lookup inside the main executable.
+
+ // First, look inside the current library.
+ const ELF::Sym* entry = lib_->LookupSymbolEntry(symbol_name);
+ if (entry)
+ return reinterpret_cast<void*>(lib_->load_bias() + entry->st_value);
+
+ // Special case: redirect the dynamic linker symbols to our wrappers.
+ // This ensures that loaded libraries can call dlopen() / dlsym()
+ // and transparently use the crazy linker to perform their duty.
+ void* address = WrapLinkerSymbol(symbol_name);
+ if (address)
+ return address;
+
+ // Then look inside the dependencies.
+ for (size_t n = 0; n < dependencies_->GetCount(); ++n) {
+ LibraryView* wrap = (*dependencies_)[n];
+ // LOG("%s: Looking into dependency %p (%s)\n", __FUNCTION__, wrap,
+ // wrap->GetName());
+ if (wrap->IsSystem()) {
+ address = ::dlsym(wrap->GetSystem(), symbol_name);
+#ifdef __arm__
+ // Android libm.so defines isnanf as weak. This means that its
+ // address cannot be found by dlsym(), which always returns NULL
+ // for weak symbols. However, libm.so contains the real isnanf
+ // as __isnanf. If we encounter isnanf and fail to resolve it in
+ // libm.so, retry with __isnanf.
+ //
+ // This occurs only in clang, which lacks __builtin_isnanf. The
+ // gcc compiler implements isnanf as a builtin, so the symbol
+ // isnanf never need be resolved in gcc builds.
+ //
+ // http://code.google.com/p/chromium/issues/detail?id=376828
+ if (!address &&
+ !strcmp(symbol_name, "isnanf") &&
+ !strcmp(wrap->GetName(), "libm.so"))
+ address = ::dlsym(wrap->GetSystem(), "__isnanf");
+#endif
+ if (address)
+ return address;
+ }
+ if (wrap->IsCrazy()) {
+ SharedLibrary* dep = wrap->GetCrazy();
+ entry = dep->LookupSymbolEntry(symbol_name);
+ if (entry)
+ return reinterpret_cast<void*>(dep->load_bias() + entry->st_value);
+ }
+ }
+
+ // Nothing found here.
+ return NULL;
+ }
+
+ private:
+ SharedLibrary* lib_;
+ Vector<LibraryView*>* dependencies_;
+};
+
+} // namespace
+
+SharedLibrary::SharedLibrary() { ::memset(this, 0, sizeof(*this)); }
+
+SharedLibrary::~SharedLibrary() {
+ // Ensure the library is unmapped on destruction.
+ if (view_.load_address())
+ munmap(reinterpret_cast<void*>(view_.load_address()), view_.load_size());
+}
+
+bool SharedLibrary::Load(const char* full_path,
+ size_t load_address,
+ size_t file_offset,
+ Error* error) {
+ // First, record the path.
+ LOG("%s: full path '%s'\n", __FUNCTION__, full_path);
+
+ size_t full_path_len = strlen(full_path);
+ if (full_path_len >= sizeof(full_path_)) {
+ error->Format("Path too long: %s", full_path);
+ return false;
+ }
+
+ strlcpy(full_path_, full_path, sizeof(full_path_));
+ base_name_ = GetBaseNamePtr(full_path_);
+
+ // Load the ELF binary in memory.
+ LOG("%s: Loading ELF segments for %s\n", __FUNCTION__, base_name_);
+
+ {
+ ElfLoader loader;
+ if (!loader.LoadAt(full_path_, file_offset, load_address, error)) {
+ return false;
+ }
+
+ if (!view_.InitUnmapped(loader.load_start(),
+ loader.loaded_phdr(),
+ loader.phdr_count(),
+ error)) {
+ return false;
+ }
+
+ if (!symbols_.Init(&view_)) {
+ *error = "Missing or malformed symbol table";
+ return false;
+ }
+ }
+
+ if (phdr_table_get_relro_info(view_.phdr(),
+ view_.phdr_count(),
+ view_.load_bias(),
+ &relro_start_,
+ &relro_size_) < 0) {
+ relro_start_ = 0;
+ relro_size_ = 0;
+ }
+
+#ifdef __arm__
+ LOG("%s: Extracting ARM.exidx table for %s\n", __FUNCTION__, base_name_);
+ (void)phdr_table_get_arm_exidx(
+ phdr(), phdr_count(), load_bias(), &arm_exidx_, &arm_exidx_count_);
+#endif
+
+ LOG("%s: Parsing dynamic table for %s\n", __FUNCTION__, base_name_);
+ ElfView::DynamicIterator dyn(&view_);
+ for (; dyn.HasNext(); dyn.GetNext()) {
+ ELF::Addr dyn_value = dyn.GetValue();
+ uintptr_t dyn_addr = dyn.GetAddress(load_bias());
+ switch (dyn.GetTag()) {
+ case DT_DEBUG:
+ if (view_.dynamic_flags() & PF_W) {
+ *dyn.GetValuePointer() =
+ reinterpret_cast<uintptr_t>(Globals::GetRDebug()->GetAddress());
+ }
+ break;
+ case DT_INIT:
+ LOG(" DT_INIT addr=%p\n", dyn_addr);
+ init_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
+ break;
+ case DT_FINI:
+ LOG(" DT_FINI addr=%p\n", dyn_addr);
+ fini_func_ = reinterpret_cast<linker_function_t>(dyn_addr);
+ break;
+ case DT_INIT_ARRAY:
+ LOG(" DT_INIT_ARRAY addr=%p\n", dyn_addr);
+ init_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+ break;
+ case DT_INIT_ARRAYSZ:
+ init_array_count_ = dyn_value / sizeof(ELF::Addr);
+ LOG(" DT_INIT_ARRAYSZ value=%p count=%p\n",
+ dyn_value,
+ init_array_count_);
+ break;
+ case DT_FINI_ARRAY:
+ LOG(" DT_FINI_ARRAY addr=%p\n", dyn_addr);
+ fini_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+ break;
+ case DT_FINI_ARRAYSZ:
+ fini_array_count_ = dyn_value / sizeof(ELF::Addr);
+ LOG(" DT_FINI_ARRAYSZ value=%p count=%p\n",
+ dyn_value,
+ fini_array_count_);
+ break;
+ case DT_PREINIT_ARRAY:
+ LOG(" DT_PREINIT_ARRAY addr=%p\n", dyn_addr);
+ preinit_array_ = reinterpret_cast<linker_function_t*>(dyn_addr);
+ break;
+ case DT_PREINIT_ARRAYSZ:
+ preinit_array_count_ = dyn_value / sizeof(ELF::Addr);
+ LOG(" DT_PREINIT_ARRAYSZ value=%p count=%p\n",
+ dyn_value,
+ preinit_array_count_);
+ break;
+ case DT_SYMBOLIC:
+ LOG(" DT_SYMBOLIC\n");
+ has_DT_SYMBOLIC_ = true;
+ break;
+ case DT_FLAGS:
+ if (dyn_value & DF_SYMBOLIC)
+ has_DT_SYMBOLIC_ = true;
+ break;
+#if defined(__mips__)
+ case DT_MIPS_RLD_MAP:
+ *dyn.GetValuePointer() =
+ reinterpret_cast<ELF::Addr>(Globals::GetRDebug()->GetAddress());
+ break;
+#endif
+ default:
+ ;
+ }
+ }
+
+ LOG("%s: Load complete for %s\n", __FUNCTION__, base_name_);
+ return true;
+}
+
+bool SharedLibrary::Relocate(LibraryList* lib_list,
+ Vector<LibraryView*>* dependencies,
+ Error* error) {
+ // Apply relocations.
+ LOG("%s: Applying relocations to %s\n", __FUNCTION__, base_name_);
+
+ ElfRelocations relocations;
+
+ if (!relocations.Init(&view_, error))
+ return false;
+
+ SharedLibraryResolver resolver(this, lib_list, dependencies);
+ if (!relocations.ApplyAll(&symbols_, &resolver, error))
+ return false;
+
+ LOG("%s: Relocations applied for %s\n", __FUNCTION__, base_name_);
+ return true;
+}
+
+const ELF::Sym* SharedLibrary::LookupSymbolEntry(const char* symbol_name) {
+ return symbols_.LookupByName(symbol_name);
+}
+
+void* SharedLibrary::FindAddressForSymbol(const char* symbol_name) {
+ return symbols_.LookupAddressByName(symbol_name, view_.load_bias());
+}
+
+bool SharedLibrary::CreateSharedRelro(size_t load_address,
+ size_t* relro_start,
+ size_t* relro_size,
+ int* relro_fd,
+ Error* error) {
+ SharedRelro relro;
+
+ if (!relro.Allocate(relro_size_, base_name_, error))
+ return false;
+
+ if (load_address != 0 && load_address != this->load_address()) {
+ // Need to relocate the content of the ashmem region first to accomodate
+ // for the new load address.
+ if (!relro.CopyFromRelocated(
+ &view_, load_address, relro_start_, relro_size_, error))
+ return false;
+ } else {
+ // Simply copy, no relocations.
+ if (!relro.CopyFrom(relro_start_, relro_size_, error))
+ return false;
+ }
+
+ // Enforce read-only mode for the region's content.
+ if (!relro.ForceReadOnly(error))
+ return false;
+
+ // All good.
+ *relro_start = relro.start();
+ *relro_size = relro.size();
+ *relro_fd = relro.DetachFd();
+ return true;
+}
+
+bool SharedLibrary::UseSharedRelro(size_t relro_start,
+ size_t relro_size,
+ int relro_fd,
+ Error* error) {
+ LOG("%s: relro_start=%p relro_size=%p relro_fd=%d\n",
+ __FUNCTION__,
+ (void*)relro_start,
+ (void*)relro_size,
+ relro_fd);
+
+ if (relro_fd < 0 || relro_size == 0) {
+ // Nothing to do here.
+ return true;
+ }
+
+ // Sanity check: A shared RELRO is not already used.
+ if (relro_used_) {
+ *error = "Library already using shared RELRO section";
+ return false;
+ }
+
+ // Sanity check: RELRO addresses must match.
+ if (relro_start_ != relro_start || relro_size_ != relro_size) {
+ error->Format("RELRO mismatch addr=%p size=%p (wanted addr=%p size=%p)",
+ relro_start_,
+ relro_size_,
+ relro_start,
+ relro_size);
+ return false;
+ }
+
+ // Everything's good, swap pages in this process's address space.
+ SharedRelro relro;
+ if (!relro.InitFrom(relro_start, relro_size, relro_fd, error))
+ return false;
+
+ relro_used_ = true;
+ return true;
+}
+
+void SharedLibrary::CallConstructors() {
+ CallFunction(init_func_, "DT_INIT");
+ for (size_t n = 0; n < init_array_count_; ++n)
+ CallFunction(init_array_[n], "DT_INIT_ARRAY");
+}
+
+void SharedLibrary::CallDestructors() {
+ for (size_t n = fini_array_count_; n > 0; --n) {
+ CallFunction(fini_array_[n - 1], "DT_FINI_ARRAY");
+ }
+ CallFunction(fini_func_, "DT_FINI");
+}
+
+bool SharedLibrary::SetJavaVM(void* java_vm,
+ int minimum_jni_version,
+ Error* error) {
+ if (java_vm == NULL)
+ return true;
+
+ // Lookup for JNI_OnLoad, exit if it doesn't exist.
+ JNI_OnLoadFunctionPtr jni_onload = reinterpret_cast<JNI_OnLoadFunctionPtr>(
+ FindAddressForSymbol("JNI_OnLoad"));
+ if (!jni_onload)
+ return true;
+
+ int jni_version = (*jni_onload)(java_vm, NULL);
+ if (jni_version < minimum_jni_version) {
+ error->Format("JNI_OnLoad() in %s returned %d, expected at least %d",
+ full_path_,
+ jni_version,
+ minimum_jni_version);
+ return false;
+ }
+
+ // Save the JavaVM handle for unload time.
+ java_vm_ = java_vm;
+ return true;
+}
+
+void SharedLibrary::CallJniOnUnload() {
+ if (!java_vm_)
+ return;
+
+ JNI_OnUnloadFunctionPtr jni_on_unload =
+ reinterpret_cast<JNI_OnUnloadFunctionPtr>(
+ this->FindAddressForSymbol("JNI_OnUnload"));
+
+ if (jni_on_unload)
+ (*jni_on_unload)(java_vm_, NULL);
+}
+
+bool SharedLibrary::DependencyIterator::GetNext() {
+ dep_name_ = NULL;
+ for (; iter_.HasNext(); iter_.GetNext()) {
+ if (iter_.GetTag() == DT_NEEDED) {
+ dep_name_ = symbols_->GetStringById(iter_.GetValue());
+ iter_.GetNext();
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace crazy

Powered by Google App Engine
This is Rietveld 408576698