Add crazy_linker sources.

This patch adds a new NDK support static library, named crazy_linker,
that implements a custom dynamic linker, to work-around limitations
of the system one.

See sources/android/crazy_linker/README.TXT for all details.

+ Add a new device test (crazy_linker) to run all unit and functional
  tests for the linker.

+ Fix a bug in minitest's formatting function and reformat the code
  a bit.

This code has been written for Chrome, but could also benefit other
applications that use multiple-processes and large shared libraries
at the same time. For more details, see:

  https://code.google.com/p/chromium/issues/detail?id=287739

Change-Id: Ibefbbabedeb68d023b3b4c3b8392bd76b7baa1c2
diff --git a/sources/android/crazy_linker/Android.mk b/sources/android/crazy_linker/Android.mk
new file mode 100644
index 0000000..261fdc3
--- /dev/null
+++ b/sources/android/crazy_linker/Android.mk
@@ -0,0 +1,61 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+crazy_linker_sources := \
+  src/crazy_linker_api.cpp \
+  src/crazy_linker_ashmem.cpp \
+  src/crazy_linker_debug.cpp \
+  src/crazy_linker_elf_loader.cpp \
+  src/crazy_linker_elf_relocator.cpp \
+  src/crazy_linker_error.cpp \
+  src/crazy_linker_globals.cpp \
+  src/crazy_linker_library_list.cpp \
+  src/crazy_linker_library_view.cpp \
+  src/crazy_linker_line_reader.cpp \
+  src/crazy_linker_proc_maps.cpp \
+  src/crazy_linker_rdebug.cpp \
+  src/crazy_linker_search_path_list.cpp \
+  src/crazy_linker_shared_library.cpp \
+  src/crazy_linker_thread.cpp \
+  src/crazy_linker_util.cpp \
+  src/crazy_linker_wrappers.cpp \
+  src/crazy_linker_system.cpp \
+  src/linker_phdr.cpp \
+
+# The crazy linker itself.
+include $(CLEAR_VARS)
+LOCAL_MODULE := crazy_linker
+LOCAL_C_INCLUDES = $(LOCAL_PATH)/include $(LOCAL_PATH)/src
+LOCAL_CFLAGS := -Os -fvisibility=hidden -Wall -Werror
+LOCAL_SRC_FILES := $(crazy_linker_sources)
+LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_LDLIBS := -llog
+include $(BUILD_STATIC_LIBRARY)
+
+# The crazy linker unit tests.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := crazylinker_unittest
+LOCAL_SRC_FILES := \
+  $(crazy_linker_sources) \
+  src/crazy_linker_ashmem_unittest.cpp \
+  src/crazy_linker_error_unittest.cpp \
+  src/crazy_linker_line_reader_unittest.cpp \
+  src/crazy_linker_system_mock.cpp \
+  src/crazy_linker_system_unittest.cpp \
+  src/crazy_linker_globals_unittest.cpp \
+  src/crazy_linker_proc_maps_unittest.cpp \
+  src/crazy_linker_search_path_list_unittest.cpp \
+  src/crazy_linker_util_unittest.cpp \
+  src/crazy_linker_thread_unittest.cpp \
+  minitest/minitest.cc \
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/src
+LOCAL_CFLAGS += -DUNIT_TESTS
+LOCAL_LDLIBS := -llog
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/sources/android/crazy_linker/DESIGN.TXT b/sources/android/crazy_linker/DESIGN.TXT
new file mode 100644
index 0000000..ea4482d
--- /dev/null
+++ b/sources/android/crazy_linker/DESIGN.TXT
@@ -0,0 +1,205 @@
+The design of crazy_linker:
+===========================
+
+Introduction:
+-------------
+
+A system linker (e.g. ld.so on Linux, or /system/bin/linker on Android), is a
+particularly sophisticated piece of code because it is used to load and start
+_executables_ on the system. This requires dealing with really low-level
+details like:
+
+  - The way the kernel loads and initializes binaries into a new process.
+
+  - The way it passes initialization data (e.g. command-line arguments) to
+    the process being launched.
+
+  - Setting up the C runtime library, thread-local storage, and others properly
+    before calling main().
+
+  - Be very careful in the way it operates, due to the fact that it will be used
+    to load set-uid programs.
+
+  - Need to support a flurry of exotic flags and environment variables that
+    affect runtime behaviour in "interesting" but mostly unpredictable ways
+    (see the manpages for dlopen, dlsym and ld.so for details).
+
+Add to this that most of this must be done without the C library being loaded or
+initialized yet. No wonder this code is really complex.
+
+By contrast, crazy_linker is a static library whose only purpose is to load
+ELF shared libraries, inside an _existing_ executable process. This makes it
+considerably simpler:
+
+  - The runtime environment (C library, libstdc++) is available and properly
+    initialized.
+
+  - No need to care about kernel interfaces. Everything uses mmap() and simple
+    file accesses.
+
+  - The API is simple, and straightforward (no hidden behaviour changes due to
+    environment variables).
+
+This document explains how the crazy_linker works. A good understanding of the
+ELF file format is recommended, though not necessary.
+
+
+I. ELF Loading Basics:
+----------------------
+
+When it comes to loading shared libraries, an ELF file mainly consists in the
+following parts:
+
+  - A fixed-size header that identifies the file as an ELF file and gives
+    offsets/sizes to other tables.
+
+  - A table (called the "program header table"), containing entries describing
+    'segments' of interest in the ELF file.
+
+  - A table (called the "dynamic table"), containing entries describing
+    properties of the ELF library. The most interesting ones are the list
+    of libraries the current one depends on.
+
+  - A table describing the symbols (function or global names) that the library
+    references or exports.
+
+  - One or more tables containing 'relocations'. Because libraries can be loaded
+    at any page-aligned address in memory, numerical pointers they contain must
+    be adjusted after load. That's what the relocation entries do. They can
+    also reference symbols to be found in other libraries.
+
+The process of loading a given ELF shared library can be decomposed into 4 steps:
+
+  1) Map loadable segments into memory.
+
+    This step parses the program header table to identify 'loadable' segments,
+    reserve the corresponding address space, then map them directly into
+    memory with mmap().
+
+       Related: src/crazy_linker_elf_loader.cpp
+
+
+  2) Load library dependencies.
+
+    This step parses the dynamic table to identify all the other shared
+    libraries the current one depends on, then will _recursively_ load them.
+
+        Related: src/crazy_linker_library_list.cpp
+                 (crazy::LibraryList::LoadLibrary())
+
+  3) Apply all relocations.
+
+     This steps adjusts all pointers within the library for the actual load
+     address. This can also reference symbols that appear in other libraries
+     loaded in step 2).
+
+        Related: src/crazy_linker_elf_relocator.cpp
+
+  4) Run constructors.
+
+     Libraries include a list of functions to be run at load time, typically
+     to perform static C++ initialization.
+
+        Related: src/crazy_linker_shared_library.cpp
+                 (SharedLibrary::RunConstructors())
+
+Unloading a library is similar, but in reverse order:
+
+  1) Run destructors.
+  2) Unload dependencies recursively.
+  3) Unmap loadable segments.
+
+
+II. Managing the list of libraries:
+-----------------------------------
+
+It is crucial to avoid loading the same library twice in the same process,
+otherwise some really bad undefined behaviour may happen.
+
+This implies that, inside an Android application process, all system libraries
+should be loaded by the system linker (because otherwise, the Dalvik-based
+framework might load the same library on demand, at an unpredictable time).
+
+To handle this, the crazy_linker uses a custom class (crazy::LibraryList) where
+each entry (crazy::LibraryView) is reference-counted, and either references:
+
+  - An application shared libraries, loaded by the crazy_linker itself.
+  - A system shared libraries, loaded through the system dlopen().
+
+Libraries loaded by the crazy_linker are modelled by a crazy::SharedLibrary
+object. The source code comments often refer to these objects as
+"crazy libraries", as opposed to "system libraries".
+
+As an example, here's a diagram that shows the list after loading a library
+'libfoo.so' that depends on the system libraries 'libc.so', 'libm.so' and
+'libOpenSLES.so'.
+
+    +-------------+
+    | LibraryList |
+    +-------------+
+           |
+           |    +-------------+
+           +----| LibraryView | ----> libc.so
+           |    +-------------+
+           |
+           |    +-------------+
+           +----| LibraryView | ----> libm.so
+           |    +-------------+
+           |
+           |    +-------------+
+           +----| LibraryView | ----> libOpenSLES.so
+           |    +-------------+
+           |
+           |    +-------------+      +-------------+
+           +----| LibraryView |----->|SharedLibrary| ---> libfoo.so
+           |    +-------------+      +-------------+
+           |
+          ___
+           _
+
+System libraries are identified by name. Only the official NDK-official system
+libraries are listed. It is likely that using crazy_linker to load non-NDK
+system libraries will not work correctly, so don't do it.
+
+
+III. Wrapping of linker symbols within crazy ones:
+--------------------------------------------------
+
+Libraries loaded by the crazy linker are not visible to the system linker.
+
+This means that directly calling the system dlopen() or dlsym() from a library
+code loaded by the crazy_linker will not work properly.
+
+To work-around this, crazy_linker redirects all linker symbols to its own
+wrapper implementation. This redirection happens transparently.
+
+  Related: src/crazy_linker_wrappers.cpp
+
+This also includes a few "hidden" dynamic linker symbols which are used for
+stack-unwinding. This guarantees that C++ exception propagation works.
+
+
+IV. GDB support:
+----------------
+
+The crazy_linker contains support code to ensure that libraries loaded with it
+are visible through GDB at runtime. For more details, see the extensive comments
+in src/crazy_linker_rdebug.h
+
+
+V. Other Implementation details:
+--------------------------------
+
+The crazy_linker is written in C++, but its API is completely C-based.
+
+The implementation doesn't require any C++ STL feature (except for new
+and delete).
+
+Very little of the code is actually Android-specific. The target system's
+bitness is abstracted through a C++ traits class (see src/elf_traits.h).
+
+Written originally for Chrome, so follows the Chromium coding style. Which can
+be enforced by using the 'clang-format' tool with:
+
+  cd /path/to/crazy_linker/
+  find . -name "*.h" -o -name "*.cpp" | xargs clang-format -style Chromium -i
diff --git a/sources/android/crazy_linker/LICENSE b/sources/android/crazy_linker/LICENSE
new file mode 100644
index 0000000..5bc7f5a
--- /dev/null
+++ b/sources/android/crazy_linker/LICENSE
@@ -0,0 +1,55 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/sources/android/crazy_linker/README.TXT b/sources/android/crazy_linker/README.TXT
new file mode 100644
index 0000000..bf05402
--- /dev/null
+++ b/sources/android/crazy_linker/README.TXT
@@ -0,0 +1,137 @@
+Introduction:
+-------------
+
+A custom dynamic linker for Android programs that adds a few interesting
+features compared to /system/bin/linker:
+
+  - Support changing the library search path. The system linker, when used
+    inside Android applications, is limited to the value of LD_LIBRARY_PATH
+    at boot time, that only looks into system directories, not application
+    ones.
+
+    This linker allows the client to add application paths before the
+    default system ones, this has two benefits:
+
+      - Library dependencies are loaded automatically in the right order.
+
+      - Libraries from the application paths are favored over system ones.
+        This avoids conflicts when one of your application's libraries
+        has the same name than a system one (which happens randomly
+        on certain devices due to system application bundling).
+
+    (Note: The system linker issue above has been fixed in Android 4.3).
+
+  - Supports any number of shared libraries. On older Android platforms,
+    the system linker will refuse to load more than 64 or 128 libraries
+    in a single process (Note: Fixed in Android 4.3).
+
+  - Supports loading a library at an explicit (page-aligned) memory
+    address. The system linker always randomizes the address. Note that
+    this is generally a _bad_ idea for security reasons. Consider using
+    this only when using shared RELROs (see below).
+
+  - Supports loading a library from an explicit (page-aligned) file
+    offset. This can be useful to load a library directly from an .apk,
+    provided that it is uncompressed and at a page-aligned offset.
+
+  - Support sharing of RELRO sections. When two processes load the same
+    library at exactly the same address, the content of its RELRO section
+    is identical. By default, each instance uses private RAM pages to host
+    it, but it is possible to use a single ashmem region to share the same
+    data instead.
+
+    WARNING: This feature will not work on certain older kernels. See
+             the documentation for crazy_system_can_share_relro() for
+             more details.
+
+See include/crazy_linker.h for the API and its documentation.
+
+See LICENSE file for full licensing details (hint: BSD)
+
+A few notes:
+
+  - Do not use this if you don't know what you're doing. Read the API
+    documentation first, and look at the test programs for usage examples.
+
+  - The crazy linker will always use the system linker to load NDK-exposed
+    system libraries (e.g. liblog.so and others). This avoids having two
+    instances of the same library in the same process, and correctly
+    resolving any symbols from system libraries.
+
+  - Any library loaded by the crazy linker, and which uses functions of
+    libdl.so will continue to work. However, calls to dlopen(), dlsym(),
+    et al will be redirected to the crazy linker's own wrappers.
+
+    This ensures that if a library is loaded by the crazy linker, any of
+    its dependencies will be loaded by it too.
+
+  - Libraries loaded with the crazy linker are visible to GDB, or Breakpad,
+    and stack unwinding / C++ exception propagation should just work.
+
+
+Caveats:
+--------
+
+  You can't call the crazy_linker code directly from Java in your Android
+  application (it's a static library). You need to put it into your own
+  shared library, loaded with System.loadLibrary() instead (or alternatively,
+  inside your NativeActivity's shared library).
+
+  Also, libraries loaded with the crazy linker are not visible to the system
+  one. In practice, it means that lazy native method lookup will not work. I.e.:
+
+  The first time you call a native method like:
+
+    'mypackage.MyClass.myNativeMethod()'
+
+  The VM will look into existing native libraries with dlsym() for a
+  function symbol named like:
+
+    Java_mypackage_MyClass_myNativeMethod
+
+  This will not work if the symbol is inside a library loaded with the
+  crazy_linker.
+
+  To work-around this, register the native methods explicitely
+  in your JNI_OnLoad() by calling env->RegisterNatives() with the
+  appropriate parameters.
+
+
+Usage instructions:
+-------------------
+
+  1/ Add the following to your module definition in your project's Android.mk:
+
+        LOCAL_STATIC_LIBRARIES := crazy_linker
+
+  2/ Also Include the top-level crazy_linker Android.mk, as in:
+
+        include /path/to/crazy_linker/Android.mk
+
+  3/ In your C or C++ source:
+
+        #include <crazy_linker.h>
+
+    Read the header for API documentation.
+
+  If your library implements native methods, it must explicitely register
+  them with env->RegisterNatives() before they become usable.
+
+BUGS & TODOs:
+-------------
+
+  - Libraries loaded by the crazy linker are not automatically closed when
+    the process exits.
+
+  - dlopen() when called inside a library loaded by the crazy linker doesn't
+    support RTLD_MAIN or RTLD_NEXT.
+
+Testing:
+--------
+
+  If you modify this code, check your changes by running the test suite using:
+
+    cd $NDK
+    tests/run-tests.sh crazy_linker
+
+  See DESIGN.TXT for an overview of the library's design.
diff --git a/sources/android/crazy_linker/include/crazy_linker.h b/sources/android/crazy_linker/include/crazy_linker.h
new file mode 100644
index 0000000..16a61e6
--- /dev/null
+++ b/sources/android/crazy_linker/include/crazy_linker.h
@@ -0,0 +1,268 @@
+// 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.
+
+#ifndef CRAZY_LINKER_H
+#define CRAZY_LINKER_H
+
+// This is the crazy linker, a custom dynamic linker that can be used
+// by NDK applications to load shared libraries (not executables) with
+// a twist.
+//
+// Compared to the dynamic linker, the crazy one has the following
+// features:
+//
+//   - It can use an arbitrary search path.
+//
+//   - It can load a library at a memory fixed address, or from a fixed
+//     file offset (both must be page-aligned).
+//
+//   - It can share the RELRO section between two libraries
+//     loaded at the same address in two distinct processes.
+//
+#include <dlfcn.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Function attribute to indicate that it needs to be exported by
+// the library.
+#define _CRAZY_PUBLIC __attribute__((__visibility__("default")))
+
+// Status values returned by crazy linker functions to indicate
+// success or failure. They were chosen to match boolean values,
+// this allows one to test for failures with:
+//
+//    if (!crazy_linker_function(....)) {
+//       ... an error occured.
+//    }
+//
+// If the function called used a crazy_context_t, it is possible to
+// retrieve the error details with crazy_context_get_error().
+typedef enum {
+  CRAZY_STATUS_FAILURE = 0,
+  CRAZY_STATUS_SUCCESS = 1
+} crazy_status_t;
+
+// Opaque handle to a context object that will hold parameters
+// for the crazy linker's operations. For example, this is where
+// you would set the explicit load address, and other user-provided
+// values before calling functions like crazy_library_open().
+//
+// The context holds a list of library search paths, initialized to
+// the content of the LD_LIBRARY_PATH variable on creation.
+//
+// The context also holds a string buffer to hold error messages that
+// can be queried with crazy_context_get_error().
+typedef struct crazy_context_t crazy_context_t;
+
+// Create a new context object.
+// Note that this calls crazy_context_reset_search_paths().
+crazy_context_t* crazy_context_create(void) _CRAZY_PUBLIC;
+
+// Return current error string, or NULL if there was no error.
+const char* crazy_context_get_error(crazy_context_t* context) _CRAZY_PUBLIC;
+
+// Clear error in a given context.
+void crazy_context_clear_error(crazy_context_t* context) _CRAZY_PUBLIC;
+
+// Set the explicit load address in a context object. Value 0 means
+// the address is randomized.
+void crazy_context_set_load_address(crazy_context_t* context,
+                                    size_t load_address) _CRAZY_PUBLIC;
+
+// Return the current load address in a context.
+size_t crazy_context_get_load_address(crazy_context_t* context) _CRAZY_PUBLIC;
+
+// Set the explicit file offset in a context object. The value should
+// always page-aligned, or the load will fail.
+void crazy_context_set_file_offset(crazy_context_t* context,
+                                   size_t file_offset) _CRAZY_PUBLIC;
+
+// Return the current file offset in a context object.
+size_t crazy_context_get_file_offset(crazy_context_t* context);
+
+// Add one or more paths to the list of library search paths held
+// by a given context. |path| is a string using a column (:) as a
+// list separator. As with the PATH variable, an empty list item
+// is equivalent to '.', the current directory.
+// This can fail if too many paths are added to the context.
+//
+// NOTE: Calling this function appends new paths to the search list,
+// but all paths added with this function will be searched before
+// the ones listed in LD_LIBRARY_PATH.
+crazy_status_t crazy_context_add_search_path(
+    crazy_context_t* context,
+    const char* file_path) _CRAZY_PUBLIC;
+
+// Find the ELF binary that contains |address|, and add its directory
+// path to the context's list of search directories. This is useful to
+// load libraries in the same directory than the current program itself.
+crazy_status_t crazy_context_add_search_path_for_address(
+    crazy_context_t* context,
+    void* address) _CRAZY_PUBLIC;
+
+// Reset the search paths to the value of the LD_LIBRARY_PATH
+// environment variable. This essentially removes any paths
+// that were added with crazy_context_add_search_path() or
+// crazy_context_add_search_path_for_address().
+void crazy_context_reset_search_paths(crazy_context_t* context) _CRAZY_PUBLIC;
+
+// Record the value of |java_vm| inside of |context|. If this is not NULL,
+// which is the default, then after loading any library, the crazy linker
+// will look for a "JNI_OnLoad" symbol within it, and, if it exists, will call
+// it, passing the value of |java_vm| to it. If the function returns with
+// a jni version number that is smaller than |minimum_jni_version|, then
+// the library load will fail with an error.
+//
+// The |java_vm| field is also saved in the crazy_library_t object, and
+// used at unload time to call JNI_OnUnload() if it exists.
+void crazy_context_set_java_vm(crazy_context_t* context,
+                               void* java_vm,
+                               int minimum_jni_version);
+
+// Retrieves the last values set with crazy_context_set_java_vm().
+// A value of NUMM in |*java_vm| means the feature is disabled.
+void crazy_context_get_java_vm(crazy_context_t* context,
+                               void** java_vm,
+                               int* minimum_jni_version);
+
+// Destroy a given context object.
+void crazy_context_destroy(crazy_context_t* context) _CRAZY_PUBLIC;
+
+// Opaque handle to a library as seen/loaded by the crazy linker.
+typedef struct crazy_library_t crazy_library_t;
+
+// Try to open or load a library with the crazy linker.
+// |lib_name| if the library name or path. If it contains a directory
+// separator (/), this is treated as a explicit file path, otherwise
+// it is treated as a base name, and the context's search path list
+// will be used to locate the corresponding file.
+// |context| is a linker context handle. Can be NULL for defaults.
+// On success, return CRAZY_STATUS_SUCCESS and sets |*library|.
+// Libraries are reference-counted, trying to open the same library
+// twice will return the same library handle.
+//
+// NOTE: The load address and file offset from the context only apply
+// to the library being loaded (when not already in the process). If the
+// operations needs to load any dependency libraries, these will use
+// offset and address values of 0 to do so.
+//
+// NOTE: It is possible to open NDK system libraries (e.g. "liblog.so")
+// with this function, but they will be loaded with the system dlopen().
+crazy_status_t crazy_library_open(crazy_library_t** library,
+                                  const char* lib_name,
+                                  crazy_context_t* context) _CRAZY_PUBLIC;
+
+// A structure used to hold information about a given library.
+// |load_address| is the library's actual (page-aligned) load address.
+// |load_size| is the library's actual (page-aligned) size.
+// |relro_start| is the address of the library's RELRO section in memory.
+// |relso_size| is the size of the library's RELRO section (or 0 if none).
+// |relro_fd| is the ashmem file descriptor for the shared section, if one
+// was created with crazy_library_enable_relro_sharing(), -1 otherwise.
+typedef struct {
+  size_t load_address;
+  size_t load_size;
+  size_t relro_start;
+  size_t relro_size;
+  int relro_fd;
+} crazy_library_info_t;
+
+// Retrieve information about a given library.
+// |library| is a library handle.
+// |context| will get an error message on failure.
+// On success, return true and sets |*info|.
+// Note that this function will fail for system libraries.
+crazy_status_t crazy_library_get_info(crazy_library_t* library,
+                                      crazy_context_t* context,
+                                      crazy_library_info_t* info);
+
+// Checks whether the system can support RELRO section sharing. This is
+// mainly due to the fact that old Android kernel images have a bug in their
+// implementation of Ashmem region mapping protection.
+// If this function returns CRAZY_STATUS_FAILURE, then calls to
+// crazy_library_enable_relro_sharing() will return a failure to prevent
+// the exploitation of this security issue in your code.
+crazy_status_t crazy_system_can_share_relro(void);
+
+// Enable RELRO section sharing for this library. This can only be
+// called once per library loaded through crazy_library_open(), and
+// will only work for non-system libraries.
+// On success, return CRAZY_STATUS_SUCCESS and sets |*library_info| with
+// all relevant data. On failure, return CRAZY_STATUS_FAILURE and sets
+// the error message in |context|.
+crazy_status_t crazy_library_enable_relro_sharing(
+    crazy_library_t* library,
+    crazy_context_t* context) _CRAZY_PUBLIC;
+
+// Use the shared RELRO section of the same library loaded in a different
+// address space. On success, return CRAZY_STATUS_SUCCESS and owns |relro_fd|.
+// On failure, return CRAZY_STATUS_FAILURE and sets error message in |context|.
+// |library| is the library handle.
+// |relro_start| is the address of the RELRO section in memory.
+// |relro_size| is the size of the RELRO section.
+// |relro_fd| is the file descriptor for the shared RELRO ashmem region.
+// |context| will receive an error in case of failure.
+// NOTE: This will fail if this is a system library, or if the RELRO
+// parameters do not match the library's actual load address.
+// IMPORTANT NOTE: For security reasons, this _always_ closes the |relro_fd|
+// file descriptor, if it's non-negative, whether the function succeeds or not.
+crazy_status_t crazy_library_use_relro_sharing(
+    crazy_library_t* library,
+    size_t relro_start,
+    size_t relro_size,
+    int relro_fd,
+    crazy_context_t* context) _CRAZY_PUBLIC;
+
+// Look for a library named |library_name| in the set of currently
+// loaded libraries, and return a handle for it in |*library| on success.
+// Note that this increments the reference count on the library, thus
+// the caller shall call crazy_library_close() when it's done with it.
+crazy_status_t crazy_library_find_by_name(const char* library_name,
+                                          crazy_library_t** library);
+
+// Find the library that contains a given |address| in memory.
+// On success, return CRAZY_STATUS_SUCCESS and sets |*library|.
+crazy_status_t crazy_linker_find_library_from_address(
+    void* address,
+    crazy_library_t** library) _CRAZY_PUBLIC;
+
+// Lookup a symbol's address by its |symbol_name| in a given library.
+// This only looks at the symbols in |library|.
+// On success, returns CRAZY_STATUS_SUCCESS and sets |*symbol_address|,
+// which could be NULL for some symbols.
+crazy_status_t crazy_library_find_symbol(crazy_library_t* library,
+                                         const char* symbol_name,
+                                         void** symbol_address) _CRAZY_PUBLIC;
+
+// Lookup a symbol's address in all libraries known by the crazy linker.
+// |symbol_name| is the symbol name. On success, returns CRAZY_STATUS_SUCCESS
+// and sets |*symbol_address|.
+// NOTE: This will _not_ look into system libraries that were not opened
+// with the crazy linker.
+crazy_status_t crazy_linker_find_symbol(const char* symbol_name,
+                                        void** symbol_address) _CRAZY_PUBLIC;
+
+// Find the in-process library that contains a given memory address.
+// Note that this works even if the memory is inside a system library that
+// was not previously opened with crazy_library_open().
+// |address| is the memory address.
+// On success, returns CRAZY_STATUS_SUCCESS and sets |*library|.
+// The caller muyst call crazy_library_close() once it's done with the
+// library.
+crazy_status_t crazy_library_find_from_address(
+    void* address,
+    crazy_library_t** library) _CRAZY_PUBLIC;
+
+// Close a library. This decrements its reference count. If it reaches
+// zero, the library be unloaded from the process.
+void crazy_library_close(crazy_library_t* library) _CRAZY_PUBLIC;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* CRAZY_LINKER_H */
diff --git a/sources/android/crazy_linker/minitest b/sources/android/crazy_linker/minitest
new file mode 120000
index 0000000..1a3ea42
--- /dev/null
+++ b/sources/android/crazy_linker/minitest
@@ -0,0 +1 @@
+../support/tests/minitest
\ No newline at end of file
diff --git a/sources/android/crazy_linker/src/crazy_linker_api.cpp b/sources/android/crazy_linker/src/crazy_linker_api.cpp
new file mode 100644
index 0000000..d5f0b2d
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_api.cpp
@@ -0,0 +1,292 @@
+// 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.
+
+// Implements the crazy linker C-based API exposed by <crazy_linker.h>
+
+#include <crazy_linker.h>
+
+#include <string.h>
+
+#include "crazy_linker_error.h"
+#include "crazy_linker_ashmem.h"
+#include "crazy_linker_globals.h"
+#include "crazy_linker_proc_maps.h"
+#include "crazy_linker_search_path_list.h"
+#include "crazy_linker_shared_library.h"
+#include "crazy_linker_thread.h"
+#include "crazy_linker_util.h"
+#include "crazy_linker_library_view.h"
+#include "crazy_linker_system.h"
+
+using crazy::Globals;
+using crazy::Error;
+using crazy::SearchPathList;
+using crazy::ScopedGlobalLock;
+using crazy::LibraryView;
+
+//
+// crazy_context_t
+//
+
+struct crazy_context_t {
+ public:
+  crazy_context_t()
+      : load_address(0),
+        file_offset(0),
+        error(),
+        search_paths(),
+        java_vm(NULL),
+        minimum_jni_version(0) {
+    ResetSearchPaths();
+  }
+
+  void ResetSearchPaths();
+
+  size_t load_address;
+  size_t file_offset;
+  Error error;
+  SearchPathList search_paths;
+  void* java_vm;
+  int minimum_jni_version;
+};
+
+void crazy_context_t::ResetSearchPaths() {
+  search_paths.ResetFromEnv("LD_LIBRARY_PATH");
+}
+
+//
+// API functions
+//
+
+extern "C" {
+
+crazy_context_t* crazy_context_create(void) { return new crazy_context_t(); }
+
+const char* crazy_context_get_error(crazy_context_t* context) {
+  const char* error = context->error.c_str();
+  return (error[0] != '\0') ? error : NULL;
+}
+
+// Clear error in a given context.
+void crazy_context_clear_error(crazy_context_t* context) {
+  context->error = "";
+}
+
+void crazy_context_set_load_address(crazy_context_t* context,
+                                    size_t load_address) {
+  context->load_address = load_address;
+}
+
+size_t crazy_context_get_load_address(crazy_context_t* context) {
+  return context->load_address;
+}
+
+void crazy_context_set_file_offset(crazy_context_t* context,
+                                   size_t file_offset) {
+  context->file_offset = file_offset;
+}
+
+size_t crazy_context_get_file_offset(crazy_context_t* context) {
+  return context->file_offset;
+}
+
+crazy_status_t crazy_context_add_search_path(crazy_context_t* context,
+                                             const char* file_path) {
+  context->search_paths.AddPaths(file_path);
+  return CRAZY_STATUS_SUCCESS;
+}
+
+crazy_status_t crazy_context_add_search_path_for_address(
+    crazy_context_t* context,
+    void* address) {
+  uintptr_t load_address;
+  char path[512];
+  char* p;
+
+  if (crazy::FindElfBinaryForAddress(
+          address, &load_address, path, sizeof(path)) &&
+      (p = strrchr(path, '/')) != NULL && p[1]) {
+    *p = '\0';
+    return crazy_context_add_search_path(context, path);
+  }
+
+  context->error.Format("Could not find ELF binary at address @%p", address);
+  return CRAZY_STATUS_FAILURE;
+}
+
+void crazy_context_reset_search_paths(crazy_context_t* context) {
+  context->ResetSearchPaths();
+}
+
+void crazy_context_set_java_vm(crazy_context_t* context,
+                               void* java_vm,
+                               int minimum_jni_version) {
+  context->java_vm = java_vm;
+  context->minimum_jni_version = minimum_jni_version;
+}
+
+void crazy_context_get_java_vm(crazy_context_t* context,
+                               void** java_vm,
+                               int* minimum_jni_version) {
+  *java_vm = context->java_vm;
+  *minimum_jni_version = context->minimum_jni_version;
+}
+
+void crazy_context_destroy(crazy_context_t* context) { delete context; }
+
+crazy_status_t crazy_library_open(crazy_library_t** library,
+                                  const char* lib_name,
+                                  crazy_context_t* context) {
+  ScopedGlobalLock lock;
+  LibraryView* wrap =
+      crazy::Globals::GetLibraries()->LoadLibrary(lib_name,
+                                                  RTLD_NOW,
+                                                  context->load_address,
+                                                  context->file_offset,
+                                                  &context->search_paths,
+                                                  &context->error);
+  if (!wrap)
+    return CRAZY_STATUS_FAILURE;
+
+  if (context->java_vm != NULL && wrap->IsCrazy()) {
+    crazy::SharedLibrary* lib = wrap->GetCrazy();
+    if (!lib->SetJavaVM(
+             context->java_vm, context->minimum_jni_version, &context->error)) {
+      crazy::Globals::GetLibraries()->UnloadLibrary(wrap);
+      return CRAZY_STATUS_FAILURE;
+    }
+  }
+
+  *library = reinterpret_cast<crazy_library_t*>(wrap);
+  return CRAZY_STATUS_SUCCESS;
+}
+
+crazy_status_t crazy_library_get_info(crazy_library_t* library,
+                                      crazy_context_t* context,
+                                      crazy_library_info_t* info) {
+  if (!library) {
+    context->error = "Invalid library file handle";
+    return CRAZY_STATUS_FAILURE;
+  }
+
+  LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
+  if (!wrap->GetInfo(&info->load_address,
+                     &info->load_size,
+                     &info->relro_start,
+                     &info->relro_size,
+                     &info->relro_fd,
+                     &context->error)) {
+    return CRAZY_STATUS_FAILURE;
+  }
+
+  return CRAZY_STATUS_SUCCESS;
+}
+
+crazy_status_t crazy_system_can_share_relro(void) {
+  crazy::AshmemRegion region;
+  if (!region.Allocate(PAGE_SIZE, NULL) ||
+      !region.SetProtectionFlags(PROT_READ) ||
+      !crazy::AshmemRegion::CheckFileDescriptorIsReadOnly(region.Get()))
+    return CRAZY_STATUS_FAILURE;
+
+  return CRAZY_STATUS_SUCCESS;
+}
+
+crazy_status_t crazy_library_enable_relro_sharing(crazy_library_t* library,
+                                                  crazy_context_t* context) {
+  LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
+
+  if (!library) {
+    context->error = "Invalid library file handle";
+    return CRAZY_STATUS_FAILURE;
+  }
+
+  if (!wrap->EnableSharedRelro(&context->error))
+    return CRAZY_STATUS_FAILURE;
+
+  return CRAZY_STATUS_SUCCESS;
+}
+
+crazy_status_t crazy_library_use_relro_sharing(crazy_library_t* library,
+                                               size_t relro_start,
+                                               size_t relro_size,
+                                               int relro_fd,
+                                               crazy_context_t* context) {
+  LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
+
+  if (!library) {
+    context->error = "Invalid library file handle";
+    return CRAZY_STATUS_FAILURE;
+  }
+
+  bool success =
+      wrap->UseSharedRelro(relro_start, relro_size, relro_fd, &context->error);
+
+  // Always close the file descriptor.
+  if (relro_fd >= 0)
+    TEMP_FAILURE_RETRY(::close(relro_fd));
+
+  if (!success)
+    return CRAZY_STATUS_FAILURE;
+
+  return CRAZY_STATUS_SUCCESS;
+}
+
+crazy_status_t crazy_library_find_by_name(const char* library_name,
+                                          crazy_library_t** library) {
+  {
+    ScopedGlobalLock lock;
+    LibraryView* wrap =
+        Globals::GetLibraries()->FindLibraryByName(library_name);
+    if (!wrap)
+      return CRAZY_STATUS_FAILURE;
+
+    wrap->AddRef();
+    *library = reinterpret_cast<crazy_library_t*>(wrap);
+  }
+  return CRAZY_STATUS_SUCCESS;
+}
+
+crazy_status_t crazy_library_find_symbol(crazy_library_t* library,
+                                         const char* symbol_name,
+                                         void** symbol_address) {
+  LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
+
+  // TODO(digit): Handle NULL symbols properly.
+  *symbol_address = wrap->LookupSymbol(symbol_name);
+  return (*symbol_address == NULL) ? CRAZY_STATUS_FAILURE
+                                   : CRAZY_STATUS_SUCCESS;
+}
+
+crazy_status_t crazy_linker_find_symbol(const char* symbol_name,
+                                        void** symbol_address) {
+  // TODO(digit): Implement this.
+  return CRAZY_STATUS_FAILURE;
+}
+
+crazy_status_t crazy_library_find_from_address(void* address,
+                                               crazy_library_t** library) {
+  {
+    ScopedGlobalLock lock;
+    LibraryView* wrap = Globals::GetLibraries()->FindLibraryForAddress(address);
+    if (!wrap)
+      return CRAZY_STATUS_FAILURE;
+
+    wrap->AddRef();
+
+    *library = reinterpret_cast<crazy_library_t*>(wrap);
+    return CRAZY_STATUS_SUCCESS;
+  }
+}
+
+void crazy_library_close(crazy_library_t* library) {
+  if (library) {
+    ScopedGlobalLock lock;
+    LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
+
+    Globals::GetLibraries()->UnloadLibrary(wrap);
+  }
+}
+
+}  // extern "C"
diff --git a/sources/android/crazy_linker/src/crazy_linker_ashmem.cpp b/sources/android/crazy_linker/src/crazy_linker_ashmem.cpp
new file mode 100644
index 0000000..4bcb9e3
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_ashmem.cpp
@@ -0,0 +1,89 @@
+// 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_ashmem.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <linux/ashmem.h>
+
+#include "crazy_linker_system.h"
+#include "crazy_linker_memory_mapping.h"
+
+namespace crazy {
+
+bool AshmemRegion::Allocate(size_t region_size, const char* region_name) {
+  int fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR));
+  if (fd < 0)
+    return false;
+
+  if (ioctl(fd, ASHMEM_SET_SIZE, region_size) < 0)
+    goto ERROR;
+
+  if (region_name) {
+    char buf[256];
+    strlcpy(buf, region_name, sizeof(buf));
+    if (ioctl(fd, ASHMEM_SET_NAME, buf) < 0)
+      goto ERROR;
+  }
+
+  Reset(fd);
+  return true;
+
+ERROR:
+  ::close(fd);
+  return false;
+}
+
+bool AshmemRegion::SetProtectionFlags(int prot) {
+  return ioctl(fd_, ASHMEM_SET_PROT_MASK, prot) == 0;
+}
+
+// static
+bool AshmemRegion::CheckFileDescriptorIsReadOnly(int fd) {
+  const size_t map_size = PAGE_SIZE;
+  ScopedMemoryMapping map;
+
+  // First, check that trying to map a page of the region with PROT_WRITE
+  // fails with EPERM.
+  if (map.Allocate(NULL, map_size, MemoryMapping::CAN_WRITE, fd)) {
+    LOG("%s: Region could be mapped writable. Should not happen.\n",
+        __FUNCTION__);
+    errno = EPERM;
+    return false;
+  }
+  if (errno != EPERM) {
+    LOG_ERRNO("%s: Region failed writable mapping with unexpected error",
+              __FUNCTION__);
+    return false;
+  }
+
+  // Second, check that it can be mapped PROT_READ, but cannot be remapped
+  // with PROT_READ | PROT_WRITE through mprotect().
+  if (!map.Allocate(NULL, map_size, MemoryMapping::CAN_READ, fd)) {
+    LOG_ERRNO("%s: Failed to map region read-only", __FUNCTION__);
+    return false;
+  }
+  if (map.SetProtection(MemoryMapping::CAN_READ_WRITE)) {
+    LOG_ERRNO("%s: Region could be remapped read-write. Should not happen.\n",
+              __FUNCTION__);
+    return false;
+  }
+  if (errno != EACCES) {
+    LOG_ERRNO(
+        "%s: Region failed to be remapped read-write with unexpected error",
+        __FUNCTION__);
+    return false;
+  }
+
+  // Everything's good.
+  return true;
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_ashmem.h b/sources/android/crazy_linker/src/crazy_linker_ashmem.h
new file mode 100644
index 0000000..4233ba2
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_ashmem.h
@@ -0,0 +1,64 @@
+// 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.
+
+#ifndef CRAZY_LINKER_ASHMEM_H
+#define CRAZY_LINKER_ASHMEM_H
+
+#include <unistd.h>
+
+namespace crazy {
+
+// Helper class to hold a scoped ashmem region file descriptor.
+class AshmemRegion {
+ public:
+  AshmemRegion() : fd_(-1) {}
+
+  ~AshmemRegion() { Reset(-1); }
+
+  int Get() const { return fd_; }
+
+  int Release() {
+    int ret = fd_;
+    fd_ = -1;
+    return ret;
+  }
+
+  void Reset(int fd) {
+    if (fd_ != -1) {
+      ::close(fd_);
+    }
+    fd_ = fd;
+  }
+
+  // Try to allocate a new ashmem region of |region_size|
+  // (page-aligned) bytes. |region_name| is optional, if not NULL
+  // it will be the name of the region (only used for debugging).
+  // Returns true on success, false otherwise.
+  bool Allocate(size_t region_size, const char* region_name);
+
+  // Change the protection flags of the region. Returns true on success.
+  // On failure, check errno for an error code.
+  bool SetProtectionFlags(int prot_flags);
+
+  // Check that the region tied to file descriptor |fd| is properly read-only:
+  // I.e. that it cannot be mapped writable, or that a read-only mapping cannot
+  // be mprotect()-ed into MAP_WRITE. On failure, return false and sets errno.
+  //
+  // See:
+  //   http://www.cvedetails.com/cve/CVE-2011-1149/
+  // And kernel patch at:
+  //   https://android.googlesource.com/kernel/common.git/+/
+  //     56f76fc68492af718fff88927bc296635d634b78%5E%21/
+  static bool CheckFileDescriptorIsReadOnly(int fd);
+
+ private:
+  AshmemRegion(const AshmemRegion& other);
+  AshmemRegion& operator=(const AshmemRegion& other);
+
+  int fd_;
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_ASHMEM_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_ashmem_unittest.cpp b/sources/android/crazy_linker/src/crazy_linker_ashmem_unittest.cpp
new file mode 100644
index 0000000..59b6a31
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_ashmem_unittest.cpp
@@ -0,0 +1,38 @@
+// 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_ashmem.h"
+
+#include <sys/mman.h>
+
+#include <minitest/minitest.h>
+
+namespace crazy {
+
+TEST(AshmemRegion, Construction) {
+  AshmemRegion region;
+  EXPECT_EQ(-1, region.Get());
+}
+
+TEST(AshmemRegion, Allocate) {
+  AshmemRegion region;
+  const size_t kSize = 4096 * 10;
+  EXPECT_TRUE(region.Allocate(kSize, __FUNCTION__));
+  void* map = ::mmap(NULL,
+                     kSize,
+                     PROT_READ | PROT_WRITE,
+                     MAP_ANONYMOUS | MAP_SHARED,
+                     region.Get(),
+                     0);
+  EXPECT_NE(MAP_FAILED, map);
+
+  for (size_t n = 0; n < kSize; ++n) {
+    TEST_TEXT << "Checking region[" << n << "]";
+    EXPECT_EQ(0, ((char*)map)[n]);
+  }
+
+  EXPECT_EQ(0, ::munmap(map, kSize));
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_debug.cpp b/sources/android/crazy_linker/src/crazy_linker_debug.cpp
new file mode 100644
index 0000000..eb57157
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_debug.cpp
@@ -0,0 +1,69 @@
+// 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_debug.h"
+
+#include <errno.h>
+#include <string.h>
+
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+
+namespace crazy {
+
+#if CRAZY_DEBUG
+
+namespace {
+
+void LogArgs(const char* fmt, va_list args, bool print_error, int error) {
+  const size_t buffer_size = 4096;
+  char* buffer = reinterpret_cast<char*>(::malloc(buffer_size));
+  int ret;
+
+  ret = vsnprintf(buffer, buffer_size, fmt, args);
+  if (ret >= static_cast<int>(buffer_size))
+    ret = static_cast<int>(buffer_size) - 1;
+
+  if (print_error) {
+    strlcat(buffer, ": ", buffer_size);
+    strlcat(buffer, strerror(error), buffer_size);
+  }
+
+  // First, send to stderr.
+  fprintf(stderr, "%.*s", ret, buffer);
+
+#ifdef __ANDROID__
+  // Then to the Android log.
+  __android_log_write(ANDROID_LOG_INFO, "crazy_linker", buffer);
+#endif
+
+  ::free(buffer);
+}
+
+}  // namespace
+
+void Log(const char* fmt, ...) {
+  int old_errno = errno;
+  va_list args;
+  va_start(args, fmt);
+  LogArgs(fmt, args, false, -1);
+  va_end(args);
+  errno = old_errno;
+}
+
+void LogErrno(const char* fmt, ...) {
+  int old_errno = errno;
+  va_list args;
+  va_start(args, fmt);
+  LogArgs(fmt, args, true, old_errno);
+  va_end(args);
+  errno = old_errno;
+}
+
+#endif  // CRAZY_DEBUG
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_debug.h b/sources/android/crazy_linker/src/crazy_linker_debug.h
new file mode 100644
index 0000000..78817ee
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_debug.h
@@ -0,0 +1,47 @@
+// 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.
+
+#ifndef CRAZY_LINKER_DEBUG_H
+#define CRAZY_LINKER_DEBUG_H
+
+// Set CRAZY_DEBUG on the command-line to 1 to enable debugging support.
+// This really means adding traces that will be sent to both stderr
+// and the logcat during execution.
+#ifndef CRAZY_DEBUG
+#define CRAZY_DEBUG 0
+#endif
+
+namespace crazy {
+
+#if CRAZY_DEBUG
+
+void Log(const char* fmt, ...);
+void LogErrno(const char* fmt, ...);
+
+#define LOG(...) ::crazy::Log(__VA_ARGS__)
+#define LOG_ERRNO(...) ::crazy::LogErrno(__VA_ARGS__)
+
+#else
+
+#define LOG(...) ((void)0)
+#define LOG_ERRNO(...) ((void)0)
+
+#endif
+
+// Conditional logging.
+#define LOG_IF(cond, ...) \
+  do {                    \
+    if ((cond))           \
+      LOG(__VA_ARGS__);   \
+  } while (0)
+
+#define LOG_ERRNO_IF(cond, ...) \
+  do {                          \
+    if ((cond))                 \
+      LOG_ERRNO(__VA_ARGS__);   \
+  } while (0)
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_DEBUG_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_loader.cpp b/sources/android/crazy_linker/src/crazy_linker_elf_loader.cpp
new file mode 100644
index 0000000..21cac7c
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_loader.cpp
@@ -0,0 +1,339 @@
+// 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_elf_loader.h"
+
+#include <limits.h>  // For PAGE_SIZE and PAGE_MASK
+
+#include "crazy_linker_debug.h"
+#include "linker_phdr.h"
+
+#define PAGE_START(x) ((x) & PAGE_MASK)
+#define PAGE_OFFSET(x) ((x) & ~PAGE_MASK)
+#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
+
+namespace crazy {
+
+#define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0)
+#define PFLAGS_TO_PROT(x)                 \
+  (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \
+   MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \
+   MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE))
+
+ElfLoader::ElfLoader()
+    : fd_(),
+      path_(NULL),
+      phdr_num_(0),
+      phdr_mmap_(NULL),
+      phdr_table_(NULL),
+      phdr_size_(0),
+      file_offset_(0),
+      wanted_load_address_(0),
+      load_start_(NULL),
+      load_size_(0),
+      load_bias_(0),
+      loaded_phdr_(NULL) {}
+
+ElfLoader::~ElfLoader() {
+  if (phdr_mmap_) {
+    // Deallocate the temporary program header copy.
+    munmap(phdr_mmap_, phdr_size_);
+  }
+}
+
+bool ElfLoader::LoadAt(const char* lib_path,
+                       off_t file_offset,
+                       uintptr_t wanted_address,
+                       Error* error) {
+
+  LOG("%s: lib_path='%s', file_offset=%p, load_address=%p\n",
+      __FUNCTION__,
+      lib_path,
+      file_offset,
+      wanted_address);
+
+  // Check that the load address is properly page-aligned.
+  if (wanted_address != PAGE_START(wanted_address)) {
+    error->Format("Load address is not page aligned (%08x)", wanted_address);
+    return false;
+  }
+  wanted_load_address_ = reinterpret_cast<void*>(wanted_address);
+
+  // Check that the file offset is also properly page-aligned.
+  // PAGE_START() can't be used here due to the compiler complaining about
+  // comparing signed (off_t) and unsigned (size_t) values.
+  if ((file_offset & static_cast<off_t>(PAGE_SIZE - 1)) != 0) {
+    error->Format("File offset is not page aligned (%08x)", file_offset);
+    return false;
+  }
+  file_offset_ = file_offset;
+
+  // Open the file.
+  if (!fd_.OpenReadOnly(lib_path)) {
+    error->Format("Can't open file: %s", strerror(errno));
+    return false;
+  }
+
+  if (file_offset && fd_.SeekTo(file_offset) < 0) {
+    error->Format(
+        "Can't seek to file offset %08x: %s", file_offset, strerror(errno));
+    return false;
+  }
+
+  path_ = lib_path;
+
+  if (!ReadElfHeader(error) || !ReadProgramHeader(error) ||
+      !ReserveAddressSpace(error)) {
+    return false;
+  }
+
+  if (!LoadSegments(error) || !FindPhdr(error)) {
+    // An error occured, cleanup the address space by un-mapping the
+    // range that was reserved by ReserveAddressSpace().
+    if (load_start_ && load_size_)
+      munmap(load_start_, load_size_);
+
+    return false;
+  }
+
+  return true;
+}
+
+bool ElfLoader::ReadElfHeader(Error* error) {
+  int ret = fd_.Read(&header_, sizeof(header_));
+  if (ret < 0) {
+    error->Format("Can't read file: %s", strerror(errno));
+    return false;
+  }
+  if (ret != static_cast<int>(sizeof(header_))) {
+    error->Set("File too small to be ELF");
+    return false;
+  }
+
+  if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) {
+    error->Set("Bad ELF magic");
+    return false;
+  }
+
+  if (header_.e_ident[EI_CLASS] != ELFCLASS32) {
+    error->Format("Not a 32-bit class: %d", header_.e_ident[EI_CLASS]);
+    return false;
+  }
+  if (header_.e_ident[EI_DATA] != ELFDATA2LSB) {
+    error->Format("Not little-endian class: %d", header_.e_ident[EI_DATA]);
+    return false;
+  }
+
+  if (header_.e_type != ET_DYN) {
+    error->Format("Not a shared library type: %d", header_.e_type);
+    return false;
+  }
+
+  if (header_.e_version != EV_CURRENT) {
+    error->Format("Unexpected ELF version: %d", header_.e_version);
+    return false;
+  }
+
+  if (header_.e_machine != ELF_MACHINE) {
+    error->Format("Unexpected ELF machine type: %d", header_.e_machine);
+    return false;
+  }
+
+  return true;
+}
+
+// Loads the program header table from an ELF file into a read-only private
+// anonymous mmap-ed block.
+bool ElfLoader::ReadProgramHeader(Error* error) {
+  phdr_num_ = header_.e_phnum;
+
+  // Like the kernel, only accept program header tables smaller than 64 KB.
+  if (phdr_num_ < 1 || phdr_num_ > 65536 / sizeof(ELF::Phdr)) {
+    error->Format("Invalid program header count: %d", phdr_num_);
+    return false;
+  }
+
+  ELF::Addr page_min = PAGE_START(header_.e_phoff);
+  ELF::Addr page_max =
+      PAGE_END(header_.e_phoff + (phdr_num_ * sizeof(ELF::Phdr)));
+  ELF::Addr page_offset = PAGE_OFFSET(header_.e_phoff);
+
+  phdr_size_ = page_max - page_min;
+
+  void* mmap_result = fd_.Map(
+      NULL, phdr_size_, PROT_READ, MAP_PRIVATE, page_min + file_offset_);
+  if (mmap_result == MAP_FAILED) {
+    error->Format("Phdr mmap failed: %s", strerror(errno));
+    return false;
+  }
+
+  phdr_mmap_ = mmap_result;
+  phdr_table_ = reinterpret_cast<ELF::Phdr*>(
+      reinterpret_cast<char*>(mmap_result) + page_offset);
+  return true;
+}
+
+// Reserve a virtual address range big enough to hold all loadable
+// segments of a program header table. This is done by creating a
+// private anonymous mmap() with PROT_NONE.
+//
+// This will use the wanted_load_address_ value,
+bool ElfLoader::ReserveAddressSpace(Error* error) {
+  ELF::Addr min_vaddr;
+  load_size_ =
+      phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr, NULL);
+  if (load_size_ == 0) {
+    error->Set("No loadable segments");
+    return false;
+  }
+
+  uint8_t* addr = reinterpret_cast<uint8_t*>(min_vaddr);
+  int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
+
+  // Support loading at a fixed address.
+  if (wanted_load_address_) {
+    addr = static_cast<uint8_t*>(wanted_load_address_);
+    mmap_flags |= MAP_FIXED;
+  }
+
+  LOG("%s: address=%p size=%p\n", __FUNCTION__, addr, load_size_);
+  void* start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0);
+  if (start == MAP_FAILED) {
+    error->Format("Could not reserve %d bytes of address space", load_size_);
+    return false;
+  }
+
+  load_start_ = start;
+  load_bias_ = reinterpret_cast<ELF::Addr>(start) - min_vaddr;
+  return true;
+}
+
+// Returns the address of the program header table as it appears in the loaded
+// segments in memory. This is in contrast with 'phdr_table_' which
+// is temporary and will be released before the library is relocated.
+bool ElfLoader::FindPhdr(Error* error) {
+  const ELF::Phdr* phdr_limit = phdr_table_ + phdr_num_;
+
+  // If there is a PT_PHDR, use it directly.
+  for (const ELF::Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
+    if (phdr->p_type == PT_PHDR) {
+      return CheckPhdr(load_bias_ + phdr->p_vaddr, error);
+    }
+  }
+
+  // Otherwise, check the first loadable segment. If its file offset
+  // is 0, it starts with the ELF header, and we can trivially find the
+  // loaded program header from it.
+  for (const ELF::Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
+    if (phdr->p_type == PT_LOAD) {
+      if (phdr->p_offset == 0) {
+        ELF::Addr elf_addr = load_bias_ + phdr->p_vaddr;
+        const ELF::Ehdr* ehdr = (const ELF::Ehdr*)(void*)elf_addr;
+        ELF::Addr offset = ehdr->e_phoff;
+        return CheckPhdr((ELF::Addr)ehdr + offset, error);
+      }
+      break;
+    }
+  }
+
+  error->Set("Can't find loaded program header");
+  return false;
+}
+
+// Ensures that our program header is actually within a loadable
+// segment. This should help catch badly-formed ELF files that
+// would cause the linker to crash later when trying to access it.
+bool ElfLoader::CheckPhdr(ELF::Addr loaded, Error* error) {
+  const ELF::Phdr* phdr_limit = phdr_table_ + phdr_num_;
+  ELF::Addr loaded_end = loaded + (phdr_num_ * sizeof(ELF::Phdr));
+  for (ELF::Phdr* phdr = phdr_table_; phdr < phdr_limit; ++phdr) {
+    if (phdr->p_type != PT_LOAD) {
+      continue;
+    }
+    ELF::Addr seg_start = phdr->p_vaddr + load_bias_;
+    ELF::Addr seg_end = phdr->p_filesz + seg_start;
+    if (seg_start <= loaded && loaded_end <= seg_end) {
+      loaded_phdr_ = reinterpret_cast<const ELF::Phdr*>(loaded);
+      return true;
+    }
+  }
+  error->Format("Loaded program header %x not in loadable segment", loaded);
+  return false;
+}
+
+// Map all loadable segments in process' address space.
+// This assumes you already called phdr_table_reserve_memory to
+// reserve the address space range for the library.
+bool ElfLoader::LoadSegments(Error* error) {
+  for (size_t i = 0; i < phdr_num_; ++i) {
+    const ELF::Phdr* phdr = &phdr_table_[i];
+
+    if (phdr->p_type != PT_LOAD) {
+      continue;
+    }
+
+    // Segment addresses in memory.
+    ELF::Addr seg_start = phdr->p_vaddr + load_bias_;
+    ELF::Addr seg_end = seg_start + phdr->p_memsz;
+
+    ELF::Addr seg_page_start = PAGE_START(seg_start);
+    ELF::Addr seg_page_end = PAGE_END(seg_end);
+
+    ELF::Addr seg_file_end = seg_start + phdr->p_filesz;
+
+    // File offsets.
+    ELF::Addr file_start = phdr->p_offset;
+    ELF::Addr file_end = file_start + phdr->p_filesz;
+
+    ELF::Addr file_page_start = PAGE_START(file_start);
+    ELF::Addr file_length = file_end - file_page_start;
+
+    LOG("%s: file_offset=%p file_length=%p start_address=%p end_address=%p\n",
+        __FUNCTION__,
+        file_offset_ + file_page_start,
+        file_length,
+        seg_page_start,
+        seg_page_start + PAGE_END(file_length));
+
+    if (file_length != 0) {
+      void* seg_addr = fd_.Map((void*)seg_page_start,
+                               file_length,
+                               PFLAGS_TO_PROT(phdr->p_flags),
+                               MAP_FIXED | MAP_PRIVATE,
+                               file_page_start + file_offset_);
+      if (seg_addr == MAP_FAILED) {
+        error->Format("Could not map segment %d: %s", i, strerror(errno));
+        return false;
+      }
+    }
+
+    // if the segment is writable, and does not end on a page boundary,
+    // zero-fill it until the page limit.
+    if ((phdr->p_flags & PF_W) != 0 && PAGE_OFFSET(seg_file_end) > 0) {
+      memset((void*)seg_file_end, 0, PAGE_SIZE - PAGE_OFFSET(seg_file_end));
+    }
+
+    seg_file_end = PAGE_END(seg_file_end);
+
+    // seg_file_end is now the first page address after the file
+    // content. If seg_end is larger, we need to zero anything
+    // between them. This is done by using a private anonymous
+    // map for all extra pages.
+    if (seg_page_end > seg_file_end) {
+      void* zeromap = mmap((void*)seg_file_end,
+                           seg_page_end - seg_file_end,
+                           PFLAGS_TO_PROT(phdr->p_flags),
+                           MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE,
+                           -1,
+                           0);
+      if (zeromap == MAP_FAILED) {
+        error->Format("Could not zero-fill gap: %s", strerror(errno));
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_loader.h b/sources/android/crazy_linker/src/crazy_linker_elf_loader.h
new file mode 100644
index 0000000..efc6dd2
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_loader.h
@@ -0,0 +1,87 @@
+// 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.
+
+#ifndef CRAZY_LINKER_ELF_LOADER_H
+#define CRAZY_LINKER_ELF_LOADER_H
+
+#include "crazy_linker_error.h"
+#include "crazy_linker_system.h"  // For ScopedFileDescriptor
+#include "elf_traits.h"
+
+namespace crazy {
+
+// Helper class used to load an ELF binary in memory.
+//
+// Note that this doesn't not perform any relocation, the purpose
+// of this class is strictly to map all loadable segments from the
+// file to their correct location.
+//
+class ElfLoader {
+ public:
+  ElfLoader();
+  ~ElfLoader();
+
+  // Try to load a library at a given address. On failure, this will
+  // update the linker error message and returns false.
+  //
+  // |lib_path| is the full library path, and |wanted_address| should
+  // be the desired load address, or 0 to enable randomization.
+  //
+  // |file_offset| is an offset in the file where the ELF header will
+  // be looked for.
+  //
+  // |wanted_address| is the wanted load address (of the first loadable
+  // segment), or 0 to enable randomization.
+  //
+  // On success, the library's loadable segments will be mapped in
+  // memory with their original protection. However, no further processing
+  // will be performed.
+  //
+  // On failure, returns false and assign an error message to |error|.
+  bool LoadAt(const char* lib_path,
+              off_t file_offset,
+              uintptr_t wanted_address,
+              Error* error);
+
+  // Only call the following functions after a succesfull LoadAt() call.
+
+  size_t phdr_count() { return phdr_num_; }
+  ELF::Addr load_start() { return reinterpret_cast<ELF::Addr>(load_start_); }
+  ELF::Addr load_size() { return load_size_; }
+  ELF::Addr load_bias() { return load_bias_; }
+  const ELF::Phdr* loaded_phdr() { return loaded_phdr_; }
+
+ private:
+  FileDescriptor fd_;
+  const char* path_;
+
+  ELF::Ehdr header_;
+  size_t phdr_num_;
+
+  void* phdr_mmap_;  // temporary copy of the program header.
+  ELF::Phdr* phdr_table_;
+  ELF::Addr phdr_size_;  // and its size.
+
+  off_t file_offset_;
+  void* wanted_load_address_;
+  void* load_start_;     // First page of reserved address space.
+  ELF::Addr load_size_;  // Size in bytes of reserved address space.
+  ELF::Addr load_bias_;  // load_bias, add this value to all "vaddr"
+                         // values in the library to get the corresponding
+                         // memory address.
+
+  const ELF::Phdr* loaded_phdr_;  // points to the loaded program header.
+
+  // Individual steps used by ::LoadAt()
+  bool ReadElfHeader(Error* error);
+  bool ReadProgramHeader(Error* error);
+  bool ReserveAddressSpace(Error* error);
+  bool LoadSegments(Error* error);
+  bool FindPhdr(Error* error);
+  bool CheckPhdr(ELF::Addr, Error* error);
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_ELF_LOADER_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_relocator.cpp b/sources/android/crazy_linker/src/crazy_linker_elf_relocator.cpp
new file mode 100644
index 0000000..554ae2b
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_relocator.cpp
@@ -0,0 +1,485 @@
+// 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_elf_relocator.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include "crazy_linker_debug.h"
+#include "crazy_linker_util.h"
+#include "linker_phdr.h"
+
+// Set to 1 to print debug traces during relocations.
+// Default is 0 since there are _tons_ of them.
+#define DEBUG_RELOCATIONS 0
+
+#define RLOG(...) LOG_IF(DEBUG_RELOCATIONS, __VA_ARGS__)
+#define RLOG_ERRNO(...) LOG_IF_ERRNO(DEBUG_RELOCATIONS, __VA_ARGS__)
+
+#ifndef DF_SYMBOLIC
+#define DF_SYMBOLIC 2
+#endif
+
+#ifndef DF_TEXTREL
+#define DF_TEXTREL 4
+#endif
+
+#ifndef DT_FLAGS
+#define DT_FLAGS 30
+#endif
+
+// Processor-specific relocation types supported by the linker.
+#ifdef __arm__
+
+#define R_ARM_ABS32 2
+#define R_ARM_REL32 3
+#define R_ARM_GLOB_DAT 21
+#define R_ARM_JUMP_SLOT 22
+#define R_ARM_COPY 20
+#define R_ARM_RELATIVE 23
+
+#endif  // __arm__
+
+#ifdef __i386__
+
+/* i386 relocations */
+#define R_386_32 1
+#define R_386_PC32 2
+#define R_386_GLOB_DAT 6
+#define R_386_JMP_SLOT 7
+#define R_386_RELATIVE 8
+
+#endif  // __i386__
+
+namespace crazy {
+
+namespace {
+
+// List of known relocation types the relocator knows about.
+enum RelocationType {
+  RELOCATION_TYPE_UNKNOWN = 0,
+  RELOCATION_TYPE_ABSOLUTE = 1,
+  RELOCATION_TYPE_RELATIVE = 2,
+  RELOCATION_TYPE_PC_RELATIVE = 3,
+  RELOCATION_TYPE_COPY = 4,
+};
+
+// Convert an ELF relocation type info a RelocationType value.
+RelocationType GetRelocationType(unsigned r_type) {
+  switch (r_type) {
+#ifdef __arm__
+    case R_ARM_JUMP_SLOT:
+    case R_ARM_GLOB_DAT:
+    case R_ARM_ABS32:
+      return RELOCATION_TYPE_ABSOLUTE;
+
+    case R_ARM_REL32:
+    case R_ARM_RELATIVE:
+      return RELOCATION_TYPE_RELATIVE;
+
+    case R_ARM_COPY:
+      return RELOCATION_TYPE_COPY;
+#endif
+
+#ifdef __i386__
+    case R_386_JMP_SLOT:
+    case R_386_GLOB_DAT:
+    case R_386_32:
+      return RELOCATION_TYPE_ABSOLUTE;
+
+    case R_386_RELATIVE:
+      return RELOCATION_TYPE_RELATIVE;
+
+    case R_386_PC32:
+      return RELOCATION_TYPE_PC_RELATIVE;
+#endif
+
+#ifdef __mips__
+    case R_MIPS_REL32:
+      return RELOCATION_TYPE_RELATIVE;
+#endif
+
+    default:
+      return RELOCATION_TYPE_UNKNOWN;
+  }
+}
+
+}  // namespace
+
+ElfRelocator::ElfRelocator() { memset(this, 0, sizeof(*this)); }
+
+bool ElfRelocator::Init(const ELF::Phdr* phdr,
+                        size_t phdr_count,
+                        size_t load_bias,
+                        const ELF::Dyn* dyn,
+                        size_t count,
+                        Error* error) {
+  phdr_ = phdr;
+  phdr_count_ = phdr_count;
+  load_bias_ = load_bias;
+
+  LOG("%s: phdr=%p phdr_count=%d load_bias=%x dyn_table=%p dyn_count=%d\n",
+      __FUNCTION__,
+      phdr,
+      (int)phdr_count,
+      (int)load_bias,
+      dyn,
+      (int)count);
+
+  for (; count > 0; dyn++, count--) {
+    // Be paranoid.
+    if (dyn->d_tag == DT_NULL)
+      break;
+
+    ELF::Addr dyn_value = dyn->d_un.d_val;
+    uintptr_t dyn_addr = load_bias_ + dyn->d_un.d_ptr;
+
+    switch (dyn->d_tag) {
+      case DT_PLTREL:
+        // NOTE: Yes, there is nothing to record here, the content of
+        // plt_rel_ will come from DT_JMPREL instead.
+        RLOG("  DT_PLTREL");
+        if (dyn_value != DT_REL) {
+          *error = "Unsupported DT_RELA entry in dynamic section";
+          return false;
+        }
+        break;
+      case DT_JMPREL:
+        RLOG("  DT_JMPREL addr=%p\n", dyn_addr);
+        plt_rel_ = reinterpret_cast<ELF::Rel*>(dyn_addr);
+        break;
+      case DT_PLTRELSZ:
+        plt_rel_count_ = dyn_value / sizeof(ELF::Rel);
+        RLOG("  DT_PLTRELSZ size=%d count=%d\n", dyn_value, plt_rel_count_);
+        break;
+      case DT_REL:
+        RLOG("  DT_REL addr=%p\n", dyn_addr);
+        rel_ = reinterpret_cast<ELF::Rel*>(dyn_addr);
+        break;
+      case DT_RELSZ:
+        rel_count_ = dyn_value / sizeof(ELF::Rel);
+        RLOG("  DT_RELSZ size=%d count=%d\n", dyn_value, rel_count_);
+        break;
+      case DT_PLTGOT:
+        // NOTE from original linker:
+        // Save this in case we decide to do lazy binding. We don't yet.
+        RLOG("  DT_PLTGOT addr=%p\n", dyn_addr);
+        plt_got_ = reinterpret_cast<uintptr_t*>(dyn_addr);
+        break;
+      case DT_RELA:
+        *error = "Unsupported DT_RELA entry in dynamic section";
+        return false;
+      case DT_TEXTREL:
+        RLOG("  DT_TEXTREL\n");
+        has_text_relocations_ = true;
+        break;
+      case DT_SYMBOLIC:
+        RLOG("  DT_SYMBOLIC\n");
+        has_symbolic_ = true;
+        break;
+      case DT_FLAGS:
+        if (dyn_value & DF_TEXTREL)
+          has_text_relocations_ = true;
+        if (dyn_value & DF_SYMBOLIC)
+          has_symbolic_ = true;
+        RLOG(" DT_FLAGS has_text_relocations=%s has_symbolic=%s\n",
+             has_text_relocations_ ? "true" : "false",
+             has_symbolic_ ? "true" : "false");
+        break;
+#if defined(__mips__)
+      case DT_MIPS_SYMTABNO:
+        RLOG("  DT_MIPS_SYMTABNO value=%d\n", dyn_value);
+        mips_symtab_count_ = dyn_value;
+        break;
+
+      case DT_MIPS_LOCAL_GOTNO:
+        RLOG("  DT_MIPS_LOCAL_GOTNO value=%d\n", dyn_value);
+        mips_local_got_count_ = dyn_value;
+        break;
+
+      case DT_MIPS_GOTSYM:
+        RLOG("  DT_MIPS_GOTSYM value=%d\n", dyn_value);
+        mips_gotsym_ = dyn_value;
+        break;
+#endif
+      default:
+        RLOG("  UNKNOWN tag=%d value=%08x addr=%p\n",
+             dyn->d_tag,
+             dyn_value,
+             (void*)dyn_addr);
+        ;
+    }
+  }
+  LOG("%s: Done!\n", __FUNCTION__);
+  return true;
+}
+
+bool ElfRelocator::Apply(SymbolResolver* resolver,
+                         const char* string_table,
+                         const ELF::Sym* symbol_table,
+                         Error* error) {
+  resolver_ = resolver;
+  string_table_ = string_table;
+  symbol_table_ = symbol_table;
+
+  LOG("%s: string_table=%p sybol_table=%p\n",
+      __FUNCTION__,
+      string_table,
+      symbol_table);
+
+  if (has_text_relocations_) {
+    if (phdr_table_unprotect_segments(phdr_, phdr_count_, load_bias_) < 0) {
+      error->Format("Can't unprotect loadable segments: %s", strerror(errno));
+      return false;
+    }
+  }
+
+  if (!ApplyRelocs(plt_rel_, plt_rel_count_, error) ||
+      !ApplyRelocs(rel_, rel_count_, error)) {
+    return false;
+  }
+
+#ifdef __mips__
+  if (!RelocateMipsGot(error))
+    return false;
+#endif
+
+  if (has_text_relocations_) {
+    if (phdr_table_protect_segments(phdr_, phdr_count_, load_bias_) < 0) {
+      error->Format("Can't protect loadable segments: %s", strerror(errno));
+      return false;
+    }
+  }
+
+  // Turn on GNU RELRO protection now.
+  LOG("%s: Enabling GNU RELRO protection\n", __FUNCTION__);
+
+  if (phdr_table_protect_gnu_relro(phdr_, phdr_count_, load_bias_) < 0) {
+    error->Format("Can't enable GNU RELRO protection: %s", strerror(errno));
+    return false;
+  }
+
+  LOG("%s: Done\n", __FUNCTION__);
+  return true;
+}
+
+bool ElfRelocator::ApplyRelocs(const ELF::Rel* rel,
+                               size_t rel_count,
+                               Error* error) {
+  RLOG("%s: rel=%p rel_count=%d\n", __FUNCTION__, rel, rel_count);
+
+  if (!rel)
+    return true;
+
+  for (size_t rel_n = 0; rel_n < rel_count; rel++, rel_n++) {
+    unsigned rel_type = ELF_R_TYPE(rel->r_info);
+    unsigned rel_symbol = ELF_R_SYM(rel->r_info);
+
+    ELF::Addr sym_addr = 0;
+    ELF::Addr reloc = static_cast<ELF::Addr>(rel->r_offset + load_bias_);
+    RLOG("  %d/%d reloc=%p offset=%p type=%d symbol=%d\n",
+         rel_n + 1,
+         rel_count,
+         reloc,
+         rel->r_offset,
+         rel_type,
+         rel_symbol);
+
+    if (rel_type == 0)
+      continue;
+
+    bool CRAZY_UNUSED resolved = false;
+
+    // If this is a symbolic relocation, compute the symbol's address.
+    if (__builtin_expect(rel_symbol != 0, 0)) {
+      const char* sym_name = string_table_ + symbol_table_[rel_symbol].st_name;
+      RLOG("    symbol name='%s'\n", sym_name);
+      void* address = resolver_->Lookup(sym_name);
+      if (address) {
+        // The symbol was found, so compute its address.
+        RLOG("%s: symbol %s resolved to %p\n", __FUNCTION__, sym_name, address);
+        resolved = true;
+        sym_addr = reinterpret_cast<ELF::Addr>(address);
+      } else {
+        // The symbol was not found. Normally this is an error except
+        // if this is a weak reference.
+        if (ELF_ST_BIND(symbol_table_[rel_symbol].st_info) != STB_WEAK) {
+          error->Format("Could not find symbol '%s'", sym_name);
+          return false;
+        }
+
+        resolved = true;
+        RLOG("%s: weak reference to unresolved symbol %s\n",
+             __FUNCTION__,
+             sym_name);
+
+        // IHI0044C AAELF 4.5.1.1:
+        // Libraries are not searched to resolve weak references.
+        // It is not an error for a weak reference to remain
+        // unsatisfied.
+        //
+        // During linking, the value of an undefined weak reference is:
+        // - Zero if the relocation type is absolute
+        // - The address of the place if the relocation is pc-relative
+        // - The address of nominal base address if the relocation
+        //   type is base-relative.
+        RelocationType r = GetRelocationType(rel_type);
+        if (r == RELOCATION_TYPE_ABSOLUTE || r == RELOCATION_TYPE_RELATIVE)
+          sym_addr = 0;
+        else if (r == RELOCATION_TYPE_PC_RELATIVE)
+          sym_addr = reloc;
+        else {
+          error->Format(
+              "Invalid weak relocation type (%d) for unknown symbol '%s'",
+              r,
+              sym_name);
+          return false;
+        }
+      }
+    }
+
+    // Apply the relocation.
+    ELF::Addr* target = reinterpret_cast<ELF::Addr*>(reloc);
+    switch (rel_type) {
+#ifdef __arm__
+      case R_ARM_JUMP_SLOT:
+        RLOG("  R_ARM_JUMP_SLOT target=%p addr=%p\n", target, sym_addr);
+        *target = sym_addr;
+        break;
+
+      case R_ARM_GLOB_DAT:
+        RLOG("  R_ARM_GLOB_DAT target=%p addr=%p\n", target, sym_addr);
+        *target = sym_addr;
+        break;
+
+      case R_ARM_ABS32:
+        RLOG("  R_ARM_ABS32 target=%p (%p) addr=%p\n",
+             target,
+             *target,
+             sym_addr);
+        *target += sym_addr;
+        break;
+
+      case R_ARM_REL32:
+        RLOG("  R_ARM_REL32 target=%p (%p) addr=%p offset=%p\n",
+             target,
+             *target,
+             sym_addr,
+             rel->r_offset);
+        *target += sym_addr - rel->r_offset;
+        break;
+
+      case R_ARM_RELATIVE:
+        RLOG("  R_ARM_RELATIVE target=%p (%p) bias=%p\n",
+             target,
+             *target,
+             load_bias_);
+        if (__builtin_expect(rel_symbol, 0)) {
+          *error = "Invalid relative relocation with symbol";
+          return false;
+        }
+        *target += load_bias_;
+        break;
+
+      case R_ARM_COPY:
+        // NOTE: These relocations are forbidden in shared libraries.
+        // The Android linker has special code to deal with this, which
+        // is not needed here.
+        RLOG("  R_ARM_COPY\n");
+        *error = "Invalid R_ARM_COPY relocation in shared library";
+        return false;
+#endif  // __arm__
+
+#ifdef __i386__
+      case R_386_JMP_SLOT:
+        *target = sym_addr;
+        break;
+
+      case R_386_GLOB_DAT:
+        *target = sym_addr;
+        break;
+
+      case R_386_RELATIVE:
+        if (rel_symbol) {
+          *error = "Invalid relative relocation with symbol";
+          return false;
+        }
+        *target += load_bias_;
+        break;
+
+      case R_386_32:
+        *target += sym_addr;
+        break;
+
+      case R_386_PC32:
+        *target += (sym_addr - reloc);
+        break;
+#endif  // __i386__
+
+#ifdef __mips__
+      case R_MIPS_REL32:
+        if (resolved)
+          *target += sym_addr;
+        else
+          *target += load_bias_;
+        break;
+#endif  // __mips__
+
+      default:
+        error->Format("Invalid relocation type (%d)", rel_type);
+        return false;
+    }
+  }
+
+  return true;
+}
+
+#ifdef __mips__
+bool ElfRelocator::RelocateMipsGot(Error* error) {
+  if (!plt_got_)
+    return true;
+
+  // Handle the local GOT entries.
+  // This mimics what the system linker does.
+  // Note from the system linker:
+  // got[0]: lazy resolver function address.
+  // got[1]: may be used for a GNU extension.
+  // Set it to a recognizable address in case someone calls it
+  // (should be _rtld_bind_start).
+  ELF::Addr* got = plt_got_;
+  got[0] = 0xdeadbeef;
+  if (got[1] & 0x80000000)
+    got[1] = 0xdeadbeef;
+
+  for (ELF::Addr n = 2; n < mips_local_got_count_; ++n)
+    got[n] += load_bias_;
+
+  // Handle the global GOT entries.
+  got += mips_local_got_count_;
+  const ELF::Sym* sym = symbol_table_ + mips_gotsym_;
+  const ELF::Sym* sym_limit = symbol_table_ + mips_symtab_count_;
+  for (; sym < sym_limit; ++sym, ++got) {
+    const char* sym_name = string_table_ + sym->st_name;
+    void* sym_addr = resolver_->Lookup(sym_name);
+    if (sym_addr) {
+      *got = reinterpret_cast<ELF::Addr>(sym_addr);
+      continue;
+    }
+
+    // Undefined symbols are only ok if this is a weak reference.
+    if (ELF_ST_BIND(sym->st_info) != STB_WEAK) {
+      error->Format("Cannot locate symbol %s", sym_name);
+      return false;
+    }
+
+    // Leave undefined symbols from weak references to 0.
+    *got = 0;
+  }
+
+  return true;
+}
+#endif  // __mips__
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_elf_relocator.h b/sources/android/crazy_linker/src/crazy_linker_elf_relocator.h
new file mode 100644
index 0000000..a2d42c0
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_elf_relocator.h
@@ -0,0 +1,104 @@
+// 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.
+
+#ifndef CRAZY_LINKER_ELF_RELOCATOR_H
+#define CRAZY_LINKER_ELF_RELOCATOR_H
+
+#include "crazy_linker_error.h"
+#include "elf_traits.h"
+
+namespace crazy {
+
+// A class used to perform ELF relocations in a library.
+// Usage is:
+//   1/ Create new instance.
+//   2/ Call Init().
+//   3/ Call Apply() to apply all relocations.
+//
+// If any of these steps fail, the library will be in inconsistent
+// state and must be unloaded/unmapped as soon as possible by the
+// caller to avoid any issue.
+class ElfRelocator {
+ public:
+  // Abstract class used to resolve symbol names into addresses.
+  // Callers of ::Apply() should pass the address of a derived class
+  // that properly implements the Lookup() method.
+  class SymbolResolver {
+   public:
+    SymbolResolver() {}
+    ~SymbolResolver() {}
+    virtual void* Lookup(const char* symbol_name) = 0;
+  };
+
+  ElfRelocator();
+
+  // Initialize the relocator. This will parse the library's dynamic
+  // table to populate the structure.
+  // |phdr| is the library's program header address.
+  // |phdr_count| is the number of program header entries.
+  // |load_bias| is the library load bias.
+  // |dynamic| is the library's dynamic table.
+  // |dyn_count| is the number of dynamic table entries.
+  // On failure, return false and sets the |error| message.
+  bool Init(const ELF::Phdr* phdr,
+            size_t phdr_count,
+            size_t load_bias,
+            const ELF::Dyn* dynamic,
+            size_t dyn_count,
+            Error* error);
+
+  // After initialization, apply all relocations in the library.
+  // |resolver| must be an non-abstract instance of SymbolResolver
+  // that will be used to resolve symbol lookups during relocations.
+  // |string_table| is the library's string table.
+  // |symbol_table| is the library's symbol table.
+  // On failure, returns false and sets the |error| message.
+  bool Apply(SymbolResolver* resolver,
+             const char* string_table,
+             const ELF::Sym* symbol_table,
+             Error* error);
+
+ private:
+  bool ApplyRelocs(const ELF::Rel* rel, size_t rel_count, Error* error);
+
+#ifdef __mips__
+  bool RelocateMipsGot(Error* error);
+#endif
+
+  // Program header table
+  const ELF::Phdr* phdr_;
+  size_t phdr_count_;
+
+  // Load bias to apply to all virtual address in the library.
+  size_t load_bias_;
+
+  // Relocations for the GOT and the PLT section that contains
+  // the trampolines to call external symbols.
+  ELF::Addr* plt_got_;
+  ELF::Rel* plt_rel_;
+  size_t plt_rel_count_;
+
+  // Relocations for the rest of the library.
+  ELF::Rel* rel_;
+  size_t rel_count_;
+
+#if defined(__mips__)
+  // MIPS-specific relocation fields.
+  ELF::Word mips_symtab_count_;
+  ELF::Word mips_local_got_count_;
+  ELF::Word mips_gotsym_;
+#endif
+
+  bool has_text_relocations_;
+  bool has_symbolic_;
+
+  // Used only during Apply().
+  SymbolResolver* resolver_;
+  const char* string_table_;
+  const ELF::Sym* symbol_table_;
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_ELF_RELOCATOR_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_error.cpp b/sources/android/crazy_linker/src/crazy_linker_error.cpp
new file mode 100644
index 0000000..faa98ba
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_error.cpp
@@ -0,0 +1,50 @@
+// 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_error.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "crazy_linker_debug.h"
+
+namespace crazy {
+
+void Error::Set(const char* message) {
+  if (!message)
+    message = "";
+  strlcpy(buff_, message, sizeof(buff_));
+
+  LOG("--- ERROR: %s\n", buff_);
+}
+
+void Error::Append(const char* message) {
+  if (!message)
+    return;
+  strlcat(buff_, message, sizeof(buff_));
+
+  LOG("--- ERROR: %s\n", buff_);
+}
+
+void Error::Format(const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  vsnprintf(buff_, sizeof(buff_), fmt, args);
+  va_end(args);
+
+  LOG("--- ERROR: %s\n", buff_);
+}
+
+void Error::AppendFormat(const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  size_t buff_len = strlen(buff_);
+  vsnprintf(buff_ + buff_len, sizeof(buff_) - buff_len, fmt, args);
+  va_end(args);
+
+  LOG("--- ERROR: %s\n", buff_);
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_error.h b/sources/android/crazy_linker/src/crazy_linker_error.h
new file mode 100644
index 0000000..f34a7a0
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_error.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef CRAZY_LINKER_ERROR_H
+#define CRAZY_LINKER_ERROR_H
+
+namespace crazy {
+
+// A class used to hold a fixed-size buffer to hold error messages
+// as well as perform assignment and formatting.
+//
+// Usage examples:
+//     Error error;
+//     error = "Unimplemented feature";
+//     error->Set("Unimplemented feature");
+//     error->Format("Feature %s is not implemented", feature_name);
+//     error->Append(strerror(errno));
+//     error->AppendFormat("Error: %s", strerror(errno));
+//
+class Error {
+ public:
+  Error() { buff_[0] = '\0'; }
+
+  Error(const char* message) { Set(message); }
+
+  Error(const Error& other) { Set(other.buff_); }
+
+  const char* c_str() const { return buff_; }
+
+  void Set(const char* message);
+
+  void Format(const char* fmt, ...);
+
+  void Append(const char* message);
+
+  void AppendFormat(const char* fmt, ...);
+
+ private:
+  char buff_[512];
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_ERROR_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_error_unittest.cpp b/sources/android/crazy_linker/src/crazy_linker_error_unittest.cpp
new file mode 100644
index 0000000..2d74b0c
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_error_unittest.cpp
@@ -0,0 +1,54 @@
+// 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_error.h"
+
+#include <minitest/minitest.h>
+
+namespace crazy {
+
+TEST(Error, ConstructEmpty) {
+  Error error;
+  EXPECT_STREQ("", error.c_str());
+}
+
+TEST(Error, ConstructWithString) {
+  Error error("Foo Bar");
+  EXPECT_STREQ("Foo Bar", error.c_str());
+}
+
+TEST(Error, CopyConstructor) {
+  Error error("FooFoo");
+  Error error2(error);
+
+  EXPECT_STREQ("FooFoo", error2.c_str());
+}
+
+TEST(Error, Set) {
+  Error error;
+  error.Set("BarFoo");
+  EXPECT_STREQ("BarFoo", error.c_str());
+  error.Set("FooBar");
+  EXPECT_STREQ("FooBar", error.c_str());
+}
+
+TEST(Error, Append) {
+  Error error("Foo");
+  error.Append("Bar");
+  EXPECT_STREQ("FooBar", error.c_str());
+}
+
+TEST(Error, Format) {
+  Error error;
+  error.Format("%s %s!", "Hi", "Cowboy");
+  EXPECT_STREQ("Hi Cowboy!", error.c_str());
+}
+
+TEST(Error, AppendFormat) {
+  Error error("Hi");
+  error.AppendFormat(" there %s!", "Cowboy");
+  EXPECT_STREQ("Hi there Cowboy!", error.c_str());
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_globals.cpp b/sources/android/crazy_linker/src/crazy_linker_globals.cpp
new file mode 100644
index 0000000..26c399c
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_globals.cpp
@@ -0,0 +1,34 @@
+// 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_globals.h"
+
+#include <pthread.h>
+
+#include "crazy_linker_system.h"
+
+namespace crazy {
+
+namespace {
+
+Globals* g_globals = NULL;
+pthread_once_t g_globals_once = PTHREAD_ONCE_INIT;
+
+void CreateGlobalsInstance() { g_globals = new Globals(); }
+
+}  // namespace
+
+Globals::Globals() : search_paths_(), rdebug_() {
+  pthread_mutex_init(&lock_, NULL);
+  search_paths_.ResetFromEnv("LD_LIBRARY_PATH");
+}
+
+Globals::~Globals() { pthread_mutex_destroy(&lock_); }
+
+Globals* Globals::Get() {
+  pthread_once(&g_globals_once, CreateGlobalsInstance);
+  return g_globals;
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_globals.h b/sources/android/crazy_linker/src/crazy_linker_globals.h
new file mode 100644
index 0000000..21ea222
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_globals.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef CRAZY_LINKER_GLOBALS_H
+#define CRAZY_LINKER_GLOBALS_H
+
+#include <pthread.h>
+
+#include "crazy_linker_library_list.h"
+#include "crazy_linker_rdebug.h"
+#include "crazy_linker_search_path_list.h"
+#include "crazy_linker_util.h"
+
+// All crazy linker globals are declared in this header.
+
+namespace crazy {
+
+class Globals {
+ public:
+  Globals();
+  ~Globals();
+
+  void Lock() { pthread_mutex_lock(&lock_); }
+
+  void Unlock() { pthread_mutex_unlock(&lock_); }
+
+  static Globals* Get();
+
+  static LibraryList* GetLibraries() { return &Get()->libraries_; }
+
+  static SearchPathList* GetSearchPaths() { return &Get()->search_paths_; }
+
+  static RDebug* GetRDebug() { return &Get()->rdebug_; }
+
+ private:
+  pthread_mutex_t lock_;
+  LibraryList libraries_;
+  SearchPathList search_paths_;
+  RDebug rdebug_;
+};
+
+// Helper class to access the globals with scoped locking.
+class ScopedGlobalLock {
+ public:
+  ScopedGlobalLock() { Globals::Get()->Lock(); }
+
+  ~ScopedGlobalLock() { Globals::Get()->Unlock(); }
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_GLOBALS_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_globals_unittest.cpp b/sources/android/crazy_linker/src/crazy_linker_globals_unittest.cpp
new file mode 100644
index 0000000..01eb346
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_globals_unittest.cpp
@@ -0,0 +1,21 @@
+// 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_globals.h"
+
+#include <minitest/minitest.h>
+
+#include "crazy_linker_system_mock.h"
+
+namespace crazy {
+
+TEST(Globals, Get) {
+  SystemMock sys;
+  ASSERT_TRUE(Globals::Get());
+  ASSERT_TRUE(Globals::GetLibraries());
+  ASSERT_TRUE(Globals::GetSearchPaths());
+  ASSERT_TRUE(Globals::GetRDebug());
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_library_list.cpp b/sources/android/crazy_linker/src/crazy_linker_library_list.cpp
new file mode 100644
index 0000000..dd605d9
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_library_list.cpp
@@ -0,0 +1,392 @@
+// 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_library_list.h"
+
+#include <dlfcn.h>
+
+#include "crazy_linker_debug.h"
+#include "crazy_linker_library_view.h"
+#include "crazy_linker_globals.h"
+#include "crazy_linker_rdebug.h"
+#include "crazy_linker_shared_library.h"
+#include "crazy_linker_system.h"
+
+namespace crazy {
+
+namespace {
+
+// A helper struct used when looking up symbols in libraries.
+struct SymbolLookupState {
+  void* found_addr;
+  void* weak_addr;
+  int weak_count;
+
+  SymbolLookupState() : found_addr(NULL), weak_addr(NULL), weak_count(0) {}
+
+  // Check a symbol entry.
+  bool CheckSymbol(const char* symbol, SharedLibrary* lib) {
+    ELF::Sym* entry = lib->LookupSymbolEntry(symbol);
+    if (!entry)
+      return false;
+
+    void* address = reinterpret_cast<void*>(lib->base + entry->st_value);
+
+    // If this is a strong symbol, record it and return true.
+    if (ELF_ST_BIND(entry->st_info) == STB_GLOBAL) {
+      found_addr = address;
+      return true;
+    }
+    // If this is a weak symbol, record the first one and
+    // increment the weak_count.
+    if (++weak_count == 1)
+      weak_addr = address;
+
+    return false;
+  }
+};
+
+}  // namespace
+
+LibraryList::LibraryList() : head_(0), count_(0), has_error_(false) {
+  // Nothing for now
+}
+
+LibraryList::~LibraryList() {
+  // Invalidate crazy library list.
+  head_ = NULL;
+
+  // Destroy all known libraries.
+  while (!known_libraries_.IsEmpty()) {
+    LibraryView* wrap = known_libraries_.PopLast();
+    delete wrap;
+  }
+}
+
+LibraryView* LibraryList::FindLibraryByName(const char* base_name) {
+  // Sanity check.
+  if (!base_name || strchr(base_name, '/'))
+    return NULL;
+
+  for (size_t n = 0; n < known_libraries_.GetCount(); ++n) {
+    LibraryView* wrap = known_libraries_[n];
+    if (!strcmp(base_name, wrap->GetName()))
+      return wrap;
+  }
+  return NULL;
+}
+
+void* LibraryList::FindSymbolFrom(const char* symbol_name, LibraryView* from) {
+  SymbolLookupState lookup_state;
+
+  if (!from)
+    return NULL;
+
+  // Use a work-queue and a set to ensure to perform a breadth-first
+  // search.
+  Vector<LibraryView*> work_queue;
+  Set<LibraryView*> visited_set;
+
+  work_queue.PushBack(from);
+
+  while (!work_queue.IsEmpty()) {
+    LibraryView* lib = work_queue.PopFirst();
+    if (lib->IsCrazy()) {
+      if (lookup_state.CheckSymbol(symbol_name, lib->GetCrazy()))
+        return lookup_state.found_addr;
+    } else if (lib->IsSystem()) {
+      // TODO(digit): Support weak symbols in system libraries.
+      // With the current code, all symbols in system libraries
+      // are assumed to be non-weak.
+      void* addr = lib->LookupSymbol(symbol_name);
+      if (addr)
+        return addr;
+    }
+
+    // If this is a crazy library, add non-visited dependencies
+    // to the work queue.
+    if (lib->IsCrazy()) {
+      SharedLibrary::DependencyIterator iter(lib->GetCrazy());
+      while (iter.GetNext()) {
+        LibraryView* dependency = FindKnownLibrary(iter.GetName());
+        if (dependency && !visited_set.Has(dependency)) {
+          work_queue.PushBack(dependency);
+          visited_set.Add(dependency);
+        }
+      }
+    }
+  }
+
+  if (lookup_state.weak_count >= 1) {
+    // There was at least a single weak symbol definition, so use
+    // the first one found in breadth-first search order.
+    return lookup_state.weak_addr;
+  }
+
+  // There was no symbol definition.
+  return NULL;
+}
+
+LibraryView* LibraryList::FindLibraryForAddress(void* address) {
+  // Linearly scan all libraries, looking for one that contains
+  // a given address. NOTE: This doesn't check that this falls
+  // inside one of the mapped library segments.
+  uintptr_t addr = reinterpret_cast<uintptr_t>(address);
+
+  for (size_t n = 0; n < known_libraries_.GetCount(); ++n) {
+    LibraryView* wrap = known_libraries_[n];
+    // TODO(digit): Search addresses inside system libraries.
+    if (wrap->IsCrazy()) {
+      SharedLibrary* lib = wrap->GetCrazy();
+      if (lib->base <= addr && addr < lib->base + lib->size)
+        return wrap;
+    }
+  }
+  return NULL;
+}
+
+#ifdef __arm__
+_Unwind_Ptr LibraryList::FindArmExIdx(void* pc, int* count) {
+  uintptr_t addr = reinterpret_cast<uintptr_t>(pc);
+
+  for (SharedLibrary* lib = head_; lib; lib = lib->list_next) {
+    if (addr >= lib->base && addr < lib->base + lib->size) {
+      *count = static_cast<int>(lib->ARM_exidx_count);
+      return reinterpret_cast<_Unwind_Ptr>(lib->ARM_exidx);
+    }
+  }
+  *count = 0;
+  return NULL;
+}
+#else  // !__arm__
+int LibraryList::IteratePhdr(PhdrIterationCallback callback, void* data) {
+  int result = 0;
+  for (SharedLibrary* lib = head_; lib; lib = lib->list_next) {
+    dl_phdr_info info;
+    info.dlpi_addr = lib->link_map.l_addr;
+    info.dlpi_name = lib->link_map.l_name;
+    info.dlpi_phdr = lib->phdr;
+    info.dlpi_phnum = lib->phnum;
+    result = callback(&info, sizeof(info), data);
+    if (result)
+      break;
+  }
+  return result;
+}
+#endif  // !__arm__
+
+void LibraryList::UnloadLibrary(LibraryView* wrap) {
+  // Sanity check.
+  LOG("%s: for %s (ref_count=%d)\n",
+      __FUNCTION__,
+      wrap->GetName(),
+      wrap->ref_count());
+
+  if (!wrap->IsSystem() && !wrap->IsCrazy())
+    return;
+
+  if (!wrap->SafeDecrementRef())
+    return;
+
+  // If this is a crazy library, perform manual cleanup first.
+  if (wrap->IsCrazy()) {
+    SharedLibrary* lib = wrap->GetCrazy();
+
+    // Remove from internal list of crazy libraries.
+    if (lib->list_next)
+      lib->list_next->list_prev = lib->list_prev;
+    if (lib->list_prev)
+      lib->list_prev->list_next = lib->list_next;
+    if (lib == head_)
+      head_ = lib->list_next;
+
+    // Call JNI_OnUnload, if necessary, then the destructors.
+    lib->CallJniOnUnload();
+    lib->CallDestructors();
+
+    // Unload the dependencies recursively.
+    SharedLibrary::DependencyIterator iter(lib);
+    while (iter.GetNext()) {
+      LibraryView* dependency = FindKnownLibrary(iter.GetName());
+      if (dependency)
+        UnloadLibrary(dependency);
+    }
+
+    // Tell GDB of this removal.
+    Globals::GetRDebug()->DelEntry(&lib->link_map);
+  }
+
+  known_libraries_.Remove(wrap);
+
+  // Delete the wrapper, which will delete the crazy library, or
+  // dlclose() the system one.
+  delete wrap;
+}
+
+LibraryView* LibraryList::LoadLibrary(const char* lib_name,
+                                      int dlopen_mode,
+                                      uintptr_t load_address,
+                                      off_t file_offset,
+                                      SearchPathList* search_path_list,
+                                      Error* error) {
+
+  const char* base_name = GetBaseNamePtr(lib_name);
+
+  LOG("%s: lib_name='%s'\n", __FUNCTION__, lib_name);
+
+  // First check whether a library with the same base name was
+  // already loaded.
+  LibraryView* wrap = FindKnownLibrary(lib_name);
+  if (wrap) {
+    if (load_address) {
+      // Check that this is a crazy library and that is was loaded at
+      // the correct address.
+      if (!wrap->IsCrazy()) {
+        error->Format("System library can't be loaded at fixed address %08x",
+                      load_address);
+        return NULL;
+      }
+      uintptr_t actual_address = wrap->GetCrazy()->base;
+      if (actual_address != load_address) {
+        error->Format("Library already loaded at @%08x, can't load it at @%08x",
+                      actual_address,
+                      load_address);
+        return NULL;
+      }
+    }
+    wrap->AddRef();
+    return wrap;
+  }
+
+  if (IsSystemLibrary(lib_name)) {
+    // This is a system library, probably because we're loading the
+    // library as a dependency.
+    LOG("%s: Loading system library '%s'\n", __FUNCTION__, lib_name);
+    ::dlerror();
+    void* system_lib = dlopen(lib_name, dlopen_mode);
+    if (!system_lib) {
+      error->Format("Can't load system library %s: %s", lib_name, ::dlerror());
+      return NULL;
+    }
+
+    LibraryView* wrap = new LibraryView();
+    wrap->SetSystem(system_lib, lib_name);
+    known_libraries_.PushBack(wrap);
+
+    LOG("%s: System library %s loaded at %p\n", __FUNCTION__, lib_name, wrap);
+    LOG("  name=%s\n", wrap->GetName());
+    return wrap;
+  }
+
+  ScopedPtr<SharedLibrary> lib(new SharedLibrary());
+
+  // Find the full library path.
+  String full_path;
+
+  if (!strchr(lib_name, '/')) {
+    LOG("%s: Looking through the search path list\n", __FUNCTION__);
+    const char* path = search_path_list->FindFile(lib_name);
+    if (!path) {
+      error->Format("Can't find library file %s", lib_name);
+      return NULL;
+    }
+    full_path = path;
+  } else {
+    if (lib_name[0] != '/') {
+      // Need to transform this into a full path.
+      full_path = GetCurrentDirectory();
+      if (full_path.size() && full_path[full_path.size() - 1] != '/')
+        full_path += '/';
+      full_path += lib_name;
+    } else {
+      // Absolute path. Easy.
+      full_path = lib_name;
+    }
+    LOG("%s: Full library path: %s\n", __FUNCTION__, full_path.c_str());
+    if (!PathIsFile(full_path.c_str())) {
+      error->Format("Library file doesn't exist: %s", full_path.c_str());
+      return NULL;
+    }
+  }
+
+  // Load the library
+  if (!lib->Load(full_path.c_str(), load_address, file_offset, error))
+    return NULL;
+
+  // Load all dependendent libraries.
+  LOG("%s: Loading dependencies of %s\n", __FUNCTION__, base_name);
+  SharedLibrary::DependencyIterator iter(lib.Get());
+  Vector<LibraryView*> dependencies;
+  while (iter.GetNext()) {
+    Error dep_error;
+    LibraryView* dependency = LoadLibrary(iter.GetName(),
+                                          dlopen_mode,
+                                          0U /* load address */,
+                                          0U /* file offset */,
+                                          search_path_list,
+                                          &dep_error);
+    if (!dependency) {
+      error->Format("When loading %s: %s", base_name, dep_error.c_str());
+      return NULL;
+    }
+    dependencies.PushBack(dependency);
+  }
+  if (CRAZY_DEBUG) {
+    LOG("%s: Dependencies loaded for %s\n", __FUNCTION__, base_name);
+    for (size_t n = 0; n < dependencies.GetCount(); ++n)
+      LOG("  ... %p %s\n", dependencies[n], dependencies[n]->GetName());
+    LOG("    dependencies @%p\n", &dependencies);
+  }
+
+  // Relocate the library.
+  LOG("%s: Relocating %s", __FUNCTION__, base_name);
+  if (!lib->Relocate(this, &dependencies, error))
+    return NULL;
+
+  // Notify GDB of load.
+  lib->link_map.l_addr = lib->base;
+  lib->link_map.l_name = const_cast<char*>(lib->base_name);
+  lib->link_map.l_ld = reinterpret_cast<uintptr_t>(lib->dynamic);
+  Globals::GetRDebug()->AddEntry(&lib->link_map);
+
+  // The library was properly loaded, add it to the list of crazy
+  // libraries. IMPORTANT: Do this _before_ calling the constructors
+  // because these could call dlopen().
+  lib->list_next = head_;
+  lib->list_prev = NULL;
+  if (head_)
+    head_->list_prev = lib.Get();
+  head_ = lib.Get();
+
+  // Then create a new LibraryView for it.
+  wrap = new LibraryView();
+  wrap->SetCrazy(lib.Get(), lib_name);
+  known_libraries_.PushBack(wrap);
+
+  LOG("%s: Running constructors for %s\n", __FUNCTION__, base_name);
+
+  // Now run the constructors.
+  lib->CallConstructors();
+
+  LOG("%s: Done loading %s\n", __FUNCTION__, base_name);
+  lib.Release();
+
+  return wrap;
+}
+
+void LibraryList::AddLibrary(LibraryView* wrap) {
+  known_libraries_.PushBack(wrap);
+}
+
+LibraryView* LibraryList::FindKnownLibrary(const char* name) {
+  const char* base_name = GetBaseNamePtr(name);
+  for (size_t n = 0; n < known_libraries_.GetCount(); ++n) {
+    LibraryView* wrap = known_libraries_[n];
+    if (!strcmp(base_name, wrap->GetName()))
+      return wrap;
+  }
+  return NULL;
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_library_list.h b/sources/android/crazy_linker/src/crazy_linker_library_list.h
new file mode 100644
index 0000000..301febd
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_library_list.h
@@ -0,0 +1,105 @@
+// 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.
+
+#ifndef CRAZY_LINKER_LIBRARY_LIST_H
+#define CRAZY_LINKER_LIBRARY_LIST_H
+
+#include <link.h>
+
+#include "crazy_linker_error.h"
+#include "crazy_linker_search_path_list.h"
+#include "elf_traits.h"
+
+// This header contains definitions related to the global list of
+// library views maintained by the crazy linker. Each library view
+// points to either a crazy library, or a system one.
+
+namespace crazy {
+
+class SharedLibrary;
+class LibraryView;
+
+// The list of all shared libraries loaded by the crazy linker.
+// IMPORTANT: This class is not thread-safe!
+class LibraryList {
+ public:
+  LibraryList();
+  ~LibraryList();
+
+  // Find a library in the list by its base name.
+  // |base_name| must not contain a directory separator.
+  LibraryView* FindLibraryByName(const char* base_name);
+
+  // Lookup for a given |symbol_name|, starting from |from_lib|
+  // then through its dependencies in breadth-first search order.
+  // On failure, returns NULL.
+  void* FindSymbolFrom(const char* symbol_name, LibraryView* from_lib);
+
+  // Return the address of a visible given symbol. Used to implement
+  // the dlsym() wrapper. Returns NULL on failure.
+  void* FindAddressForSymbol(const char* symbol_name);
+
+  // Find a SharedLibrary that contains a given address, or NULL if none
+  // could be found. This simply scans all libraries.
+  LibraryView* FindLibraryForAddress(void* address);
+
+#ifdef __arm__
+  // Find the base address of the .ARM.exidx section corresponding
+  // to the address |pc|, as well as the number of 8-byte entries in
+  // the table into |*count|. Used to implement the wrapper for
+  // dl_unwind_find_exidx().
+  _Unwind_Ptr FindArmExIdx(void* pc, int* count);
+#else
+  typedef int (*PhdrIterationCallback)(dl_phdr_info* info,
+                                       size_t info_size,
+                                       void* data);
+
+  // Loop over all loaded libraries and call the |cb| callback
+  // on each iteration. If the function returns 0, stop immediately
+  // and return its value. Used to implement the wrapper for
+  // dl_iterate_phdr().
+  int IteratePhdr(PhdrIterationCallback callback, void* data);
+#endif
+
+  // Try to load a library, possibly at a fixed address.
+  // On failure, returns NULL and sets the |error| message.
+  LibraryView* LoadLibrary(const char* path,
+                           int dlopen_flags,
+                           uintptr_t load_address,
+                           off_t file_offset,
+                           SearchPathList* search_path_list,
+                           Error* error);
+
+  // Unload a given shared library. This really decrements the library's
+  // internal reference count. When it reaches zero, the library's
+  // destructors are run, its dependencies are unloaded, then the
+  // library is removed from memory.
+  void UnloadLibrary(LibraryView* lib);
+
+  // Used internally by the wrappers only.
+  void AddLibrary(LibraryView* lib);
+
+ private:
+  LibraryList(const LibraryList&);
+  LibraryList& operator=(const LibraryList&);
+
+  void ClearError();
+
+  // The list of all known libraries.
+  Vector<LibraryView*> known_libraries_;
+
+  LibraryView* FindKnownLibrary(const char* name);
+
+  // The list of all libraries loaded by the crazy linker.
+  // This does _not_ include system libraries present in known_libraries_.
+  SharedLibrary* head_;
+
+  size_t count_;
+  bool has_error_;
+  char error_buffer_[512];
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_LIBRARY_LIST_H
\ No newline at end of file
diff --git a/sources/android/crazy_linker/src/crazy_linker_library_view.cpp b/sources/android/crazy_linker/src/crazy_linker_library_view.cpp
new file mode 100644
index 0000000..c78e87c
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_library_view.cpp
@@ -0,0 +1,83 @@
+// 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_library_view.h"
+
+#include <dlfcn.h>
+#include "crazy_linker_debug.h"
+#include "crazy_linker_globals.h"
+#include "crazy_linker_shared_library.h"
+
+namespace crazy {
+
+LibraryView::~LibraryView() {
+  LOG("%s: Destroying %s\n", __FUNCTION__, name_.c_str());
+  if (type_ == TYPE_SYSTEM) {
+    ::dlclose(system_);
+    system_ = NULL;
+  }
+  if (type_ == TYPE_CRAZY) {
+    delete crazy_;
+    crazy_ = NULL;
+  }
+  type_ = TYPE_NONE;
+}
+
+void* LibraryView::LookupSymbol(const char* symbol_name) {
+  if (type_ == TYPE_SYSTEM)
+    return ::dlsym(system_, symbol_name);
+
+  if (type_ == TYPE_CRAZY) {
+    LibraryList* lib_list = Globals::GetLibraries();
+    return lib_list->FindSymbolFrom(symbol_name, this);
+  }
+
+  return NULL;
+}
+
+bool LibraryView::GetInfo(size_t* load_address,
+                          size_t* load_size,
+                          size_t* relro_start,
+                          size_t* relro_size,
+                          int* relro_fd,
+                          Error* error) {
+  if (type_ != TYPE_CRAZY) {
+    *error = "No RELRO sharing with system libraries";
+    return false;
+  }
+
+  *load_address = crazy_->base;
+  *load_size = crazy_->size;
+  *relro_start = crazy_->relro_start;
+  *relro_size = crazy_->relro_size;
+  *relro_fd = crazy_->relro_fd;
+  return true;
+}
+
+bool LibraryView::EnableSharedRelro(Error* error) {
+  if (type_ != TYPE_CRAZY) {
+    *error = "No RELRO sharing with system libraries";
+    return false;
+  }
+
+  return crazy_->EnableSharedRelro(error);
+}
+
+bool LibraryView::UseSharedRelro(size_t relro_start,
+                                 size_t relro_size,
+                                 int relro_fd,
+                                 Error* error) {
+  if (type_ != TYPE_CRAZY) {
+    *error = "No RELRO sharing with system libraries";
+    return false;
+  }
+
+  // If there is no RELRO segment, don't do anything.
+  if (relro_fd < 0 || relro_size == 0)
+    return true;
+
+  return crazy_->UseSharedRelro(relro_start, relro_size, relro_fd, error);
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_library_view.h b/sources/android/crazy_linker/src/crazy_linker_library_view.h
new file mode 100644
index 0000000..ed776d0
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_library_view.h
@@ -0,0 +1,110 @@
+// 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.
+
+#ifndef CRAZY_LINKER_LIBRARY_VIEW_H
+#define CRAZY_LINKER_LIBRARY_VIEW_H
+
+#include "crazy_linker_error.h"
+#include "crazy_linker_util.h"
+
+namespace crazy {
+
+class SharedLibrary;
+
+// A LibraryView is a reference-counted handle to either a
+// crazy::SharedLibrary object or a library handle returned by the system's
+// dlopen() function.
+//
+// It has a name, which is always a base name, because only one
+// library with a given base name can be loaded in the system.
+class LibraryView {
+ public:
+  enum {
+    TYPE_NONE = 0xbaadbaad,
+    TYPE_SYSTEM = 0x2387cef,
+    TYPE_CRAZY = 0xcdef2387,
+  };
+
+  LibraryView()
+      : type_(TYPE_NONE), crazy_(NULL), system_(NULL), name_(), ref_count_(1) {}
+
+  ~LibraryView();
+
+  bool IsSystem() const { return type_ == TYPE_SYSTEM; }
+
+  bool IsCrazy() const { return type_ == TYPE_CRAZY; }
+
+  void SetSystem(void* system_lib, const char* name) {
+    type_ = TYPE_SYSTEM;
+    system_ = system_lib;
+    name_ = name;
+  }
+
+  void SetCrazy(SharedLibrary* crazy_lib, const char* name) {
+    type_ = TYPE_CRAZY;
+    crazy_ = crazy_lib;
+    name_ = name;
+  }
+
+  const char* GetName() { return name_.c_str(); }
+
+  SharedLibrary* GetCrazy() { return IsCrazy() ? crazy_ : NULL; }
+
+  void* GetSystem() { return IsSystem() ? system_ : NULL; }
+
+  void AddRef() { ref_count_++; }
+
+  // Decrement reference count. Returns true iff it reaches 0.
+  // This never destroys the object.
+  bool SafeDecrementRef() { return (--ref_count_ == 0); }
+
+  // Lookup a symbol from this library.
+  // If this is a crazy library, perform a breadth-first search,
+  // for system libraries, use dlsym() instead.
+  void* LookupSymbol(const char* symbol_name);
+
+  // Retrieve library information.
+  bool GetInfo(size_t* load_address,
+               size_t* load_size,
+               size_t* relro_start,
+               size_t* relro_size,
+               int* relro_fd,
+               Error* error);
+
+  // Enable RELRO section sharing. On This moves the RELRO section into
+  // a shareable ashmem region. On success, return true and sets
+  // |*load_address| to the library load address, |*load_size| to its
+  // full load size, |*relro_start| to the start of the RELRO section
+  // in memory, |*relro_size| to its size, and |*relro_fd| to the ashmem
+  // region's file descriptor.
+  // On failure, return false and sets |error| message.
+  bool EnableSharedRelro(Error* error);
+
+  // Use a shared RELRO section from another process. |relro_start|
+  // and |relro_size| are the start and size of the RELRO section,
+  // and |relro_fd| is a file descriptor for the ashmem region.
+  // On success, return true. On failure, return false and sets
+  // |error| message.
+  // NOTE: This function doesn't do anything except return true
+  // if |relro_fd| is negative, or |relro_size| is 0. This shall correspond
+  // to the case where there is no RELRO section in a library.
+  bool UseSharedRelro(size_t relro_start,
+                      size_t relro_size,
+                      int relro_fd,
+                      Error* error);
+
+  // Only used for debugging.
+  int ref_count() const { return ref_count_; }
+
+ private:
+  uint32_t type_;
+  SharedLibrary* crazy_;
+  void* system_;
+  String name_;
+  int ref_count_;
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_LIBRARY_VIEW_H
\ No newline at end of file
diff --git a/sources/android/crazy_linker/src/crazy_linker_line_reader.cpp b/sources/android/crazy_linker/src/crazy_linker_line_reader.cpp
new file mode 100644
index 0000000..5dbba0f
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_line_reader.cpp
@@ -0,0 +1,136 @@
+// 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_line_reader.h"
+
+#include "crazy_linker_debug.h"
+
+// Set to 1 to enable debug logs here.
+#define DEBUG_LINE_READER 0
+
+#define LLOG(...) LOG_IF(DEBUG_LINE_READER, __VA_ARGS__)
+
+namespace crazy {
+
+LineReader::LineReader() : fd_(), buff_(buff0_) {
+  Reset();
+  eof_ = true;
+}
+
+LineReader::LineReader(const char* path) : fd_(), buff_(buff0_) { Open(path); }
+
+LineReader::~LineReader() { Reset(); }
+
+void LineReader::Open(const char* path) {
+  Reset();
+  eof_ = !fd_.OpenReadOnly(path);
+}
+
+void LineReader::Reset() {
+  if (buff_ != buff0_)
+    ::free(buff_);
+
+  eof_ = false;
+  line_start_ = 0;
+  line_len_ = 0;
+  buff_size_ = 0;
+  buff_capacity_ = sizeof buff0_;
+  buff_ = buff0_;
+}
+
+bool LineReader::GetNextLine() {
+  // Eat previous line.
+  line_start_ += line_len_;
+  line_len_ = 0;
+
+  for (;;) {
+    LLOG("%s: LOOP line_start=%d buff_size=%d buff_capacity=%d\n",
+         __FUNCTION__,
+         line_start_,
+         buff_size_,
+         buff_capacity_);
+
+    // Find the end of the current line in the current buffer.
+    const char* line = buff_ + line_start_;
+    const char* line_end = reinterpret_cast<const char*>(
+        ::memchr(line, '\n', buff_size_ - line_start_));
+    if (line_end != NULL) {
+      // Found one, return it directly.
+      line_len_ = static_cast<size_t>(line_end + 1 - line);
+      LLOG("%s: LINE line_start=%d line_len=%d '%.*s'\n",
+           __FUNCTION__,
+           line_start_,
+           line_len_,
+           line_len_,
+           buff_ + line_start_);
+      return true;
+    }
+
+    // Eat the start of the buffer
+    if (line_start_ > 0) {
+      ::memmove(buff_, buff_ + line_start_, buff_size_ - line_start_);
+      buff_size_ -= line_start_;
+      line_start_ = 0;
+      LLOG("%s: MOVE buff_size=%d\n", __FUNCTION__, buff_size_);
+    }
+
+    // Handle end of input now.
+    if (eof_) {
+      // If there is a last line that isn't terminated by a newline, and
+      // there is room for it in the buffer. Manually add a \n and return
+      // the line.
+      if (buff_size_ > 0 && buff_size_ < buff_capacity_) {
+        buff_[buff_size_++] = '\n';
+        line_len_ = buff_size_;
+        LLOG("%s: EOF_LINE buff_size=%d '%.*s'\n",
+             __FUNCTION__,
+             buff_size_,
+             buff_size_,
+             buff_);
+        return true;
+      }
+      // Otherwise, ignore the last line.
+      LLOG("%s: EOF\n", __FUNCTION__);
+      return false;
+    }
+
+    // Before reading more data, grow the buffer if needed.
+    if (buff_size_ == buff_capacity_) {
+      size_t new_capacity = buff_capacity_ * 2;
+      void* old_buff = (buff_ == buff0_) ? NULL : buff_;
+      buff_ = static_cast<char*>(::realloc(old_buff, new_capacity));
+      if (old_buff != buff_)
+        ::memcpy(buff_, buff0_, buff_capacity_);
+
+      buff_capacity_ = new_capacity;
+      LLOG("%s: GROW buff_size=%d buff_capacity=%d '%.*s'\n",
+           __FUNCTION__,
+           buff_size_,
+           buff_capacity_,
+           buff_size_,
+           buff_);
+    }
+
+    // Try to fill the rest of buffer after current content.
+    size_t avail = buff_capacity_ - buff_size_;
+    int ret = fd_.Read(buff_ + buff_size_, avail);
+    LLOG("%s: READ buff_size=%d buff_capacity=%d avail=%d ret=%d\n",
+         __FUNCTION__,
+         buff_size_,
+         buff_capacity_,
+         avail,
+         ret);
+    if (ret <= 0) {
+      eof_ = true;
+      ret = 0;
+    }
+    buff_size_ += static_cast<size_t>(ret);
+  }
+}
+
+const char* LineReader::line() const { return buff_ + line_start_; }
+
+size_t LineReader::length() const { return line_len_; }
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_line_reader.h b/sources/android/crazy_linker/src/crazy_linker_line_reader.h
new file mode 100644
index 0000000..1f71d56
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_line_reader.h
@@ -0,0 +1,60 @@
+// 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.
+
+#ifndef CRAZY_LINKER_LINE_READER_H
+#define CRAZY_LINKER_LINE_READER_H
+
+#include <string.h>
+
+#include "crazy_linker_system.h"
+
+namespace crazy {
+
+// A class used to read text files line-by-line.
+// Usage:
+//    LineReader reader("/path/to/file");
+//    while (reader.GetNextLine()) {
+//       const char* line = reader.line();
+//       size_t line_len = reader.length();
+//       ... line is not necessarily zero-terminated.
+//    }
+
+class LineReader {
+ public:
+  LineReader();
+  explicit LineReader(const char* path);
+  ~LineReader();
+
+  // Open a new file for testing. Doesn't fail. If there was an error
+  // opening the file, GetNextLine() will simply return false.
+  void Open(const char* file_path);
+
+  // Grab next line. Returns true on success, or false otherwise.
+  bool GetNextLine();
+
+  // Return the start of the current line, this is _not_ zero-terminated
+  // and always contains a final newline (\n).
+  // Only call this after a successful GetNextLine().
+  const char* line() const;
+
+  // Return the line length, this includes the final \n.
+  // Only call this after a successful GetNextLine().
+  size_t length() const;
+
+ private:
+  void Reset();
+
+  FileDescriptor fd_;
+  bool eof_;
+  size_t line_start_;
+  size_t line_len_;
+  size_t buff_size_;
+  size_t buff_capacity_;
+  char* buff_;
+  char buff0_[128];
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_LINE_READER_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_line_reader_unittest.cpp b/sources/android/crazy_linker/src/crazy_linker_line_reader_unittest.cpp
new file mode 100644
index 0000000..eb6eb48
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_line_reader_unittest.cpp
@@ -0,0 +1,92 @@
+// 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_line_reader.h"
+
+#include <minitest/minitest.h>
+
+#include "crazy_linker_system_mock.h"
+
+namespace crazy {
+
+static const char kFilePath[] = "/tmp/foo.txt";
+
+TEST(LineReader, EmptyConstructor) {
+  LineReader reader;
+  EXPECT_FALSE(reader.GetNextLine());
+}
+
+TEST(LineReader, EmptyFile) {
+  SystemMock sys;
+  sys.AddRegularFile(kFilePath, "", 0);
+
+  LineReader reader(kFilePath);
+  EXPECT_FALSE(reader.GetNextLine());
+}
+
+TEST(LineReader, SingleLineFile) {
+  SystemMock sys;
+  static const char kFile[] = "foo bar\n";
+  static const size_t kFileSize = sizeof(kFile) - 1;
+  sys.AddRegularFile(kFilePath, kFile, kFileSize);
+
+  LineReader reader(kFilePath);
+  EXPECT_TRUE(reader.GetNextLine());
+  EXPECT_EQ(kFileSize, reader.length());
+  EXPECT_MEMEQ(kFile, kFileSize, reader.line(), reader.length());
+  EXPECT_FALSE(reader.GetNextLine());
+}
+
+TEST(LineReader, SingleLineFileUnterminated) {
+  SystemMock sys;
+  static const char kFile[] = "foo bar";
+  static const size_t kFileSize = sizeof(kFile) - 1;
+  sys.AddRegularFile(kFilePath, kFile, kFileSize);
+
+  LineReader reader(kFilePath);
+  EXPECT_TRUE(reader.GetNextLine());
+  // The LineReader will add a newline to the last line.
+  EXPECT_EQ(kFileSize + 1, reader.length());
+  EXPECT_MEMEQ(kFile, kFileSize, reader.line(), reader.length() - 1);
+  EXPECT_EQ('\n', reader.line()[reader.length() - 1]);
+  EXPECT_FALSE(reader.GetNextLine());
+}
+
+TEST(LineReader, MultiLineFile) {
+  SystemMock sys;
+  static const char kFile[] =
+      "This is a multi\n"
+      "line text file that to test the crazy::LineReader class implementation\n"
+      "And this is a very long text line to check that the class properly "
+      "handles them, through the help of dynamic allocation or something. "
+      "Yadda yadda yadda yadda. No newline";
+  static const size_t kFileSize = sizeof(kFile) - 1;
+  sys.AddRegularFile(kFilePath, kFile, kFileSize);
+
+  LineReader reader(kFilePath);
+
+  EXPECT_TRUE(reader.GetNextLine());
+  EXPECT_MEMEQ("This is a multi\n", 16, reader.line(), reader.length());
+
+  EXPECT_TRUE(reader.GetNextLine());
+  EXPECT_MEMEQ(
+      "line text file that to test the crazy::LineReader class "
+      "implementation\n",
+      88 - 17,
+      reader.line(),
+      reader.length());
+
+  EXPECT_TRUE(reader.GetNextLine());
+  EXPECT_MEMEQ(
+      "And this is a very long text line to check that the class properly "
+      "handles them, through the help of dynamic allocation or something. "
+      "Yadda yadda yadda yadda. No newline\n",
+      187 - 17,
+      reader.line(),
+      reader.length());
+
+  EXPECT_FALSE(reader.GetNextLine());
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_memory_mapping.h b/sources/android/crazy_linker/src/crazy_linker_memory_mapping.h
new file mode 100644
index 0000000..13d793d
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_memory_mapping.h
@@ -0,0 +1,91 @@
+// 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.
+
+#ifndef CRAZY_LINKER_MEMORY_MAPPING_H
+#define CRAZY_LINKER_MEMORY_MAPPING_H
+
+#include <errno.h>
+#include <sys/mman.h>
+
+#include "crazy_linker_debug.h"
+#include "crazy_linker_error.h"
+
+namespace crazy {
+
+// Helper class for a memory mapping. This is _not_ scoped.
+class MemoryMapping {
+ public:
+  enum Protection {
+    CAN_READ = PROT_READ,
+    CAN_WRITE = PROT_WRITE,
+    CAN_READ_WRITE = PROT_READ | PROT_WRITE
+  };
+  MemoryMapping() : map_(NULL), size_(0) {}
+  ~MemoryMapping() {}
+
+  // Return current mapping address.
+  void* Get() { return map_; }
+  size_t GetSize() const { return size_; }
+
+  // Allocate a new mapping.
+  // |address| is either NULL or a fixed memory address.
+  // |size| is the page-aligned size, must be > 0.
+  // |prot| are the desired protection bit flags.
+  // |fd| is -1 (for anonymous mappings), or a valid file descriptor.
+  // on failure, return false and sets errno.
+  bool Allocate(void* address,
+                size_t size,
+                Protection prot,
+                int fd) {
+    int flags = (fd >= 0) ? MAP_SHARED : MAP_ANONYMOUS;
+    if (address)
+      flags |= MAP_FIXED;
+
+    size_ = size;
+    map_ = ::mmap(address, size_, static_cast<int>(prot), flags, fd, 0);
+    if (map_ == MAP_FAILED) {
+      map_ = NULL;
+      return false;
+    }
+
+    return true;
+  }
+
+  // Change the protection flags of the mapping.
+  // On failure, return false and sets errno.
+  bool SetProtection(Protection prot) {
+    if (!map_ || ::mprotect(map_, size_, static_cast<int>(prot)) < 0)
+      return false;
+    return true;
+  }
+
+  // Deallocate an existing mapping, if any.
+  void Deallocate() {
+    if (map_) {
+      ::munmap(map_, size_);
+      map_ = NULL;
+    }
+  }
+
+ protected:
+  void* map_;
+  size_t size_;
+};
+
+// Helper class for a memory mapping that is automatically
+// unmapped on scope exit, unless its Release() method is called.
+class ScopedMemoryMapping : public MemoryMapping {
+ public:
+  void* Release() {
+    void* ret = map_;
+    map_ = NULL;
+    return ret;
+  }
+
+  ~ScopedMemoryMapping() { Deallocate(); }
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_MEMORY_MAPPING_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_proc_maps.cpp b/sources/android/crazy_linker/src/crazy_linker_proc_maps.cpp
new file mode 100644
index 0000000..d089a90
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_proc_maps.cpp
@@ -0,0 +1,295 @@
+// 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_proc_maps.h"
+
+#include <inttypes.h>
+#include <limits.h>
+
+#include "elf_traits.h"
+#include "crazy_linker_debug.h"
+#include "crazy_linker_line_reader.h"
+#include "crazy_linker_util.h"
+#include "crazy_linker_system.h"
+
+namespace crazy {
+
+namespace {
+
+// Decompose the components of a /proc/$PID/maps file into multiple
+// components. |line| should be the address of a zero-terminated line
+// of input. On success, returns true and sets |*entry|, false otherwise.
+//
+// IMPORTANT: On success, |entry->path| will point into the input line,
+// the caller will have to copy the string into a different location if
+// it needs to persist it.
+bool ParseProcMapsLine(const char* line,
+                       const char* line_end,
+                       ProcMaps::Entry* entry) {
+  // Example input lines on a 64-bit system, one cannot assume that
+  // everything is properly sized.
+  //
+  // 00400000-0040b000 r-xp 00000000 08:01 6570708
+  // /bin/cat
+  // 0060a000-0060b000 r--p 0000a000 08:01 6570708
+  // /bin/cat
+  // 0060b000-0060c000 rw-p 0000b000 08:01 6570708
+  // /bin/cat
+  // 01dd0000-01df1000 rw-p 00000000 00:00 0
+  // [heap]
+  // 7f4b8d4d7000-7f4b8e22a000 r--p 00000000 08:01 38666648
+  // /usr/lib/locale/locale-archive
+  // 7f4b8e22a000-7f4b8e3df000 r-xp 00000000 08:01 28836281
+  // /lib/x86_64-linux-gnu/libc-2.15.so
+  // 7f4b8e3df000-7f4b8e5de000 ---p 001b5000 08:01 28836281
+  // /lib/x86_64-linux-gnu/libc-2.15.so
+  // 7f4b8e5de000-7f4b8e5e2000 r--p 001b4000 08:01 28836281
+  // /lib/x86_64-linux-gnu/libc-2.15.so
+  // 7f4b8e5e2000-7f4b8e5e4000 rw-p 001b8000 08:01 28836281
+  // /lib/x86_64-linux-gnu/libc-2.15.so
+  const char* p = line;
+  for (int token = 0; token < 7; ++token) {
+    char separator = (token == 0) ? '-' : ' ';
+    // skip leading token separators first.
+    while (p < line_end && *p == separator)
+      p++;
+
+    // find start and end of current token, and compute start of
+    // next search.
+    const char* tok_start = p;
+    const char* tok_end =
+        static_cast<const char*>(memchr(p, separator, line_end - p));
+    if (!tok_end) {
+      tok_end = line_end;
+      p = line_end;
+    } else {
+      p = tok_end + 1;
+    }
+
+    if (tok_end == tok_start) {
+      if (token == 6) {
+        // empty token can happen for index 6, when there is no path
+        // element on the line. This corresponds to anonymous memory
+        // mapped segments.
+        entry->path = NULL;
+        entry->path_len = 0;
+        break;
+      }
+      return false;
+    }
+
+    switch (token) {
+      case 0:  // vma_start
+        entry->vma_start = static_cast<size_t>(strtoumax(tok_start, NULL, 16));
+        break;
+
+      case 1:  // vma_end
+        entry->vma_end = static_cast<size_t>(strtoumax(tok_start, NULL, 16));
+        break;
+
+      case 2:  // protection bits
+               {
+        int flags = 0;
+        for (const char* t = tok_start; t < tok_end; ++t) {
+          if (*t == 'r')
+            flags |= PROT_READ;
+          if (*t == 'w')
+            flags |= PROT_WRITE;
+          if (*t == 'x')
+            flags |= PROT_EXEC;
+        }
+        entry->prot_flags = flags;
+      } break;
+
+      case 3:  // page offset
+        entry->load_offset =
+            static_cast<size_t>(strtoumax(tok_start, NULL, 16)) * PAGE_SIZE;
+        break;
+
+      case 6:  // path
+        // Get rid of trailing newlines, if any.
+        while (tok_end > tok_start && tok_end[-1] == '\n')
+          tok_end--;
+        entry->path = tok_start;
+        entry->path_len = tok_end - tok_start;
+        break;
+
+      default:  // ignore all other tokens.
+        ;
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+// Internal implementation of ProcMaps class.
+class ProcMapsInternal {
+ public:
+  ProcMapsInternal() : index_(0), entries_() {}
+
+  ~ProcMapsInternal() { Reset(); }
+
+  bool Open(const char* path) {
+    Reset();
+    LineReader reader(path);
+    index_ = 0;
+    while (reader.GetNextLine()) {
+      ProcMaps::Entry entry = {0, };
+      if (!ParseProcMapsLine(
+               reader.line(), reader.line() + reader.length(), &entry)) {
+        // Ignore broken lines.
+        continue;
+      }
+
+      // Reallocate path.
+      const char* old_path = entry.path;
+      if (old_path) {
+        char* new_path = static_cast<char*>(::malloc(entry.path_len + 1));
+        ::memcpy(new_path, old_path, entry.path_len);
+        new_path[entry.path_len] = '\0';
+        entry.path = const_cast<const char*>(new_path);
+      }
+
+      entries_.PushBack(entry);
+    }
+    return true;
+  }
+
+  void Rewind() { index_ = 0; }
+
+  bool GetNextEntry(ProcMaps::Entry* entry) {
+    if (index_ >= entries_.GetCount())
+      return false;
+
+    *entry = entries_[index_++];
+    return true;
+  }
+
+ private:
+  void Reset() {
+    for (size_t n = 0; n < entries_.GetCount(); ++n) {
+      ProcMaps::Entry& entry = entries_[n];
+      ::free(const_cast<char*>(entry.path));
+    }
+    entries_.Resize(0);
+  }
+
+  size_t index_;
+  Vector<ProcMaps::Entry> entries_;
+};
+
+ProcMaps::ProcMaps() {
+  internal_ = new ProcMapsInternal();
+  (void)internal_->Open("/proc/self/maps");
+}
+
+ProcMaps::ProcMaps(pid_t pid) {
+  internal_ = new ProcMapsInternal();
+  char maps_file[32];
+  snprintf(maps_file, sizeof maps_file, "/proc/%u/maps", pid);
+  (void)internal_->Open(maps_file);
+}
+
+ProcMaps::~ProcMaps() { delete internal_; }
+
+void ProcMaps::Rewind() { internal_->Rewind(); }
+
+bool ProcMaps::GetNextEntry(Entry* entry) {
+  return internal_->GetNextEntry(entry);
+}
+
+int ProcMaps::GetProtectionFlagsForAddress(void* address) {
+  size_t vma_addr = reinterpret_cast<size_t>(address);
+  internal_->Rewind();
+  ProcMaps::Entry entry;
+  while (internal_->GetNextEntry(&entry)) {
+    if (entry.vma_start <= vma_addr && vma_addr < entry.vma_end)
+      return entry.prot_flags;
+  }
+  return 0;
+}
+
+bool FindElfBinaryForAddress(void* address,
+                             uintptr_t* load_address,
+                             char* path_buffer,
+                             size_t path_buffer_len) {
+  ProcMaps self_maps;
+  ProcMaps::Entry entry;
+
+  uintptr_t addr = reinterpret_cast<uintptr_t>(address);
+
+  while (self_maps.GetNextEntry(&entry)) {
+    if (entry.vma_start <= addr && addr < entry.vma_end) {
+      *load_address = entry.vma_start;
+      if (!entry.path) {
+        LOG("Could not find ELF binary path!?\n");
+        return false;
+      }
+      if (entry.path_len >= path_buffer_len) {
+        LOG("ELF binary path too long: '%s'\n", entry.path);
+        return false;
+      }
+      memcpy(path_buffer, entry.path, entry.path_len);
+      path_buffer[entry.path_len] = '\0';
+      return true;
+    }
+  }
+  return false;
+}
+
+// Returns the current protection bit flags for the page holding a given
+// address. Returns true on success, or false if the address is not mapped.
+bool FindProtectionFlagsForAddress(void* address, int* prot_flags) {
+  ProcMaps self_maps;
+  ProcMaps::Entry entry;
+
+  uintptr_t addr = reinterpret_cast<uintptr_t>(address);
+
+  while (self_maps.GetNextEntry(&entry)) {
+    if (entry.vma_start <= addr && addr < entry.vma_end) {
+      *prot_flags = entry.prot_flags;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool FindLoadAddressForFile(const char* file_name,
+                            uintptr_t* load_address,
+                            uintptr_t* load_offset) {
+  size_t file_name_len = strlen(file_name);
+  bool is_base_name = (strchr(file_name, '/') == NULL);
+  ProcMaps self_maps;
+  ProcMaps::Entry entry;
+
+  while (self_maps.GetNextEntry(&entry)) {
+    // Skip vDSO et al.
+    if (entry.path_len == 0 || entry.path[0] == '[')
+      continue;
+
+    const char* entry_name = entry.path;
+    size_t entry_len = entry.path_len;
+
+    if (is_base_name) {
+      const char* p = reinterpret_cast<const char*>(
+          ::memrchr(entry.path, '/', entry.path_len));
+      if (p) {
+        entry_name = p + 1;
+        entry_len = entry.path_len - (p - entry.path) - 1;
+      }
+    }
+
+    if (file_name_len == entry_len &&
+        !memcmp(file_name, entry_name, entry_len)) {
+      *load_address = entry.vma_start;
+      *load_offset = entry.load_offset;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_proc_maps.h b/sources/android/crazy_linker/src/crazy_linker_proc_maps.h
new file mode 100644
index 0000000..53848d9
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_proc_maps.h
@@ -0,0 +1,80 @@
+// 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.
+
+#ifndef CRAZY_LINKER_PROC_MAPS_H
+#define CRAZY_LINKER_PROC_MAPS_H
+
+// Helper classes and functions to extract useful information out of
+// /proc/self/maps.
+
+#include <stdint.h>
+#include <sys/mman.h>  // for PROT_READ etc...
+
+namespace crazy {
+
+class ProcMapsInternal;
+
+class ProcMaps {
+ public:
+  // Used to open /proc/self/maps.
+  // There is no error reporting. If the file can't be opened, then
+  // GetNextEntry() will return false on the first call.
+  ProcMaps();
+
+  // Used to open /proc/$PID/maps.
+  // There is also no error reporting.
+  explicit ProcMaps(pid_t pid);
+
+  ~ProcMaps();
+
+  // Small structure to model an entry.
+  struct Entry {
+    size_t vma_start;
+    size_t vma_end;
+    int prot_flags;
+    size_t load_offset;
+    const char* path;  // can be NULL, not always zero-terminated.
+    size_t path_len;   // 0 if |path| is NULL.
+  };
+
+  void Rewind();
+
+  // Get next entry in maps, return NULL on failure.
+  // On success, return true and set |entry| to valid values.
+  // Note that the |entry->path| field can be NULL or will point to
+  // an address which content may change on the next call to this method.
+  bool GetNextEntry(Entry* entry);
+
+  int GetProtectionFlagsForAddress(void* address);
+
+ private:
+  ProcMapsInternal* internal_;
+};
+
+// Find which loaded ELF binary contains |address|.
+// On success, returns true and sets |*load_address| to its load address,
+// and fills |path_buffer| with the path to the corresponding file.
+bool FindElfBinaryForAddress(void* address,
+                             uintptr_t* load_address,
+                             char* path_buffer,
+                             size_t path_buffer_len);
+
+// Returns the current protection bit flags for the page holding a given
+// |address|. On success, returns true and sets |*prot_flags|.
+bool FindProtectionFlagsForAddress(void* address, int* prot_flags);
+
+// Return the load address of a given ELF binary.
+// If |file_name| contains a slash, this will try to perform an
+// exact match with the content of /proc/self/maps. Otherwise,
+// it will be taken as a base name, and the load address of the first
+// matching entry will be returned.
+// On success, returns true and sets |*load_address| to the load address,
+// and |*load_offset| to the load offset.
+bool FindLoadAddressForFile(const char* file_name,
+                            uintptr_t* load_address,
+                            uintptr_t* load_offset);
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_PROC_MAPS_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_proc_maps_unittest.cpp b/sources/android/crazy_linker/src/crazy_linker_proc_maps_unittest.cpp
new file mode 100644
index 0000000..7ce8697
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_proc_maps_unittest.cpp
@@ -0,0 +1,214 @@
+// 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_proc_maps.h"
+
+#include <minitest/minitest.h>
+#include "crazy_linker_system_mock.h"
+
+namespace crazy {
+
+namespace {
+
+const char kProcMaps0[] =
+    "4000b000-4000c000 r--p 00000000 00:00 0\n"
+    "4005c000-40081000 r-xp 00000000 b3:01 141        /system/bin/mksh\n"
+    "40082000-40083000 r--p 00025000 b3:01 141        /system/bin/mksh\n"
+    "40083000-40084000 rw-p 00026000 b3:01 141        /system/bin/mksh\n"
+    "40084000-40088000 rw-p 00000000 00:00 0\n"
+    "40088000-40090000 r--s 00000000 00:0b 1704       /dev/__properties__\n"
+    "400eb000-400ec000 r--p 00000000 00:00 0\n"
+    "40141000-40150000 r-xp 00000000 b3:01 126        /system/bin/linker\n"
+    "40150000-40151000 r--p 0000e000 b3:01 126        /system/bin/linker\n"
+    "40151000-40152000 rw-p 0000f000 b3:01 126        /system/bin/linker\n"
+    "40152000-40153000 rw-p 00000000 00:00 0\n"
+    "40231000-40277000 r-xp 00001000 b3:01 638        /system/lib/libc.so\n"
+    "40277000-40279000 r--p 00046000 b3:01 638        /system/lib/libc.so\n"
+    "40279000-4027b000 rw-p 00048000 b3:01 638        /system/lib/libc.so\n"
+    "4027b000-40289000 rw-p 00000000 00:00 0\n"
+    "41e6b000-41e72000 rw-p 00000000 00:00 0          [heap]\n"
+    "be91b000-be93c000 rw-p 00000000 00:00 0          [stack]\n"
+    "ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]\n";
+
+class ScopedTestEnv {
+ public:
+  ScopedTestEnv() : sys_() {
+    sys_.AddRegularFile("/proc/self/maps", kProcMaps0, sizeof(kProcMaps0) - 1);
+  }
+
+  ~ScopedTestEnv() {}
+
+ private:
+  SystemMock sys_;
+};
+
+}  // namespace
+
+TEST(ProcMaps, FindElfBinaryForAddress) {
+  ScopedTestEnv env;
+  char path[512];
+  uintptr_t load_address;
+
+  EXPECT_TRUE(FindElfBinaryForAddress(
+      reinterpret_cast<void*>(0x400694c2), &load_address, path, sizeof(path)));
+  EXPECT_EQ(0x4005c000, load_address);
+  EXPECT_STREQ("/system/bin/mksh", path);
+}
+
+TEST(ProcMaps, FindElfBinaryForAddressWithBadAddress) {
+  ScopedTestEnv env;
+  char path[512];
+  uintptr_t load_address;
+
+  EXPECT_FALSE(FindElfBinaryForAddress(
+      reinterpret_cast<void*>(0x50000000), &load_address, path, sizeof(path)));
+}
+
+TEST(ProcMaps, FindProtectionFlagsForAddress) {
+  ScopedTestEnv env;
+  static const struct {
+    uintptr_t address;
+    bool success;
+    int prot;
+  } kData[] = {{0x4000afff, false, 0},
+               {0x4000b000, true, PROT_READ},
+               {0x4000bfff, true, PROT_READ},
+               {0x4000c000, false, 0},
+               {0x4005bfff, false, 0},
+               {0x4005c000, true, PROT_READ | PROT_EXEC},
+               {0x40067832, true, PROT_READ | PROT_EXEC},
+               {0x40082000, true, PROT_READ},
+               {0x40083000, true, PROT_READ | PROT_WRITE},
+               {0x40084000, true, PROT_READ | PROT_WRITE}, };
+
+  int prot;
+  for (size_t n = 0; n < ARRAY_LEN(kData); ++n) {
+    void* address = reinterpret_cast<void*>(kData[n].address);
+    TEST_TEXT << minitest::Format("Checking address %p", address);
+    EXPECT_EQ(kData[n].success, FindProtectionFlagsForAddress(address, &prot));
+    if (kData[n].success) {
+      TEST_TEXT << minitest::Format("Checking address %p", address);
+      EXPECT_EQ(kData[n].prot, prot);
+    }
+  }
+}
+
+TEST(ProcMaps, FindLoadAddressForFile) {
+  ScopedTestEnv env;
+  static const struct {
+    bool success;
+    uintptr_t address;
+    uintptr_t offset;
+    const char* name;
+  } kData[] = {{true, 0x4005c000, 0, "mksh"},
+               {true, 0x40141000, 0, "/system/bin/linker"},
+               {false, 0, 0, "[heap]"},
+               {false, 0, 0, "bin/mksh"},
+               {true, 0x4005c000, 0, "/system/bin/mksh"},
+               {true, 0x40231000, 0x1000000, "libc.so"}, };
+  for (size_t n = 0; n < ARRAY_LEN(kData); ++n) {
+    uintptr_t address, offset;
+    TEST_TEXT << "Checking " << kData[n].name;
+    bool success = FindLoadAddressForFile(kData[n].name, &address, &offset);
+    EXPECT_EQ(kData[n].success, success);
+    if (success) {
+      TEST_TEXT << "Checking " << kData[n].name;
+      EXPECT_EQ(kData[n].address, address);
+
+      TEST_TEXT << "Checking " << kData[n].name;
+      EXPECT_EQ(kData[n].offset, offset);
+    }
+  }
+}
+
+TEST(ProcMaps, GetNextEntry) {
+  ScopedTestEnv env;
+  //     "4000b000-4000c000 r--p 00000000 00:00 0\n"
+  //     "4005c000-40081000 r-xp 00000000 b3:01 141        /system/bin/mksh\n"
+  //     "40082000-40083000 r--p 00025000 b3:01 141        /system/bin/mksh\n"
+  //     "40083000-40084000 rw-p 00026000 b3:01 141        /system/bin/mksh\n"
+  //     "40084000-40088000 rw-p 00000000 00:00 0\n"
+  //     "40088000-40090000 r--s 00000000 00:0b 1704
+  // /dev/__properties__\n"
+  //     "400eb000-400ec000 r--p 00000000 00:00 0\n"
+  //     "40141000-40150000 r-xp 00000000 b3:01 126        /system/bin/linker\n"
+  //     "40150000-40151000 r--p 0000e000 b3:01 126        /system/bin/linker\n"
+  //     "40151000-40152000 rw-p 0000f000 b3:01 126        /system/bin/linker\n"
+  //     "40152000-40153000 rw-p 00000000 00:00 0\n"
+  //     "40231000-40277000 r-xp 00001000 b3:01 638
+  // /system/lib/libc.so\n"
+  //     "40277000-40279000 r--p 00046000 b3:01 638
+  // /system/lib/libc.so\n"
+  //     "40279000-4027b000 rw-p 00048000 b3:01 638
+  // /system/lib/libc.so\n"
+  //     "4027b000-40289000 rw-p 00000000 00:00 0\n"
+  //     "41e6b000-41e72000 rw-p 00000000 00:00 0          [heap]\n"
+  //     "be91b000-be93c000 rw-p 00000000 00:00 0          [stack]\n"
+  //     "ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]\n"
+  static const struct {
+    size_t vma_start;
+    size_t vma_end;
+    int prot_flags;
+    size_t load_offset;
+    const char* path;
+  } kData[] = {
+        {0x4000b000, 0x4000c000, PROT_READ, 0, NULL},
+        {0x4005c000, 0x40081000, PROT_READ | PROT_EXEC, 0, "/system/bin/mksh"},
+        {0x40082000,          0x40083000,        PROT_READ,
+         0x25000 * PAGE_SIZE, "/system/bin/mksh"},
+        {0x40083000,          0x40084000,        PROT_READ | PROT_WRITE,
+         0x26000 * PAGE_SIZE, "/system/bin/mksh"},
+        {0x40084000, 0x40088000, PROT_READ | PROT_WRITE, 0, NULL},
+        {0x40088000, 0x40090000, PROT_READ, 0, "/dev/__properties__"},
+        {0x400eb000, 0x400ec000, PROT_READ, 0, NULL},
+        {0x40141000, 0x40150000,          PROT_READ | PROT_EXEC,
+         0,          "/system/bin/linker"},
+        {0x40150000,         0x40151000,          PROT_READ,
+         0xe000 * PAGE_SIZE, "/system/bin/linker"},
+        {0x40151000,         0x40152000,          PROT_READ | PROT_WRITE,
+         0xf000 * PAGE_SIZE, "/system/bin/linker"},
+        {0x40152000, 0x40153000, PROT_READ | PROT_WRITE, 0, NULL},
+        {0x40231000,         0x40277000,           PROT_READ | PROT_EXEC,
+         0x1000 * PAGE_SIZE, "/system/lib/libc.so"},
+        {0x40277000,          0x40279000,           PROT_READ,
+         0x46000 * PAGE_SIZE, "/system/lib/libc.so"},
+        {0x40279000,          0x4027b000,           PROT_READ | PROT_WRITE,
+         0x48000 * PAGE_SIZE, "/system/lib/libc.so"},
+        {0x4027b000, 0x40289000, PROT_READ | PROT_WRITE, 0, NULL},
+        {0x41e6b000, 0x41e72000, PROT_READ | PROT_WRITE, 0, "[heap]"},
+        {0xbe91b000, 0xbe93c000, PROT_READ | PROT_WRITE, 0, "[stack]"},
+        {0xffff0000, 0xffff1000, PROT_READ | PROT_EXEC, 0, "[vectors]"}, };
+
+  ProcMaps self_maps;
+  ProcMaps::Entry entry;
+
+  for (size_t n = 0; n < ARRAY_LEN(kData); ++n) {
+    minitest::internal::String text =
+        minitest::Format("Checking entry #%d %p-%p",
+                         n + 1,
+                         kData[n].vma_start,
+                         kData[n].vma_end);
+    TEST_TEXT << text;
+    EXPECT_TRUE(self_maps.GetNextEntry(&entry));
+    TEST_TEXT << text;
+    EXPECT_EQ(kData[n].vma_start, entry.vma_start);
+    TEST_TEXT << text;
+    EXPECT_EQ(kData[n].vma_end, entry.vma_end);
+    TEST_TEXT << text;
+    EXPECT_EQ(kData[n].prot_flags, entry.prot_flags);
+    TEST_TEXT << text;
+    EXPECT_EQ(kData[n].load_offset, entry.load_offset);
+
+    if (!kData[n].path) {
+      TEST_TEXT << text;
+      EXPECT_FALSE(entry.path);
+    } else {
+      EXPECT_MEMEQ(
+          kData[n].path, strlen(kData[n].path), entry.path, entry.path_len);
+    }
+  }
+  EXPECT_FALSE(self_maps.GetNextEntry(&entry));
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_rdebug.cpp b/sources/android/crazy_linker/src/crazy_linker_rdebug.cpp
new file mode 100644
index 0000000..1461bc9
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_rdebug.cpp
@@ -0,0 +1,370 @@
+// 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_rdebug.h"
+
+#include <elf.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "crazy_linker_debug.h"
+#include "crazy_linker_proc_maps.h"
+#include "crazy_linker_util.h"
+#include "crazy_linker_system.h"
+#include "elf_traits.h"
+
+namespace crazy {
+
+namespace {
+
+// Find the full path of the current executable. On success return true
+// and sets |exe_path|. On failure, return false and sets errno.
+bool FindExecutablePath(String* exe_path) {
+  // /proc/self/exe is a symlink to the full path. Read it with
+  // readlink().
+  exe_path->Resize(512);
+  ssize_t ret = TEMP_FAILURE_RETRY(
+      readlink("/proc/self/exe", exe_path->ptr(), exe_path->size()));
+  if (ret < 0) {
+    LOG_ERRNO("%s: Could not get /proc/self/exe link", __FUNCTION__);
+    return false;
+  }
+
+  exe_path->Resize(static_cast<size_t>(ret));
+  LOG("%s: Current executable: %s\n", __FUNCTION__, exe_path->c_str());
+  return true;
+}
+
+// Given an ELF binary at |path| that is _already_ mapped in the process,
+// find the address of its dynamic section and its size.
+// |path| is the full path of the binary (as it appears in /proc/self/maps.
+// |self_maps| is an instance of ProcMaps that is used to inspect
+// /proc/self/maps. The function rewind + iterates over it.
+// On success, return true and set |*dynamic_offset| and |*dynamic_size|.
+bool FindElfDynamicSection(const char* path,
+                           ProcMaps* self_maps,
+                           size_t* dynamic_address,
+                           size_t* dynamic_size) {
+  // Read the ELF header first.
+  ELF::Ehdr header[1];
+
+  crazy::FileDescriptor fd;
+  if (!fd.OpenReadOnly(path) ||
+      fd.Read(header, sizeof(header)) != static_cast<int>(sizeof(header))) {
+    LOG_ERRNO("%s: Could not load ELF binary header", __FUNCTION__);
+    return false;
+  }
+
+  // Sanity check.
+  if (header->e_ident[0] != 127 || header->e_ident[1] != 'E' ||
+      header->e_ident[2] != 'L' || header->e_ident[3] != 'F' ||
+      header->e_ident[4] != 1) {
+    LOG("%s: Not a 32-bit ELF binary: %s\n", __FUNCTION__, path);
+    return false;
+  }
+
+  if (header->e_phoff == 0 || header->e_phentsize != sizeof(ELF::Phdr)) {
+    LOG("%s: Invalid program header values: %s\n", __FUNCTION__, path);
+    return false;
+  }
+
+  // Scan the program header table.
+  if (fd.SeekTo(header->e_phoff) < 0) {
+    LOG_ERRNO("%s: Could not find ELF program header table", __FUNCTION__);
+    return false;
+  }
+
+  ELF::Phdr phdr_load0 = {0, };
+  ELF::Phdr phdr_dyn = {0, };
+  bool found_load0 = false;
+  bool found_dyn = false;
+
+  for (size_t n = 0; n < header->e_phnum; ++n) {
+    ELF::Phdr phdr;
+    if (fd.Read(&phdr, sizeof(phdr)) != sizeof(phdr)) {
+      LOG_ERRNO("%s: Could not read program header entry", __FUNCTION__);
+      return false;
+    }
+
+    if (phdr.p_type == PT_LOAD && !found_load0) {
+      phdr_load0 = phdr;
+      found_load0 = true;
+    } else if (phdr.p_type == PT_DYNAMIC && !found_dyn) {
+      phdr_dyn = phdr;
+      found_dyn = true;
+    }
+  }
+
+  if (!found_load0) {
+    LOG("%s: Could not find loadable segment!?", __FUNCTION__);
+    return false;
+  }
+  if (!found_dyn) {
+    LOG("%s: Could not find dynamic segment!?", __FUNCTION__);
+    return false;
+  }
+
+  LOG("%s: Found first loadable segment [offset=%p vaddr=%p]\n",
+      __FUNCTION__,
+      (void*)phdr_load0.p_offset,
+      (void*)phdr_load0.p_vaddr);
+
+  LOG("%s: Found dynamic segment [offset=%p vaddr=%p size=%p]\n",
+      __FUNCTION__,
+      (void*)phdr_dyn.p_offset,
+      (void*)phdr_dyn.p_vaddr,
+      (void*)phdr_dyn.p_memsz);
+
+  // Parse /proc/self/maps to find the load address of the first
+  // loadable segment.
+  size_t path_len = strlen(path);
+  self_maps->Rewind();
+  ProcMaps::Entry entry;
+  while (self_maps->GetNextEntry(&entry)) {
+    if (!entry.path || entry.path_len != path_len ||
+        memcmp(entry.path, path, path_len) != 0)
+      continue;
+
+    LOG("%s: Found executable segment mapped [%p-%p offset=%p]\n",
+        __FUNCTION__,
+        (void*)entry.vma_start,
+        (void*)entry.vma_end,
+        (void*)entry.load_offset);
+
+    size_t load_bias = entry.vma_start - phdr_load0.p_vaddr;
+    LOG("%s: Load bias is %p\n", __FUNCTION__, (void*)load_bias);
+
+    *dynamic_address = load_bias + phdr_dyn.p_vaddr;
+    *dynamic_size = phdr_dyn.p_memsz;
+    LOG("%s: Dynamic section addr=%p size=%p\n",
+        __FUNCTION__,
+        (void*)*dynamic_address,
+        (void*)*dynamic_size);
+    return true;
+  }
+
+  LOG("%s: Executable is not mapped in current process.\n", __FUNCTION__);
+  return false;
+}
+
+// Helper class to temporarily remap a page to readable+writable until
+// scope exit.
+class ScopedPageMapper {
+ public:
+  ScopedPageMapper() : page_address_(0), page_prot_(0) {}
+  void MapReadWrite(void* address);
+  ~ScopedPageMapper();
+
+ private:
+  static const uintptr_t kPageSize = 4096;
+  uintptr_t page_address_;
+  int page_prot_;
+};
+
+void ScopedPageMapper::MapReadWrite(void* address) {
+  page_address_ = reinterpret_cast<uintptr_t>(address) & ~(kPageSize - 1);
+  page_prot_ = 0;
+  if (!FindProtectionFlagsForAddress(address, &page_prot_) ||
+      (page_prot_ & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) {
+    // If the address is invalid, or if the page is already read+write,
+    // no need to do anything here.
+    page_address_ = 0;
+    return;
+  }
+  int new_page_prot = page_prot_ | PROT_READ | PROT_WRITE;
+  int ret = mprotect(
+      reinterpret_cast<void*>(page_address_), kPageSize, new_page_prot);
+  if (ret < 0) {
+    LOG_ERRNO("Could not remap page to read/write");
+    page_address_ = 0;
+  }
+}
+
+ScopedPageMapper::~ScopedPageMapper() {
+  if (page_address_) {
+    int ret =
+        mprotect(reinterpret_cast<void*>(page_address_), kPageSize, page_prot_);
+    if (ret < 0)
+      LOG_ERRNO("Could not remap page to old protection flags");
+  }
+}
+
+}  // namespace
+
+bool RDebug::Init() {
+  // The address of '_r_debug' is in the DT_DEBUG entry of the current
+  // executable.
+  init_ = true;
+
+  size_t dynamic_addr = 0;
+  size_t dynamic_size = 0;
+  String path;
+
+  // Find the current executable's full path, and its dynamic section
+  // information.
+  if (!FindExecutablePath(&path))
+    return false;
+
+  ProcMaps self_maps;
+  if (!FindElfDynamicSection(
+           path.c_str(), &self_maps, &dynamic_addr, &dynamic_size)) {
+    return false;
+  }
+
+  // Parse the dynamic table and find the DT_DEBUG entry.
+  const ELF::Dyn* dyn_section = reinterpret_cast<const ELF::Dyn*>(dynamic_addr);
+
+  while (dynamic_size >= sizeof(*dyn_section)) {
+    if (dyn_section->d_tag == DT_DEBUG) {
+      // Found it!
+      LOG("%s: Found DT_DEBUG entry inside %s at %p, pointing to %p\n",
+          __FUNCTION__,
+          path.c_str(),
+          dyn_section,
+          dyn_section->d_un.d_ptr);
+      if (dyn_section->d_un.d_ptr) {
+        r_debug_ = reinterpret_cast<r_debug*>(dyn_section->d_un.d_ptr);
+        LOG("%s: r_debug [r_version=%d r_map=%p r_brk=%p r_ldbase=%p]\n",
+            __FUNCTION__,
+            r_debug_->r_version,
+            r_debug_->r_map,
+            r_debug_->r_brk,
+            r_debug_->r_ldbase);
+        // Only version 1 of the struct is supported.
+        if (r_debug_->r_version != 1) {
+          LOG("%s: r_debug.r_version is %d, 1 expected.\n",
+              __FUNCTION__,
+              r_debug_->r_version);
+          r_debug_ = NULL;
+        }
+
+        // The linker of recent Android releases maps its link map entries
+        // in read-only pages. Determine if this is the case and record it
+        // for later. The first entry in the list corresponds to the
+        // executable.
+        int prot = self_maps.GetProtectionFlagsForAddress(r_debug_->r_map);
+        readonly_entries_ = (prot & PROT_WRITE) == 0;
+
+        LOG("%s: r_debug.readonly_entries=%s\n",
+            __FUNCTION__,
+            readonly_entries_ ? "true" : "false");
+        return true;
+      }
+    }
+    dyn_section++;
+    dynamic_size -= sizeof(*dyn_section);
+  }
+
+  LOG("%s: There is no non-0 DT_DEBUG entry in this process\n", __FUNCTION__);
+  return false;
+}
+
+void RDebug::AddEntry(link_map_t* entry) {
+  LOG("%s\n", __FUNCTION__);
+  if (!init_)
+    Init();
+
+  if (!r_debug_) {
+    LOG("%s: Nothing to do\n", __FUNCTION__);
+    return;
+  }
+
+  // Tell GDB the list is going to be modified.
+  r_debug_->r_state = RT_ADD;
+  r_debug_->r_brk();
+
+  // IMPORTANT: GDB expects the first entry in the list to correspond
+  // to the executable. So add our new entry just after it. This is ok
+  // because by default, the linker is always the second entry, as in:
+  //
+  //   [<executable>, /system/bin/linker, libc.so, libm.so, ...]
+  //
+  // By design, the first two entries should never be removed since they
+  // can't be unloaded from the process (they are loaded by the kernel
+  // when invoking the program).
+  //
+  // TODO(digit): Does GDB expect the linker to be the second entry?
+  // It doesn't seem so, but have a look at the GDB sources to confirm
+  // this. No problem appear experimentally.
+  //
+  // What happens for static binaries? They don't have an .interp section,
+  // and don't have a r_debug variable on Android, so GDB should not be
+  // able to debug shared libraries at all for them (assuming one
+  // statically links a linker into the executable).
+  if (!r_debug_->r_map || !r_debug_->r_map->l_next ||
+      !r_debug_->r_map->l_next->l_next) {
+    // Sanity check: Must have at least two items in the list.
+    LOG("%s: Malformed r_debug.r_map list\n", __FUNCTION__);
+    r_debug_ = NULL;
+    return;
+  }
+
+  link_map_t* before = r_debug_->r_map->l_next;
+  link_map_t* after = before->l_next;
+
+  // Prepare the new entry.
+  entry->l_prev = before;
+  entry->l_next = after;
+
+  // IMPORTANT: Before modifying the previous and next entries in the
+  // list, ensure that they are writable. This avoids crashing when
+  // updating the 'l_prev' or 'l_next' fields of a system linker entry,
+  // which are mapped read-only.
+  {
+    ScopedPageMapper mapper;
+    if (readonly_entries_)
+      mapper.MapReadWrite(before);
+    before->l_next = entry;
+  }
+
+  {
+    ScopedPageMapper mapper;
+    if (readonly_entries_)
+      mapper.MapReadWrite(after);
+    after->l_prev = entry;
+  }
+
+  // Tell GDB that the list modification has completed.
+  r_debug_->r_state = RT_CONSISTENT;
+  r_debug_->r_brk();
+}
+
+void RDebug::DelEntry(link_map_t* entry) {
+  if (!r_debug_)
+    return;
+
+  // Tell GDB the list is going to be modified.
+  r_debug_->r_state = RT_DELETE;
+  r_debug_->r_brk();
+
+  // IMPORTANT: Before modifying the previous and next entries in the
+  // list, ensure that they are writable. See comment above for more
+  // details.
+  if (entry->l_prev) {
+    ScopedPageMapper mapper;
+    if (readonly_entries_)
+      mapper.MapReadWrite(entry->l_prev);
+    entry->l_prev->l_next = entry->l_next;
+  }
+
+  if (entry->l_next) {
+    ScopedPageMapper mapper;
+    if (readonly_entries_)
+      mapper.MapReadWrite(entry->l_next);
+    entry->l_next->l_prev = entry->l_prev;
+  }
+
+  if (r_debug_->r_map == entry)
+    r_debug_->r_map = entry->l_next;
+
+  entry->l_prev = NULL;
+  entry->l_next = NULL;
+
+  // Tell GDB the list modification has completed.
+  r_debug_->r_state = RT_CONSISTENT;
+  r_debug_->r_brk();
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_rdebug.h b/sources/android/crazy_linker/src/crazy_linker_rdebug.h
new file mode 100644
index 0000000..520c089
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_rdebug.h
@@ -0,0 +1,168 @@
+// 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.
+
+#ifndef CRAZY_LINKER_RDEBUG_H
+#define CRAZY_LINKER_RDEBUG_H
+
+#include <stdint.h>
+
+// The system linker maintains two lists of libraries at runtime:
+//
+// - A list that is used by GDB and other tools to search for the
+//   binaries that are loaded in the process.
+//
+//   This list is accessible by looking at the DT_DEBUG field of the
+//   dynamic section of any ELF binary loaded by the linker (including
+//   itself). The field contains the address of a global '_r_debug'
+//   variable. More on this later.
+//
+// - A list that is used internally to implement library and symbol
+//   lookup. The list head and tail are called 'solist' and 'sonext'
+//   in the linker sources, and their address is unknown (and randomized
+//   by ASLR), and there is no point trying to change it.
+//
+// This means that you cannot call the linker's dlsym() function to
+// lookup symbols in libraries that are not loaded through it, i.e.
+// any custom dynamic linker needs its own dlopen() / dlsym() and other
+// related functions, and ensure the loaded code only uses its own version.
+// (See support code in crazy_linker_wrappers.cpp)
+//
+// The global '_r_debug' variable is a r_debug structure, whose layout
+// must be known by GDB, with the following fields:
+//
+//     r_version   -> version of the structure (must be 1)
+//     r_map       -> head of a linked list of 'link_map_t' entries,
+//                    one per ELF 'binary' in the process address space.
+//     r_brk       -> pointer to a specific debugging function (see below).
+//     r_state     -> state variable to be read in r_brk().
+//     r_ldbase    -> unused by the system linker, should be 0. (?)
+//
+// The 'r_brk' field points to an empty function in the system linker
+// that is used to notify GDB of changes in the list of shared libraries,
+// this works as follows:
+//
+//   - When the linker wants to add a new shared library to the list,
+//     it first writes RT_ADD to 'r_state', then calls 'r_brk()'.
+//
+//     It modifies the list, then writes RT_CONSISTENT to 'r_state' and
+//     calls 'r_brk()' again.
+//
+//   - When unloading a library, RT_DELETE + RT_CONSISTENT are used
+//     instead.
+//
+// GDB will always place a breakpoint on the function pointed to by
+// 'r_brk', and will be able to synchronize with the linker's
+// modifications.
+//
+// The 'r_map' field is a list of nodes with the following structure
+// describing each loaded shared library for GDB:
+//
+//   l_addr  -> Load address of the first PT_LOAD segment in a
+//              shared library. Note that this is 0 for the linker itself
+//              and the load-bias for an executable.
+//   l_name  -> Name of the executable. This is _always_ a basename!!
+//   l_ld    -> Address of the dynamic table for this binary.
+//   l_next  -> Pointer to next item in 'r_map' list or NULL.
+//   l_prev  -> Pointer to previous item in 'r_map' list.
+//
+// Note that the system linker ensures that there are always at least
+// two items in this list:
+//
+// - The first item always describes the linker itself, the fields
+//   actually point to a specially crafted fake entry for it called
+//   'libdl_info' in the linker sources.
+//
+// - The second item describes the executable that was started by
+//   the kernel. For Android applications, it will always be 'app_process'
+//   and completely uninteresting.
+//
+// - Eventually, another entry for VDSOs on platforms that support them.
+//
+// When implementing a custom linker, being able to debug the process
+// unfortunately requires modifying the 'r_map' list to also account
+// for libraries loading through it.
+//
+// One issue with this is that the linker also uses another internal
+// variable, called '_r_debut_tail' that points to the last item in
+// the list. And there is no way to access it directly. This can lead
+// to problems when calling APIs that actually end up using the system's
+// own dlopen(). Consider this example:
+//
+//  1/ Program loads crazy_linker
+//
+//  2/ Program uses crazy_linker to load libfoo.so, this adds
+//     a new entry at the end of the '_r_debug.r_map' list, but
+//     '_r_debug.tail' is unmodified.
+//
+//  3/ libfoo.so or the Java portion of the program calls a system API
+//     that ends up loading another library (e.g. libGLESv2_vendor.so),
+//     this calls the system dlopen().
+//
+//  4/ The system dlopen() adds a new entry to the "_r_debug.r_map"
+//     list by updating the l_next / l_prev fields of the entry pointed
+//     to by '_r_debug_tail', and this removes 'libfoo.so' from the list!
+//
+// There is a simple work-around for this issue: Always insert our
+// libraries at the _start_ of the 'r_map' list, instead of appending
+// them to the end. The system linker doesn't know about custom-loaded
+// libraries and thus will never try to unload them.
+//
+// Note that the linker never uses the 'r_map' list (except or updating
+// it for GDB), it only uses 'solist / sonext' to actually perform its
+// operations. That's ok if our custom linker completely wraps and
+// re-implements these.
+namespace crazy {
+
+struct link_map_t {
+  uintptr_t l_addr;
+  char* l_name;
+  uintptr_t l_ld;
+  link_map_t* l_next;
+  link_map_t* l_prev;
+};
+
+// Values for r_debug->r_state
+enum {
+  RT_CONSISTENT,
+  RT_ADD,
+  RT_DELETE
+};
+
+struct r_debug {
+  int32_t r_version;
+  link_map_t* r_map;
+  void (*r_brk)(void);
+  int32_t r_state;
+  uintptr_t r_ldbase;
+};
+
+class RDebug {
+ public:
+  RDebug() : r_debug_(NULL), init_(false), readonly_entries_(false) {}
+  ~RDebug() {}
+
+  // Add a new entry to the list.
+  void AddEntry(link_map_t* entry);
+
+  // Remove an entry from the list.
+  void DelEntry(link_map_t* entry);
+
+  r_debug* GetAddress() { return r_debug_; }
+
+ private:
+  // Try to find the address of the global _r_debug variable, even
+  // though there is no symbol for it. Returns true on success.
+  bool Init();
+
+  RDebug(const RDebug&);
+  RDebug& operator=(const RDebug&);
+
+  r_debug* r_debug_;
+  bool init_;
+  bool readonly_entries_;
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_REDUG_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_search_path_list.cpp b/sources/android/crazy_linker/src/crazy_linker_search_path_list.cpp
new file mode 100644
index 0000000..027a52b
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_search_path_list.cpp
@@ -0,0 +1,83 @@
+// 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_search_path_list.h"
+
+#include <string.h>
+
+#include "crazy_linker_debug.h"
+#include "crazy_linker_system.h"
+
+namespace crazy {
+
+void SearchPathList::Reset() {
+  list_.Resize(0);
+  env_list_.Resize(0);
+  full_path_.Resize(0);
+}
+
+void SearchPathList::ResetFromEnv(const char* var_name) {
+  Reset();
+  const char* env = GetEnv(var_name);
+  if (env && *env)
+    env_list_ = env;
+}
+
+void SearchPathList::AddPaths(const char* list, const char* list_end) {
+  // Append a column to the current list, if necessary
+  if (list_.size() > 0 && list_[list_.size() - 1] != ':')
+    list_ += ':';
+  list_.Append(list, list_end - list);
+}
+
+const char* SearchPathList::FindFile(const char* file_name) {
+  // Sanity checks.
+  if (!file_name || !*file_name)
+    return NULL;
+
+  LOG("%s: Looking for %s\n", __FUNCTION__, file_name);
+
+  // Build full list by appending the env_list_ after the regular one.
+  String full_list = list_;
+  if (env_list_.size() > 0) {
+    if (full_list.size() > 0 && full_list[full_list.size() - 1] != ':')
+      full_list += ':';
+    full_list += env_list_;
+  }
+
+  // Iterate over all items in the list.
+  const char* p = full_list.c_str();
+  const char* end = p + full_list.size();
+
+  while (p < end) {
+    // compute current list item, and next item start at the same time.
+    const char* item = p;
+    const char* item_end =
+        reinterpret_cast<const char*>(memchr(p, ':', end - p));
+    if (item_end)
+      p = item_end + 1;
+    else {
+      item_end = end;
+      p = end;
+    }
+
+    full_path_.Assign(item, item_end - item);
+
+    // Add trailing directory separator if needed.
+    if (full_path_.size() > 0 && full_path_[full_path_.size() - 1] != '/')
+      full_path_ += '/';
+
+    full_path_ += file_name;
+
+    if (PathIsFile(full_path_.c_str())) {
+      LOG("    FOUND %s\n", full_path_.c_str());
+      return full_path_.c_str();
+    } else
+      LOG("    skip  %s\n", full_path_.c_str());
+  }
+
+  return NULL;
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_search_path_list.h b/sources/android/crazy_linker/src/crazy_linker_search_path_list.h
new file mode 100644
index 0000000..dd3fd91
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_search_path_list.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef CRAZY_LINKER_SEARCH_PATH_LIST_H
+#define CRAZY_LINKER_SEARCH_PATH_LIST_H
+
+#include <string.h>
+
+#include "crazy_linker_util.h"  // for String
+
+namespace crazy {
+
+// A simple class to model a list of search paths, and perform
+// file system probing with it.
+class SearchPathList {
+ public:
+  SearchPathList() : list_(), env_list_(), full_path_() {}
+
+  // Reset the list, i.e. make it empty.
+  void Reset();
+
+  // Reset the list from an environment variable value.
+  void ResetFromEnv(const char* var_name);
+
+  // Add one or more paths to the list.
+  // |path_list| contains a list of paths separated by columns.
+  // |path_list_end| points after the list's last character.
+  void AddPaths(const char* path_list, const char* path_list_end);
+
+  // Convenience function that takes a 0-terminated string.
+  void AddPaths(const char* path_list) {
+    AddPaths(path_list, path_list + ::strlen(path_list));
+  }
+
+  // Try to find a file named |file_name| by probing the file system
+  // with every item in the list as a suffix. On success, returns the
+  // full path string, or NULL on failure.
+  const char* FindFile(const char* file_name);
+
+ private:
+  String list_;
+  String env_list_;
+  String full_path_;
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_SEARCH_PATH_LIST_H
\ No newline at end of file
diff --git a/sources/android/crazy_linker/src/crazy_linker_search_path_list_unittest.cpp b/sources/android/crazy_linker/src/crazy_linker_search_path_list_unittest.cpp
new file mode 100644
index 0000000..a534970
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_search_path_list_unittest.cpp
@@ -0,0 +1,90 @@
+// 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_search_path_list.h"
+
+#include <minitest/minitest.h>
+#include "crazy_linker_system_mock.h"
+
+namespace crazy {
+
+class TestSystem {
+ public:
+  TestSystem() : sys_() {
+    sys_.AddRegularFile("/tmp/foo/bar", "BARBARBAR", 9);
+    sys_.AddRegularFile("/tmp/zoo", "ZOO", 3);
+    sys_.AddRegularFile("/foo", "Foo", 3);
+    sys_.AddEnvVariable("TEST_LIBRARY_PATH", "/tmp:/");
+  }
+
+  ~TestSystem() {}
+
+  void AddFile(const char* path, const char* data, size_t len) {
+    sys_.AddRegularFile(path, data, len);
+  }
+
+ private:
+  SystemMock sys_;
+};
+
+TEST(SearchPathList, Empty) {
+  TestSystem sys;
+  SearchPathList list;
+  EXPECT_FALSE(list.FindFile("/foo"));
+  EXPECT_FALSE(list.FindFile("/tmp/zoo"));
+  EXPECT_FALSE(list.FindFile("/tmp/foo/bar"));
+}
+
+TEST(SearchPathList, OneItem) {
+  TestSystem sys;
+  SearchPathList list;
+  list.AddPaths("/tmp/foo");
+  EXPECT_STREQ("/tmp/foo/bar", list.FindFile("bar"));
+  EXPECT_FALSE(list.FindFile("zoo"));
+  EXPECT_FALSE(list.FindFile("foo"));
+}
+
+TEST(SearchPathList, Reset) {
+  TestSystem sys;
+  SearchPathList list;
+  list.AddPaths("/tmp/foo");
+  EXPECT_STREQ("/tmp/foo/bar", list.FindFile("bar"));
+
+  list.Reset();
+  EXPECT_FALSE(list.FindFile("bar"));
+}
+
+TEST(SearchPathList, ResetFromEnv) {
+  TestSystem sys;
+  SearchPathList list;
+  list.ResetFromEnv("TEST_LIBRARY_PATH");
+  EXPECT_STREQ("/tmp/foo/bar", list.FindFile("foo/bar"));
+  EXPECT_STREQ("/foo", list.FindFile("foo"));
+}
+
+TEST(SearchPathList, ThreeItems) {
+  TestSystem sys;
+  SearchPathList list;
+  list.AddPaths("/tmp/foo");
+  list.AddPaths("/tmp/");
+
+  EXPECT_STREQ("/tmp/foo/bar", list.FindFile("bar"));
+  EXPECT_STREQ("/tmp/zoo", list.FindFile("zoo"));
+  EXPECT_FALSE(list.FindFile("foo"));
+}
+
+TEST(SearchPathList, EnvPathsAfterAddedOnes) {
+  TestSystem sys;
+  sys.AddFile("/opt/foo", "FOO", 3);
+  SearchPathList list;
+  list.ResetFromEnv("TEST_LIBRARY_PATH");
+  list.AddPaths("/opt");
+
+  // This checks that paths added with AddPaths() have priority over
+  // paths added with ResetFromEnv(). An invalid implementation would
+  // find '/tmp/foo' instead.
+  EXPECT_STREQ("/opt/foo", list.FindFile("foo"));
+}
+
+}  // namespace crazy
\ No newline at end of file
diff --git a/sources/android/crazy_linker/src/crazy_linker_shared_library.cpp b/sources/android/crazy_linker/src/crazy_linker_shared_library.cpp
new file mode 100644
index 0000000..0f7a72e
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_shared_library.cpp
@@ -0,0 +1,739 @@
+// 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_relocator.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);
+
+// Compute the ELF hash of a given symbol.
+unsigned ElfHash(const char* name) {
+  const uint8_t* ptr = reinterpret_cast<const uint8_t*>(name);
+  unsigned h = 0;
+  while (*ptr) {
+    h = (h << 4) + *ptr++;
+    unsigned g = h & 0xf0000000;
+    h ^= g;
+    h ^= g >> 24;
+  }
+  return h;
+}
+
+ELF::Sym* LookupSymbolForAddress(SharedLibrary* lib, void* address) {
+  ELF::Addr elf_addr = reinterpret_cast<ELF::Addr>(address) - lib->base;
+
+  for (size_t n = 0; n < lib->nchain; ++n) {
+    ELF::Sym* sym = &lib->symtab[n];
+    if (sym->st_shndx != SHN_UNDEF && elf_addr >= sym->st_value &&
+        elf_addr < sym->st_value + sym->st_size) {
+      return sym;
+    }
+  }
+  return NULL;
+}
+
+// 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();
+}
+
+// Parse the dynamic section of |lib| and populate various important
+// fields from it. On success, returns true. On failure, returns false,
+// and sets |error| message.
+bool ParseLibraryDynamicTable(SharedLibrary* lib, Error* error) {
+  const ELF::Phdr* phdr = lib->phdr;
+  size_t phdr_count = lib->phnum;
+  ELF::Addr base = lib->base;
+
+  phdr_table_get_dynamic_section(phdr,
+                                 phdr_count,
+                                 base,
+                                 &lib->dynamic,
+                                 &lib->dynamic_count,
+                                 &lib->dynamic_flags);
+  if (!lib->dynamic) {
+    *error = "No PT_DYNAMIC section!";
+    return false;
+  }
+
+#ifdef __arm__
+  (void)phdr_table_get_arm_exidx(
+      phdr, phdr_count, base, &lib->ARM_exidx, &lib->ARM_exidx_count);
+#endif
+
+  for (ELF::Dyn* dyn = lib->dynamic; dyn->d_tag != DT_NULL; ++dyn) {
+    ELF::Addr dyn_value = dyn->d_un.d_val;
+    uintptr_t dyn_addr = base + dyn->d_un.d_ptr;
+    switch (dyn->d_tag) {
+      case DT_HASH:
+        LOG("  DT_HASH addr=%p\n", dyn_addr);
+        {
+          uintptr_t* data = reinterpret_cast<uintptr_t*>(dyn_addr);
+          lib->nbucket = data[0];
+          lib->nchain = data[1];
+          lib->bucket = data + 2;
+          lib->chain = data + 2 + lib->nbucket;
+        }
+        break;
+      case DT_STRTAB:
+        LOG("  DT_STRTAB addr=%p\n", dyn_addr);
+        lib->strtab = reinterpret_cast<const char*>(dyn_addr);
+        break;
+      case DT_SYMTAB:
+        LOG("  DT_SYMTAB addr=%p\n", dyn_addr);
+        lib->symtab = reinterpret_cast<ELF::Sym*>(dyn_addr);
+        break;
+      case DT_DEBUG:
+        // TODO(digit): Move this to a different location.
+        if (lib->dynamic_flags & PF_W) {
+          dyn->d_un.d_val =
+              reinterpret_cast<uintptr_t>(Globals::GetRDebug()->GetAddress());
+        }
+        break;
+      case DT_INIT:
+        LOG("  DT_INIT addr=%p\n", dyn_addr);
+        lib->init_func = reinterpret_cast<linker_function_t>(dyn_addr);
+        break;
+      case DT_FINI:
+        LOG("  DT_FINI addr=%p\n", dyn_addr);
+        lib->fini_func = reinterpret_cast<linker_function_t>(dyn_addr);
+        break;
+      case DT_INIT_ARRAY:
+        LOG("  DT_INIT_ARRAY addr=%p\n", dyn_addr);
+        lib->init_array = reinterpret_cast<linker_function_t*>(dyn_addr);
+        break;
+      case DT_INIT_ARRAYSZ:
+        lib->init_array_count = dyn_value / sizeof(ELF::Addr);
+        LOG("  DT_INIT_ARRAYSZ value=%p count=%p\n",
+            dyn_value,
+            lib->init_array_count);
+        break;
+      case DT_FINI_ARRAY:
+        LOG("  DT_FINI_ARRAY addr=%p\n", dyn_addr);
+        lib->fini_array = reinterpret_cast<linker_function_t*>(dyn_addr);
+        break;
+      case DT_FINI_ARRAYSZ:
+        lib->fini_array_count = dyn_value / sizeof(ELF::Addr);
+        LOG("  DT_FINI_ARRAYSZ value=%p count=%p\n",
+            dyn_value,
+            lib->fini_array_count);
+        break;
+      case DT_PREINIT_ARRAY:
+        LOG("  DT_PREINIT_ARRAY addr=%p\n", dyn_addr);
+        lib->preinit_array = reinterpret_cast<linker_function_t*>(dyn_addr);
+        break;
+      case DT_PREINIT_ARRAYSZ:
+        lib->preinit_array_count = dyn_value / sizeof(ELF::Addr);
+        LOG("  DT_PREINIT_ARRAYSZ value=%p count=%p\n",
+            dyn_value,
+            lib->preinit_array_count);
+        break;
+      case DT_SYMBOLIC:
+        LOG("  DT_SYMBOLIC\n");
+        lib->has_DT_SYMBOLIC = true;
+        break;
+      case DT_FLAGS:
+        if (dyn_value & DF_SYMBOLIC)
+          lib->has_DT_SYMBOLIC = true;
+        break;
+#if defined(__mips__)
+      case DT_MIPS_RLD_MAP:
+        // TODO(digit): Move this to different location.
+        dyn->d_un.d_ptr =
+            reinterpret_cast<ELF::Addr>(Globals::GetRDebug()->GetAddress());
+        break;
+#endif
+      default:
+        ;
+    }
+  }
+
+  // Perform a few sanity checks.
+  if (!lib->nbucket) {
+    *error = "Missing DT_HASH entry (built with --hash-style=gnu?)";
+    return false;
+  }
+  if (!lib->strtab) {
+    *error = "Missing DT_STRTAB entry";
+    return false;
+  }
+  if (!lib->symtab) {
+    *error = "Missing DT_SYMTAB entry";
+    return false;
+  }
+  return true;
+}
+
+bool LoadLibrary(SharedLibrary* lib,
+                 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(lib->full_path)) {
+    error->Format("Path too long: %s", full_path);
+    return false;
+  }
+
+  strlcpy(lib->full_path, full_path, sizeof(lib->full_path));
+  lib->base_name = GetBaseNamePtr(lib->full_path);
+
+  // Load the ELF binary in memory.
+  LOG("%s: Loading ELF segments for %s\n", __FUNCTION__, lib->base_name);
+
+  {
+    ElfLoader loader;
+    if (!loader.LoadAt(lib->full_path, file_offset, load_address, error)) {
+      return false;
+    }
+
+    lib->base = loader.load_start();
+    lib->size = loader.load_size();
+    lib->load_bias = loader.load_bias();
+    lib->phnum = loader.phdr_count();
+    lib->phdr = loader.loaded_phdr();
+  }
+
+  // Parse the dynamic table to extract useful information.
+  LOG("%s: Parsing dynamic table of %s\n", __FUNCTION__, lib->base_name);
+  if (!ParseLibraryDynamicTable(lib, error))
+    return false;
+
+  LOG("%s: Load complete for %s\n", __FUNCTION__, lib->base_name);
+  return true;
+}
+
+// An instance of ElfRelocator::SymbolResolver that can be used
+// to resolve symbols in a shared library being loaded by
+// LibraryList::LoadLibrary.
+class SharedLibraryResolver : public ElfRelocator::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.
+    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);
+        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_;
+};
+
+bool RelocateLibrary(SharedLibrary* lib,
+                     LibraryList* lib_list,
+                     Vector<LibraryView*>* dependencies,
+                     Error* error) {
+  // Apply relocations.
+  LOG("%s: Applying relocations to %s\n", __FUNCTION__, lib->base_name);
+
+  ElfRelocator relocator;
+
+  if (!relocator.Init(lib->phdr,
+                      lib->phnum,
+                      lib->load_bias,
+                      lib->dynamic,
+                      lib->dynamic_count,
+                      error)) {
+    return false;
+  }
+
+  SharedLibraryResolver resolver(lib, lib_list, dependencies);
+  if (!relocator.Apply(&resolver, lib->strtab, lib->symtab, error))
+    return false;
+
+  LOG("%s: Relocations applied for %s\n", __FUNCTION__, lib->base_name);
+  return true;
+}
+
+// Copy all the pages from |addr| and |addr + size| into an ashmem
+// region identified by |fd|.
+bool CopyPagesToFd(void* addr, size_t size, int fd, Error* error) {
+
+  // Create temporary mapping of the ashmem region.
+  // And copy current pages there.
+  ScopedMemoryMapping fd_map;
+
+  if (!fd_map.Allocate(NULL, size, MemoryMapping::CAN_READ_WRITE, fd)) {
+    error->Format("Cannot map ashmem region of %d bytes as writable: %s",
+                  size,
+                  strerror(errno));
+    return false;
+  }
+
+  // Copy current pages there.
+  ::memcpy(fd_map.Get(), addr, size);
+  return true;
+}
+
+// Swap pages between |addr| and |addr + size| with the bytes
+// from the ashmem region identified by |fd|, starting from
+// a given |offset|. On failure return false and set |error| message.
+bool SwapPagesFromFd(void* addr,
+                     size_t size,
+                     int fd,
+                     size_t offset,
+                     Error* error) {
+  // Unmap current pages.
+  if (::munmap(addr, size) < 0) {
+    error->Format("%s: Could not unmap %p-%p: %s",
+                  __FUNCTION__,
+                  addr,
+                  (char*)addr + size,
+                  strerror(errno));
+    return false;
+  }
+
+  // Remap the fd pages at the same location now.
+  void* new_map = ::mmap(addr,
+                         size,
+                         PROT_READ,
+                         MAP_FIXED | MAP_SHARED,
+                         fd,
+                         static_cast<off_t>(offset));
+  if (new_map == MAP_FAILED) {
+    char* p = reinterpret_cast<char*>(addr);
+    error->Format("%s: Could not map %p-%p: %s",
+                  __FUNCTION__,
+                  p,
+                  p + size,
+                  strerror(errno));
+    return false;
+  }
+
+#ifdef __arm__
+  __clear_cache(addr, (char*)addr + size);
+#endif
+
+  // Done.
+  return true;
+}
+
+bool PageEquals(const char* p1, const char* p2) {
+  return ::memcmp(p1, p2, PAGE_SIZE) == 0;
+}
+
+// Conditionally swap pages between |address| and |address + size| with
+// the ones from the ashmem region identified by |fd|. This only swaps
+// pages that have exactly the same content. On failure, return false
+// and set |error| message.
+bool SwapSimilarPagesFromFd(void* addr, size_t size, int fd, Error* error) {
+  // Create temporary mapping of the ashmem region.
+  ScopedMemoryMapping fd_map;
+
+  LOG("%s: Entering addr=%p size=%p fd=%d\n",
+      __FUNCTION__,
+      addr,
+      (void*)size,
+      fd);
+
+  if (!fd_map.Allocate(NULL, size, MemoryMapping::CAN_READ, fd)) {
+    error->Format("Cannot map ashmem region as read-only: %s\n",
+                  strerror(errno));
+    return false;
+  }
+
+  LOG("%s: mapping allocate at %p\n", __FUNCTION__, fd_map.Get());
+
+  char* cur_page = reinterpret_cast<char*>(addr);
+  char* fd_page = reinterpret_cast<char*>(fd_map.Get());
+  size_t p = 0;
+  size_t similar_size = 0;
+
+  do {
+    // Skip over dissimilar pages.
+    while (p < size && !PageEquals(cur_page + p, fd_page + p)) {
+      p += PAGE_SIZE;
+    }
+
+    // Count similar pages.
+    size_t p2 = p;
+    while (p2 < size && PageEquals(cur_page + p2, fd_page + p2)) {
+      p2 += PAGE_SIZE;
+    }
+
+    if (p2 > p) {
+      // Swap pages between |pos| and |pos2|.
+      LOG("%s: Swap pages at %p-%p\n",
+          __FUNCTION__,
+          cur_page + p,
+          cur_page + p2);
+      if (!SwapPagesFromFd(cur_page + p, p2 - p, fd, p, error))
+        return false;
+
+      similar_size += (p2 - p);
+    }
+
+    p = p2;
+  } while (p < size);
+
+  LOG("%s: Swapped %d pages over %d (%d %%, %d KB not shared)\n",
+      __FUNCTION__,
+      similar_size / PAGE_SIZE,
+      size / PAGE_SIZE,
+      similar_size * 100 / size,
+      (size - similar_size) / 4096);
+
+  return true;
+}
+
+}  // namespace
+
+SharedLibrary::SharedLibrary() {
+  // Ensure all fields are empty.
+  memset(this, 0, sizeof(*this));
+  this->relro_fd = -1;
+}
+
+SharedLibrary::~SharedLibrary() {
+  // Ensure the library is unmapped on destruction.
+  if (this->base)
+    munmap(reinterpret_cast<void*>(this->base), this->size);
+  // Ensure the relro ashmem file descriptor is closed.
+  if (this->relro_fd >= 0)
+    close(this->relro_fd);
+}
+
+bool SharedLibrary::Load(const char* full_path,
+                         size_t load_address,
+                         size_t file_offset,
+                         Error* error) {
+  return LoadLibrary(this, full_path, load_address, file_offset, error);
+}
+
+bool SharedLibrary::Relocate(LibraryList* lib_list,
+                             Vector<LibraryView*>* dependencies,
+                             Error* error) {
+  return RelocateLibrary(this, lib_list, dependencies, error);
+}
+
+ELF::Sym* SharedLibrary::LookupSymbolEntry(const char* symbol_name) {
+  unsigned hash = ElfHash(symbol_name);
+  ELF::Sym* symbols = this->symtab;
+  const char* strings = this->strtab;
+
+  for (unsigned n = this->bucket[hash % this->nbucket]; n != 0;
+       n = this->chain[n]) {
+    ELF::Sym* symbol = &symbols[n];
+    // Check that the symbol has the appropriate name.
+    if (strcmp(strings + symbol->st_name, symbol_name))
+      continue;
+    // Ignore undefined symbols.
+    if (symbol->st_shndx == SHN_UNDEF)
+      continue;
+    // Ignore anything that isn't a global or weak definition.
+    switch (ELF_ST_BIND(symbol->st_info)) {
+      case STB_GLOBAL:
+      case STB_WEAK:
+        return symbol;
+      default:
+        ;
+    }
+  }
+  return NULL;
+}
+
+void* SharedLibrary::FindAddressForSymbol(const char* symbol_name) {
+  ELF::Sym* entry = LookupSymbolEntry(symbol_name);
+  if (!entry)
+    return NULL;
+
+  return reinterpret_cast<void*>(this->load_bias + entry->st_value);
+}
+
+ELF::Sym* SharedLibrary::FindSymbolForAddress(void* address) {
+  return LookupSymbolForAddress(this, address);
+}
+
+bool SharedLibrary::EnableSharedRelro(Error* error) {
+  if (this->relro_used) {
+    *error = "Shared RELRO already used in library";
+    return false;
+  }
+
+  if (this->relro_fd != -1) {
+    // Shared RELRO is already enabled, nothing to do here.
+    return true;
+  }
+
+  ELF::Addr relro_start, relro_size;
+  if (phdr_table_get_relro_info(
+          this->phdr, this->phnum, this->load_bias, &relro_start, &relro_size) <
+      0) {
+    // No RELRO section to share.
+    return true;
+  }
+
+  if (relro_size == 0) {
+    // Probably means there is no real RELRO section.
+    return true;
+  }
+
+  // Allocate new ashmem region.
+  this->relro_start = relro_start;
+  this->relro_size = relro_size;
+  AshmemRegion ashmem;
+  String relro_name("RELRO:");
+  relro_name += this->base_name;
+  if (!ashmem.Allocate(relro_size, relro_name.c_str())) {
+    error->Format("Could not allocate ashmem region: %s", strerror(errno));
+    return false;
+  }
+
+  void* relro_addr = reinterpret_cast<void*>(relro_start);
+  if (!CopyPagesToFd(relro_addr, relro_size, ashmem.Get(), error))
+    return false;
+
+  // Ensure the ashmem region content isn't writable anymore.
+  if (!ashmem.SetProtectionFlags(PROT_READ)) {
+    error->Format("Could not make ashmem region read-only: %s",
+                  strerror(errno));
+    return false;
+  }
+
+  if (!SwapPagesFromFd(relro_addr, relro_size, ashmem.Get(), 0, error))
+    return false;
+
+  this->relro_fd = ashmem.Release();
+  return true;
+}
+
+bool SharedLibrary::UseSharedRelro(size_t relro_start,
+                                   size_t relro_size,
+                                   int relro_fd,
+                                   Error* error) {
+  if (relro_fd < 0 || relro_size == 0) {
+    // Nothing to do here.
+    return true;
+  }
+
+  LOG("%s: relro_start=%p relro_size=%p relro_fd=%d\n",
+      __FUNCTION__,
+      (void*)relro_start,
+      (void*)relro_size,
+      relro_fd);
+
+  // Sanity check: A shared RELRO is not already used.
+  if (this->relro_used) {
+    *error = "Library already using shared RELRO section";
+    return false;
+  }
+
+  // Sanity check: A shared RELRO is not enabled.
+  if (this->relro_fd != -1) {
+    *error = "Library already enabled its shared RELRO";
+    return false;
+  }
+
+  // Sanity check: Ashmem file descriptor must be read-only.
+  if (!AshmemRegion::CheckFileDescriptorIsReadOnly(relro_fd)) {
+    error->Format("Ashmem file descriptor is not read-only: %s\n",
+                  strerror(errno));
+    return false;
+  }
+
+  // Sanity check: RELRO addresses must match.
+  ELF::Addr lib_relro_start, lib_relro_size;
+  if (phdr_table_get_relro_info(this->phdr,
+                                this->phnum,
+                                this->load_bias,
+                                &lib_relro_start,
+                                &lib_relro_size) < 0) {
+    *error = "No RELRO section in library";
+    return false;
+  }
+
+  if (lib_relro_start != relro_start || lib_relro_size != relro_size) {
+    error->Format("RELRO mismatch addr=%p size=%p (wanted addr=%p size=%p)",
+                  lib_relro_start,
+                  lib_relro_size,
+                  relro_start,
+                  relro_size);
+    return false;
+  }
+
+  // Everything's good, swap pages in this process's address space.
+  void* relro_addr = reinterpret_cast<void*>(relro_start);
+  if (!SwapSimilarPagesFromFd(relro_addr, relro_size, relro_fd, error))
+    return false;
+
+  this->relro_used = true;
+  return true;
+}
+
+void SharedLibrary::CallConstructors() {
+  CallFunction(this->init_func, "DT_INIT");
+  for (size_t n = 0; n < this->init_array_count; ++n)
+    CallFunction(this->init_array[n], "DT_INIT_ARRAY");
+}
+
+void SharedLibrary::CallDestructors() {
+  for (size_t n = this->fini_array_count; n > 0; --n) {
+    CallFunction(this->fini_array[n - 1], "DT_FINI_ARRAY");
+  }
+  CallFunction(this->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>(
+      this->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",
+                  this->full_path,
+                  jni_version,
+                  minimum_jni_version);
+    return false;
+  }
+
+  // Save the JavaVM handle for unload time.
+  this->java_vm = java_vm;
+  return true;
+}
+
+void SharedLibrary::CallJniOnUnload() {
+  if (!this->java_vm)
+    return;
+
+  JNI_OnUnloadFunctionPtr jni_on_unload =
+      reinterpret_cast<JNI_OnUnloadFunctionPtr>(
+          this->FindAddressForSymbol("JNI_OnUnload"));
+
+  if (jni_on_unload)
+    (*jni_on_unload)(this->java_vm, NULL);
+}
+
+bool SharedLibrary::DependencyIterator::GetNext() {
+  dep_name_ = NULL;
+  for (;;) {
+    if (dynamic_->d_tag == 0)
+      return false;
+
+    if (dynamic_->d_tag != DT_NEEDED) {
+      dynamic_++;
+      continue;
+    }
+
+    dep_name_ = strtab_ + dynamic_->d_un.d_val;
+    dynamic_++;
+    return true;
+  }
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_shared_library.h b/sources/android/crazy_linker/src/crazy_linker_shared_library.h
new file mode 100644
index 0000000..aa95a36
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_shared_library.h
@@ -0,0 +1,183 @@
+// 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.
+
+#ifndef CRAZY_LINKER_SHARED_LIBRARY_H
+#define CRAZY_LINKER_SHARED_LIBRARY_H
+
+#include <link.h>
+
+#include "crazy_linker_error.h"
+#include "crazy_linker_rdebug.h"
+#include "crazy_linker_util.h"
+#include "elf_traits.h"
+
+namespace crazy {
+
+class LibraryList;
+class LibraryView;
+
+// A class that models a shared library loaded by the crazy linker.
+
+// Libraries have dependencies (which are listed in their dynamic section
+// as DT_NEEDED entries). Circular dependencies are forbidden, so they
+// form an ADG, where the root is the crazy linker itself, since all
+// libraries that it loads will depend on it (to ensure their
+// dlopen/dlsym/dlclose calls are properly wrapped).
+
+class SharedLibrary {
+ public:
+  const ELF::Phdr* phdr;
+  size_t phnum;
+  ELF::Addr load_bias;
+
+  ELF::Addr entry;
+  ELF::Addr base;
+  size_t size;
+
+  ELF::Dyn* dynamic;
+  size_t dynamic_count;
+  ELF::Word dynamic_flags;
+
+  SharedLibrary* list_next;
+  SharedLibrary* list_prev;
+  unsigned flags;
+
+  const char* strtab;
+  ELF::Sym* symtab;
+
+  size_t nbucket;
+  size_t nchain;
+  unsigned* bucket;
+  unsigned* chain;
+
+  typedef void (*linker_function_t)();
+
+  linker_function_t* preinit_array;
+  size_t preinit_array_count;
+  linker_function_t* init_array;
+  size_t init_array_count;
+  linker_function_t* fini_array;
+  size_t fini_array_count;
+  linker_function_t init_func;
+  linker_function_t fini_func;
+
+#ifdef __arm__
+  // ARM EABI section used for stack unwinding.
+  unsigned* ARM_exidx;
+  size_t ARM_exidx_count;
+#endif
+
+  link_map_t link_map;
+
+  bool has_DT_SYMBOLIC;
+
+  size_t relro_start;
+  size_t relro_size;
+  int relro_fd;
+  bool relro_used;
+
+  void* java_vm;
+
+  const char* base_name;
+  char full_path[512];
+
+  SharedLibrary();
+  ~SharedLibrary();
+
+  // Load a library (without its dependents) from an ELF file.
+  // Note: This does not apply relocations, nor runs constructors.
+  // |full_path| if the file full path.
+  // |load_address| is the page-aligned load address in memory, or 0.
+  // |file_offset| is the page-aligned file offset.
+  // On failure, return false and set |error| message.
+  //
+  // After this, the caller should load all library dependencies,
+  // Then call Relocate() and CallConstructors() to complete the
+  // operation.
+  bool Load(const char* full_path,
+            size_t load_address,
+            size_t file_offset,
+            Error* error);
+
+  // Relocate this library, assuming all its dependencies are already
+  // loaded in |lib_list|. On failure, return false and set |error|
+  // message.
+  bool Relocate(LibraryList* lib_list,
+                Vector<LibraryView*>* dependencies,
+                Error* error);
+
+  // Call all constructors in the library.
+  void CallConstructors();
+
+  // Call all destructors in the library.
+  void CallDestructors();
+
+  // Return the ELF symbol entry for a given symbol, if defined by
+  // this library, or NULL otherwise.
+  ELF::Sym* LookupSymbolEntry(const char* symbol_name);
+
+  // Return the address of a given |symbol_name| if it is exported
+  // by the library, NULL otherwise.
+  void* FindAddressForSymbol(const char* symbol_name);
+
+  // Find the symbol entry in this library that matches a given
+  // address in memory. Returns NULL on failure.
+  ELF::Sym* FindSymbolForAddress(void* address);
+
+  // Prepare the relro section for sharing with another process.
+  // On success, return true and sets relro_fd to an ashmem file
+  // descriptor holding the shared RELRO section. On failure
+  // return false ands sets |error| message.
+  bool EnableSharedRelro(Error* error);
+
+  // Try to use a shared relro section from another process.
+  // On success, return true. On failure return false and
+  // sets |error| message.
+  bool UseSharedRelro(size_t relro_start,
+                      size_t relro_size,
+                      int relro_fd,
+                      Error* error);
+
+  // Look for a symbol named 'JNI_OnLoad' in this library, and if it
+  // exists, call it with |java_vm| as the first parameter. If the
+  // function result is less than |minimum_jni_version|, fail with
+  // a message in |error|. On success, return true, and record
+  // |java_vm| to call 'JNI_OnUnload' at unload time, if present.
+  bool SetJavaVM(void* java_vm, int minimum_jni_version, Error* error);
+
+  // Call 'JNI_OnUnload()' is necessary, i.e. if there was a succesful call
+  // to SetJavaVM() before. This will pass the same |java_vm| value to the
+  // callback, if it is present in the library.
+  void CallJniOnUnload();
+
+  // Helper class to iterate over dependencies in a given SharedLibrary.
+  // Usage:
+  //    SharedLibary::DependencyIterator iter(lib);
+  //    while (iter.GetNext() {
+  //      dependency_name = iter.GetName();
+  //      ...
+  //    }
+  class DependencyIterator {
+   public:
+    DependencyIterator(SharedLibrary* lib)
+        : dynamic_(lib->dynamic), strtab_(lib->strtab), dep_name_(NULL) {}
+
+    bool GetNext();
+
+    const char* GetName() const { return dep_name_; }
+
+   private:
+    DependencyIterator();
+    DependencyIterator(const DependencyIterator&);
+    DependencyIterator& operator=(const DependencyIterator&);
+
+    ELF::Dyn* dynamic_;
+    const char* strtab_;
+    const char* dep_name_;
+  };
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_SHARED_LIBRARY_H
\ No newline at end of file
diff --git a/sources/android/crazy_linker/src/crazy_linker_system.cpp b/sources/android/crazy_linker/src/crazy_linker_system.cpp
new file mode 100644
index 0000000..fde5053
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_system.cpp
@@ -0,0 +1,112 @@
+// 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_system.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "crazy_linker_util.h"
+
+// Note: unit-testing support files are in crazy_linker_files_mock.cpp
+
+namespace crazy {
+
+#ifndef UNIT_TESTS
+
+bool FileDescriptor::OpenReadOnly(const char* path) {
+  Close();
+  fd_ = TEMP_FAILURE_RETRY(::open(path, O_RDONLY));
+  return (fd_ != -1);
+}
+
+bool FileDescriptor::OpenReadWrite(const char* path) {
+  Close();
+  fd_ = TEMP_FAILURE_RETRY(::open(path, O_RDWR));
+  return (fd_ != -1);
+}
+
+int FileDescriptor::Read(void* buffer, size_t buffer_size) {
+  return TEMP_FAILURE_RETRY(::read(fd_, buffer, buffer_size));
+}
+
+int FileDescriptor::SeekTo(off_t offset) {
+  return ::lseek(fd_, offset, SEEK_SET);
+}
+
+void* FileDescriptor::Map(void* address,
+                          size_t length,
+                          int prot,
+                          int flags,
+                          off_t offset) {
+  return ::mmap(address, length, prot, flags, fd_, offset);
+}
+
+void FileDescriptor::Close() {
+  if (fd_ != -1) {
+    int old_errno = errno;
+    TEMP_FAILURE_RETRY(close(fd_));
+    errno = old_errno;
+    fd_ = -1;
+  }
+}
+
+const char* GetEnv(const char* var_name) { return ::getenv(var_name); }
+
+String GetCurrentDirectory() {
+  String result;
+  size_t capacity = 128;
+  for (;;) {
+    result.Resize(capacity);
+    if (getcwd(&result[0], capacity))
+      break;
+    capacity *= 2;
+  }
+  return result;
+}
+
+bool PathExists(const char* path) {
+  struct stat st;
+  if (TEMP_FAILURE_RETRY(stat(path, &st)) < 0)
+    return false;
+
+  return S_ISREG(st.st_mode) || S_ISDIR(st.st_mode);
+}
+
+bool PathIsFile(const char* path) {
+  struct stat st;
+  if (TEMP_FAILURE_RETRY(stat(path, &st)) < 0)
+    return false;
+
+  return S_ISREG(st.st_mode);
+}
+
+#endif  // !UNIT_TESTS
+
+// Returns true iff |lib_name| corresponds to one of the NDK-exposed
+// system libraries.
+bool IsSystemLibrary(const char* lib_name) {
+  static const char* const kSystemLibs[] = {
+      "libandroid.so",   "libc.so",         "libdl.so",     "libjnigraphics.so",
+      "liblog.so",       "libm.so",         "libstdc++.so", "libz.so",
+      "libEGL.so",       "libGLESv1_CM.so", "libGLESv2.so", "libGLESv3.so",
+      "libOpenMAXAL.so", "libOpenSLES.so", };
+  const size_t kSize = sizeof(kSystemLibs) / sizeof(kSystemLibs[0]);
+  const char* base_name = ::strchr(lib_name, '/');
+  if (!base_name)
+    base_name = lib_name;
+  else
+    base_name += 1;
+
+  for (size_t n = 0; n < kSize; ++n) {
+    if (!strcmp(kSystemLibs[n], base_name))
+      return true;
+  }
+  return false;
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_system.h b/sources/android/crazy_linker/src/crazy_linker_system.h
new file mode 100644
index 0000000..65a2859
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_system.h
@@ -0,0 +1,85 @@
+// 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.
+
+#ifndef CRAZY_LINKER_SYSTEM_H
+#define CRAZY_LINKER_SYSTEM_H
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "crazy_linker_util.h"  // for String
+
+// System abstraction used by the crazy linker.
+// This is used to make unit testing easier without using tons of
+// dependency injection in the rest of the code base.
+//
+// In a nutshell: in a normal build, this will wrap normal open() / read()
+// calls. During unit testing, everything is mocked, see
+// crazy_linker_system_mock.cpp
+
+namespace crazy {
+
+enum FileOpenMode {
+  FILE_OPEN_READ_ONLY = 0,
+  FILE_OPEN_READ_WRITE,
+  FILE_OPEN_WRITE
+};
+
+// Scoping file descriptor class.
+class FileDescriptor {
+ public:
+#ifdef UNIT_TESTS
+#define kEmptyFD NULL
+  typedef void* HandleType;
+#else
+#define kEmptyFD (-1)
+  typedef int HandleType;
+#endif
+
+  FileDescriptor() : fd_(kEmptyFD) {}
+
+  FileDescriptor(const char* path) : fd_(kEmptyFD) { OpenReadOnly(path); }
+
+  ~FileDescriptor() { Close(); }
+
+  bool IsOk() const { return fd_ != kEmptyFD; }
+  HandleType Get() const { return fd_; }
+  bool OpenReadOnly(const char* path);
+  bool OpenReadWrite(const char* path);
+  int Read(void* buffer, size_t buffer_size);
+  int SeekTo(off_t offset);
+  void* Map(void* address,
+            size_t length,
+            int prot_flags,
+            int flags,
+            off_t offset);
+  void Close();
+
+ private:
+  HandleType fd_;
+};
+
+// Returns true iff a given file path exists.
+bool PathExists(const char* path_name);
+
+// Returns true iff a given path is a regular file (or link to a regular
+// file).
+bool PathIsFile(const char* path_name);
+
+// Returns the current directory, as a string.
+String GetCurrentDirectory();
+
+// Returns the value of a given environment variable.
+const char* GetEnv(const char* var_name);
+
+// Returns true iff |lib_name| corresponds to one of the NDK-exposed
+// system libraries.
+bool IsSystemLibrary(const char* lib_name);
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_SYSTEM_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_system_mock.cpp b/sources/android/crazy_linker/src/crazy_linker_system_mock.cpp
new file mode 100644
index 0000000..90dd604
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_system_mock.cpp
@@ -0,0 +1,387 @@
+// 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_system_mock.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "crazy_linker_util.h"
+#include "crazy_linker_system.h"
+
+// Unit-testing support code. This should never be compiled into
+// the production code.
+
+namespace {
+
+using crazy::String;
+using crazy::Vector;
+
+void Panic(const char* msg, ...) {
+  va_list args;
+  fprintf(stderr, "PANIC: ");
+  va_start(args, msg);
+  vfprintf(stderr, msg, args);
+  va_end(args);
+  fprintf(stderr, "\n");
+  exit(1);
+}
+
+// Models a simple list of pointers to objects, which are owned by the
+// list itself.
+template <class T>
+class List {
+ public:
+  List() : entries_() {}
+
+  ~List() { Reset(); }
+
+  void Reset() {
+    for (size_t n = 0; n < entries_.GetCount(); ++n) {
+      T* entry = entries_[n];
+      delete entry;
+      entries_[n] = NULL;
+    }
+    entries_.Resize(0);
+  }
+
+  // Add an item to the list, transfer ownership to it.
+  void PushBack(T* item) { entries_.PushBack(item); }
+
+  size_t GetCount() const { return entries_.GetCount(); }
+
+  T* operator[](size_t index) { return entries_[index]; }
+
+ private:
+  crazy::Vector<T*> entries_;
+};
+
+// Models a single file entry in a mock file system.
+class MockFileEntry {
+ public:
+  MockFileEntry() : path_(), data_() {}
+
+  ~MockFileEntry() {}
+
+  const char* GetPath() const { return path_.c_str(); }
+  const char* GetData() const { return data_.c_str(); }
+  size_t GetDataSize() const { return data_.size(); }
+
+  void SetPath(const char* path) { path_.Assign(path); }
+
+  void SetData(const char* data, size_t data_size) {
+    data_.Assign(data, data_size);
+  }
+
+ private:
+  crazy::String path_;
+  crazy::String data_;
+};
+
+// Models a single mock environment variable value.
+class MockEnvEntry {
+ public:
+  MockEnvEntry(const char* var_name, const char* var_value)
+      : var_name_(var_name), var_value_(var_value) {}
+
+  const String& GetName() const { return var_name_; }
+  const String& GetValue() const { return var_value_; }
+
+ private:
+  crazy::String var_name_;
+  crazy::String var_value_;
+};
+
+class MockSystem {
+ public:
+  MockSystem() : files_(), environment_() {}
+
+  ~MockSystem() { Reset(); }
+
+  void SetCurrentDir(const char* path) { current_dir_ = path; }
+
+  String GetCurrentDir() const { return current_dir_; }
+
+  void AddFileEntry(MockFileEntry* entry) { files_.PushBack(entry); }
+
+  void AddEnvEntry(MockEnvEntry* entry) { environment_.PushBack(entry); }
+
+  MockFileEntry* FindFileEntry(const char* path) {
+    for (size_t n = 0; n < files_.GetCount(); ++n) {
+      MockFileEntry* entry = files_[n];
+      if (entry->GetPath() && !strcmp(path, entry->GetPath()))
+        return entry;
+    }
+    return NULL;
+  }
+
+  MockEnvEntry* FindEnvEntry(const char* var_name) {
+    for (size_t n = 0; n < environment_.GetCount(); ++n) {
+      MockEnvEntry* entry = environment_[n];
+      if (!strcmp(entry->GetName().c_str(), var_name))
+        return entry;
+    }
+    return NULL;
+  }
+
+  void Reset() {
+    files_.Reset();
+    environment_.Reset();
+    current_dir_ = "/";
+  }
+
+  void Check() {
+    if (!active_)
+      Panic("No mock file system setup!");
+  }
+
+  void Activate() {
+    if (active_)
+      Panic("Double mock file system activation!");
+
+    active_ = true;
+  }
+
+  void Deactivate() {
+    if (!active_)
+      Panic("Double mock file system deactivation!");
+
+    active_ = false;
+  }
+
+ private:
+  List<MockFileEntry> files_;
+  List<MockEnvEntry> environment_;
+  String current_dir_;
+  bool active_;
+};
+
+static MockSystem s_mock_fs;
+
+class MockFileHandle {
+ public:
+  MockFileHandle(MockFileEntry* entry) : entry_(entry), offset_(0) {}
+  ~MockFileHandle() {}
+
+  bool IsEof() const { return offset_ >= entry_->GetDataSize(); }
+
+  bool GetString(char* buffer, size_t buffer_size) {
+    const char* data = entry_->GetData();
+    size_t data_size = entry_->GetDataSize();
+
+    if (offset_ >= data_size || buffer_size == 0)
+      return false;
+
+    while (buffer_size > 1) {
+      char ch = data[offset_++];
+      *buffer++ = ch;
+      buffer_size--;
+      if (ch == '\n')
+        break;
+    }
+    *buffer = '\0';
+    return true;
+  }
+
+  int Read(void* buffer, size_t buffer_size) {
+    if (buffer_size == 0)
+      return 0;
+
+    const char* data = entry_->GetData();
+    size_t data_size = entry_->GetDataSize();
+
+    size_t avail = data_size - offset_;
+    if (avail == 0)
+      return 0;
+
+    if (buffer_size > avail)
+      buffer_size = avail;
+
+    ::memcpy(buffer, data + offset_, buffer_size);
+    offset_ += buffer_size;
+
+    return static_cast<int>(buffer_size);
+  }
+
+  int SeekTo(off_t offset) {
+    if (offset < 0) {
+      errno = EINVAL;
+      return -1;
+    }
+
+    const char* data = entry_->GetData();
+    size_t data_size = entry_->GetDataSize();
+
+    if (offset > static_cast<off_t>(data_size)) {
+      errno = EINVAL;
+      return -1;
+    }
+
+    offset_ = static_cast<size_t>(offset);
+    return 0;
+  }
+
+  void* Map(void* address, size_t length, int prot, int flags, off_t offset) {
+    const char* data = entry_->GetData();
+    size_t data_size = entry_->GetDataSize();
+    if (offset_ >= data_size) {
+      errno = EINVAL;
+      return MAP_FAILED;
+    }
+
+    // Allocate an anonymous memory mapping, then copy the file contents
+    // into it.
+    void* map = mmap(address, length, PROT_WRITE, MAP_ANONYMOUS, -1, 0);
+    if (map == MAP_FAILED) {
+      return map;
+    }
+
+    size_t avail = data_size - offset_;
+    if (avail > length)
+      avail = length;
+
+    ::memcpy(map, data + offset_, avail);
+
+    // Restore desired protection after the write.
+    mprotect(map, length, prot);
+
+    // Done.
+    return map;
+  }
+
+ private:
+  MockFileEntry* entry_;
+  size_t offset_;
+};
+
+MockFileHandle* NewMockFileHandle(const char* path,
+                                  crazy::FileOpenMode open_mode) {
+  // Check that a mock file system instance is active.
+  s_mock_fs.Check();
+
+  // TODO(digit): Add write support.
+  if (open_mode != crazy::FILE_OPEN_READ_ONLY)
+    Panic("Unsupported open mode (%d): %s", open_mode, path);
+
+  MockFileEntry* entry = s_mock_fs.FindFileEntry(path);
+  if (!entry)
+    Panic("Missing mock file entry: %s", path);
+
+  return new MockFileHandle(entry);
+}
+
+}  // namespace
+
+namespace crazy {
+
+#ifdef UNIT_TESTS
+
+bool PathExists(const char* path) {
+  s_mock_fs.Check();
+  return s_mock_fs.FindFileEntry(path) != NULL;
+}
+
+bool PathIsFile(const char* path) {
+  // TODO(digit): Change this when support for mock directories is added.
+  return PathExists(path);
+}
+
+String GetCurrentDirectory() {
+  s_mock_fs.Check();
+  return s_mock_fs.GetCurrentDir();
+}
+
+const char* GetEnv(const char* var_name) {
+  s_mock_fs.Check();
+  MockEnvEntry* entry = s_mock_fs.FindEnvEntry(var_name);
+  if (!entry)
+    return NULL;
+  else
+    return entry->GetValue().c_str();
+}
+
+bool FileDescriptor::OpenReadOnly(const char* path) {
+  fd_ = NewMockFileHandle(path, FILE_OPEN_READ_ONLY);
+  return fd_ != NULL;
+}
+
+bool FileDescriptor::OpenReadWrite(const char* path) {
+  // NOT IMPLEMENTED ON PURPOSE.
+  return false;
+}
+
+void FileDescriptor::Close() {
+  if (fd_) {
+    MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
+    delete handle;
+    fd_ = NULL;
+  }
+}
+
+int FileDescriptor::Read(void* buffer, size_t buffer_size) {
+  if (!fd_) {
+    errno = EBADF;
+    return -1;
+  }
+  MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
+  return handle->Read(buffer, buffer_size);
+}
+
+int FileDescriptor::SeekTo(off_t offset) {
+  if (!fd_) {
+    errno = EBADF;
+    return -1;
+  }
+  MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
+  return handle->SeekTo(offset);
+}
+
+void* FileDescriptor::Map(void* address,
+                          size_t length,
+                          int prot,
+                          int flags,
+                          off_t offset) {
+  if (!fd_ || (offset & 4095) != 0) {
+    errno = EINVAL;
+    return MAP_FAILED;
+  }
+  MockFileHandle* handle = reinterpret_cast<MockFileHandle*>(fd_);
+  return handle->Map(address, length, prot, flags, offset);
+}
+
+SystemMock::SystemMock() { s_mock_fs.Activate(); }
+
+SystemMock::~SystemMock() {
+  s_mock_fs.Deactivate();
+  s_mock_fs.Reset();
+}
+
+void SystemMock::AddRegularFile(const char* path,
+                                const char* data,
+                                size_t data_size) {
+  s_mock_fs.Check();
+
+  MockFileEntry* entry = new MockFileEntry();
+  entry->SetPath(path);
+  entry->SetData(data, data_size);
+
+  s_mock_fs.AddFileEntry(entry);
+}
+
+void SystemMock::AddEnvVariable(const char* var_name, const char* var_value) {
+  s_mock_fs.Check();
+
+  MockEnvEntry* env = new MockEnvEntry(var_name, var_value);
+  s_mock_fs.AddEnvEntry(env);
+}
+
+void SystemMock::SetCurrentDir(const char* path) {
+  s_mock_fs.Check();
+  s_mock_fs.SetCurrentDir(path);
+}
+
+#endif  // UNIT_TESTS
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_system_mock.h b/sources/android/crazy_linker/src/crazy_linker_system_mock.h
new file mode 100644
index 0000000..6e99361
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_system_mock.h
@@ -0,0 +1,33 @@
+// 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.
+
+#ifndef CRAZY_LINKER_SYSTEM_MOCK_H
+#define CRAZY_LINKER_SYSTEM_MOCK_H
+
+#include <stdint.h>
+
+namespace crazy {
+
+class SystemMock {
+ public:
+  // Create a new mock system instance and make ScopedFileDescriptor use it.
+  // There can be only one mock system active at a given time.
+  SystemMock();
+
+  // Destroy a mock system instance.
+  ~SystemMock();
+
+  // Add a regular file to the mock file system. |path| is the entry's
+  // path, and |data| and |data_size| are the data there. The data must
+  // stay valid until the mock file system is destroyed.
+  void AddRegularFile(const char* path, const char* data, size_t data_size);
+
+  void AddEnvVariable(const char* var_name, const char* var_value);
+
+  void SetCurrentDir(const char* path);
+};
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_SYSTEM_MOCK_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_system_unittest.cpp b/sources/android/crazy_linker/src/crazy_linker_system_unittest.cpp
new file mode 100644
index 0000000..632cca4
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_system_unittest.cpp
@@ -0,0 +1,60 @@
+// 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_system.h"
+
+#include <minitest/minitest.h>
+#include <stdlib.h>
+#include "crazy_linker_system_mock.h"
+
+namespace crazy {
+
+TEST(System, SingleFile) {
+  static const char kPath[] = "/tmp/foo/bar";
+
+  static const char kString[] = "Hello World";
+  const size_t kStringLen = sizeof(kString) - 1;
+
+  SystemMock sys;
+  sys.AddRegularFile(kPath, kString, kStringLen);
+
+  char buff2[kStringLen + 10];
+  FileDescriptor fd(kPath);
+
+  EXPECT_EQ(kStringLen, fd.Read(buff2, sizeof(buff2)));
+  buff2[kStringLen] = '\0';
+  EXPECT_STREQ(kString, buff2);
+}
+
+TEST(System, PathExists) {
+  SystemMock sys;
+  sys.AddRegularFile("/tmp/foo", "FOO", 3);
+
+  EXPECT_TRUE(PathExists("/tmp/foo"));
+}
+
+TEST(System, PathExistsWithBadPath) {
+  SystemMock sys;
+  EXPECT_FALSE(PathExists("/tmp/foo"));
+}
+
+TEST(System, IsSystemLibrary) {
+  SystemMock sys;
+  static const struct {
+    const char* name;
+    bool success;
+  } kData[] = {{"libEGL.so", true},       {"libGLESv1_CM.so", true},
+               {"libGLESv1.so", false},   {"libGLESv2.so", true},
+               {"libOpenMAXAL.so", true}, {"libOpenSLES.so", true},
+               {"libandroid.so", true},   {"libc.so", true},
+               {"libdl.so", true},        {"libjnigraphics.so", true},
+               {"libm.so", true},         {"libstdc++.so", true},
+               {"libstlport.so", false},  {"libz.so", true}, };
+  for (size_t n = 0; n < ARRAY_LEN(kData); ++n) {
+    TEST_TEXT << "Checking " << kData[n].name;
+    EXPECT_EQ(kData[n].success, IsSystemLibrary(kData[n].name));
+  }
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_thread.cpp b/sources/android/crazy_linker/src/crazy_linker_thread.cpp
new file mode 100644
index 0000000..d2df2b0
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_thread.cpp
@@ -0,0 +1,80 @@
+// 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_thread.h"
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace {
+
+static pthread_key_t s_thread_key;
+static pthread_once_t s_once = PTHREAD_ONCE_INIT;
+
+static void ThreadDataDestroy(void* data) { free(data); }
+
+static void InitThreadKey() {
+  pthread_key_create(&s_thread_key, ThreadDataDestroy);
+}
+
+}  // namespace
+
+namespace crazy {
+
+void ThreadData::Init() {
+  dlerror_ = dlerror_buffers_[0];
+  dlerror_[0] = '\0';
+}
+
+void ThreadData::SwapErrorBuffers() {
+  if (dlerror_ == dlerror_buffers_[0])
+    dlerror_ = dlerror_buffers_[1];
+  else
+    dlerror_ = dlerror_buffers_[0];
+  dlerror_[0] = '\0';
+}
+
+void ThreadData::SetErrorArgs(const char* fmt, va_list args) {
+  if (fmt == NULL) {
+    dlerror_[0] = '\0';
+    return;
+  }
+  vsnprintf(dlerror_, kBufferSize, fmt, args);
+}
+
+void ThreadData::AppendErrorArgs(const char* fmt, va_list args) {
+  if (fmt == NULL)
+    return;
+  size_t len = strlen(dlerror_);
+  vsnprintf(dlerror_ + len, kBufferSize - len, fmt, args);
+}
+
+ThreadData* GetThreadDataFast() {
+  return reinterpret_cast<ThreadData*>(pthread_getspecific(s_thread_key));
+}
+
+ThreadData* GetThreadData() {
+  pthread_once(&s_once, InitThreadKey);
+  ThreadData* data = GetThreadDataFast();
+  if (!data) {
+    data = reinterpret_cast<ThreadData*>(calloc(sizeof(*data), 1));
+    data->Init();
+    pthread_setspecific(s_thread_key, data);
+  }
+  return data;
+}
+
+// Set the linker error string for the current thread.
+void SetLinkerErrorString(const char* str) { GetThreadData()->SetError(str); }
+
+// Set the formatted linker error for the current thread.
+void SetLinkerError(const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  GetThreadData()->SetErrorArgs(fmt, args);
+  va_end(args);
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_thread.h b/sources/android/crazy_linker/src/crazy_linker_thread.h
new file mode 100644
index 0000000..c84c5ee
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_thread.h
@@ -0,0 +1,81 @@
+// 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.
+
+#ifndef CRAZY_LINKER_THREAD_H
+#define CRAZY_LINKER_THREAD_H
+
+#include <stdarg.h>
+#include <stddef.h>
+
+namespace crazy {
+
+// Per-thread context used during crazy linker operations.
+class ThreadData {
+
+ public:
+  ThreadData() {}
+
+  // Init new ThreadData instance.
+  void Init();
+
+  // Return the current error message. This also clears the internal
+  // error message, which means that the next call to this method
+  // will return a pointer to an empty string unless AppendError()
+  // was called.
+  const char* GetError() const { return dlerror_; }
+
+  // Swap the error buffers.
+  void SwapErrorBuffers();
+
+  // Set message string in current dlerror buffer.
+  void SetError(const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    SetErrorArgs(fmt, args);
+    va_end(args);
+  }
+
+  void SetErrorArgs(const char* fmt, va_list args);
+
+  // Append message string to current dlerror buffer.
+  void AppendError(const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    AppendErrorArgs(fmt, args);
+    va_end(args);
+  }
+
+  void AppendErrorArgs(const char* fmt, va_list args);
+
+ private:
+  // Pointer to the current dlerror buffer. This points to one
+  // of the dlerror_buffers[] arrays, swapped on each dlerror()
+  // call.
+  char* dlerror_;
+
+  // Size of each dlerror message buffer size.
+  static const size_t kBufferSize = 512;
+
+  // Two buffers used to store dlerror messages.
+  char dlerror_buffers_[2][kBufferSize];
+};
+
+// Retrieves the ThreadData structure for the current thread.
+// The first time this is called on a given thread, this creates
+// a fresh new object, so this should never return NULL.
+ThreadData* GetThreadData();
+
+// Faster variant that should only be called when GetThreadData() was
+// called at least once on the current thread.
+ThreadData* GetThreadDataFast();
+
+// Set the linker error string for the current thread.
+void SetLinkerErrorString(const char* str);
+
+// Set the formatted linker error for the current thread.
+void SetLinkerError(const char* fmt, ...);
+
+}  // namespace crazy;
+
+#endif  // CRAZY_LINKER_THREAD_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_thread_unittest.cpp b/sources/android/crazy_linker/src/crazy_linker_thread_unittest.cpp
new file mode 100644
index 0000000..4db09b1
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_thread_unittest.cpp
@@ -0,0 +1,124 @@
+// 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_thread.h"
+
+#include <minitest/minitest.h>
+
+namespace crazy {
+
+TEST(Thread, GetThreadData) {
+  ThreadData* data = GetThreadData();
+  TEST_TEXT << "Checking first GetThreadData() call";
+  EXPECT_TRUE(data);
+  EXPECT_EQ(data, GetThreadData());
+  EXPECT_EQ(data, GetThreadDataFast());
+}
+
+TEST(Thread, GetErrorEmpty) {
+  ThreadData* data = GetThreadData();
+  const char* error = data->GetError();
+  EXPECT_TRUE(error);
+  EXPECT_STREQ("", error);
+}
+
+TEST(Thread, SetError) {
+  ThreadData* data = GetThreadData();
+  data->SetError("Hello");
+  data->SetError("World");
+  EXPECT_STREQ("World", data->GetError());
+}
+
+TEST(Thread, SetErrorNull) {
+  ThreadData* data = GetThreadData();
+  data->SetError("Hello");
+  data->SetError(NULL);
+  EXPECT_STREQ("", data->GetError());
+}
+
+TEST(Thread, GetError) {
+  ThreadData* data = GetThreadData();
+  data->SetError("Hello");
+
+  const char* error = data->GetError();
+  EXPECT_STREQ("Hello", error);
+
+  error = data->GetError();
+  EXPECT_STREQ("Hello", error);
+}
+
+TEST(Thread, SwapErrorBuffers) {
+  ThreadData* data = GetThreadData();
+  data->SetError("Hello");
+  EXPECT_STREQ("Hello", data->GetError());
+
+  data->SwapErrorBuffers();
+  EXPECT_STREQ("", data->GetError());
+
+  data->SetError("World");
+  EXPECT_STREQ("World", data->GetError());
+
+  data->SwapErrorBuffers();
+  EXPECT_STREQ("", data->GetError());
+}
+
+TEST(Thread, AppendErrorTwice) {
+  ThreadData* data = GetThreadData();
+  data->SetError(NULL);
+  data->AppendError("Hello");
+  EXPECT_STREQ("Hello", data->GetError());
+
+  data->AppendError(" World");
+  EXPECT_STREQ("Hello World", data->GetError());
+}
+
+TEST(Thread, AppendErrorFull) {
+  const size_t kMaxCount = 1000;
+  ThreadData* data = GetThreadData();
+  data->SetError(NULL);
+
+  for (size_t n = 0; n < kMaxCount; ++n)
+    data->AppendError("0123456789");
+
+  const char* error = data->GetError();
+  size_t error_len = strlen(error);
+
+  EXPECT_GT(0, error_len);
+  EXPECT_LT(kMaxCount * 10, error_len);
+
+  for (size_t n = 0; n < error_len; ++n) {
+    TEST_TEXT << "Checking error[" << n << "]";
+    EXPECT_EQ('0' + (n % 10), error[n]);
+  }
+}
+
+TEST(Thread, AppendErrorNull) {
+  ThreadData* data = GetThreadData();
+  data->SetError("Hello");
+  data->AppendError(NULL);
+  data->AppendError(" World");
+  EXPECT_STREQ("Hello World", data->GetError());
+}
+
+TEST(Thread, SetLinkerErrorString) {
+  ThreadData* data = GetThreadData();
+
+  SetLinkerErrorString("Hello World");
+  EXPECT_STREQ("Hello World", data->GetError());
+
+  SetLinkerErrorString(NULL);
+  EXPECT_STREQ("", data->GetError());
+}
+
+TEST(Thread, SetLinkerError) {
+  ThreadData* data = GetThreadData();
+
+  SetLinkerError("%s %s!", "Hi", "Captain");
+  EXPECT_STREQ("Hi Captain!", data->GetError());
+
+  SetLinkerError("Woosh");
+  EXPECT_STREQ("Woosh", data->GetError());
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_util.cpp b/sources/android/crazy_linker/src/crazy_linker_util.cpp
new file mode 100644
index 0000000..0654d78
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_util.cpp
@@ -0,0 +1,124 @@
+// 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_util.h"
+
+#include <stdio.h>
+
+namespace crazy {
+
+// Return the base name from a file path. Important: this is a pointer
+// into the original string.
+// static
+const char* GetBaseNamePtr(const char* path) {
+  const char* p = strrchr(path, '/');
+  if (!p)
+    return path;
+  else
+    return p + 1;
+}
+
+// static
+const char String::kEmpty[] = "";
+
+String::String() { Init(); }
+
+String::String(const String& other) {
+  Init();
+  Assign(other.ptr_, other.size_);
+}
+
+String::String(const char* str) {
+  Init();
+  Assign(str, strlen(str));
+}
+
+String::String(char ch) {
+  Init();
+  Assign(&ch, 1);
+}
+
+String::~String() {
+  if (ptr_ != const_cast<char*>(kEmpty)) {
+    free(ptr_);
+    ptr_ = const_cast<char*>(kEmpty);
+  }
+}
+
+String::String(const char* str, size_t len) {
+  Init();
+  Assign(str, len);
+}
+
+void String::Assign(const char* str, size_t len) {
+  Resize(len);
+  if (len > 0) {
+    memcpy(ptr_, str, len);
+    ptr_[len] = '\0';
+    size_ = len;
+  }
+}
+
+void String::Append(const char* str, size_t len) {
+  if (len > 0) {
+    size_t old_size = size_;
+    Resize(size_ + len);
+    memcpy(ptr_ + old_size, str, len);
+  }
+}
+
+void String::Resize(size_t new_size) {
+  if (new_size > capacity_) {
+    size_t new_capacity = capacity_;
+    while (new_capacity < new_size) {
+      new_capacity += (new_capacity >> 1) + 16;
+    }
+    Reserve(new_capacity);
+  }
+
+  if (new_size > size_)
+    memset(ptr_ + size_, '\0', new_size - size_);
+
+  size_ = new_size;
+  if (ptr_ != kEmpty)
+    ptr_[size_] = '\0';
+}
+
+void String::Reserve(size_t new_capacity) {
+  char* old_ptr = (ptr_ == const_cast<char*>(kEmpty)) ? NULL : ptr_;
+  // Always allocate one more byte for the trailing \0
+  ptr_ = reinterpret_cast<char*>(realloc(old_ptr, new_capacity + 1));
+  ptr_[new_capacity] = '\0';
+  capacity_ = new_capacity;
+
+  if (size_ > new_capacity)
+    size_ = new_capacity;
+}
+
+#if 0
+String Format(const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  String result(FormatArgs(fmt, args));
+  va_end(args);
+  return result;
+}
+
+String FormatArgs(const char* fmt, va_list args) {
+  String result;
+  for (;;) {
+    va_list args2;
+    va_copy(args2, args);
+    int ret = vsnprintf(&result[0], result.capacity(), fmt, args2);
+    va_end(args2);
+    if (static_cast<size_t>(ret) <= result.capacity())
+      break;
+
+    result.Resize(static_cast<size_t>(ret));
+  }
+  return result;
+}
+#endif
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_util.h b/sources/android/crazy_linker/src/crazy_linker_util.h
new file mode 100644
index 0000000..2b08a85
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_util.h
@@ -0,0 +1,302 @@
+// 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.
+
+#ifndef CRAZY_LINKER_UTIL_H
+#define CRAZY_LINKER_UTIL_H
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+
+namespace crazy {
+
+// Helper macro to loop around EINTR errors in syscalls.
+#define HANDLE_EINTR(expr) TEMP_FAILURE_RETRY(expr)
+
+// Helper macro to tag unused variables. Use in the declaration, between
+// the type and name, as in:
+//     int CRAZY_UNUSED my_var = 0;
+#define CRAZY_UNUSED __attribute__((unused))
+
+// Helper scoped pointer class.
+template <class T>
+class ScopedPtr {
+ public:
+  ScopedPtr() : ptr_(NULL) {}
+  explicit ScopedPtr(T* ptr) : ptr_(ptr) {}
+  ~ScopedPtr() { Reset(NULL); }
+
+  T* Release() {
+    T* ret = ptr_;
+    ptr_ = NULL;
+    return ret;
+  }
+
+  void Reset(T* ptr) {
+    if (ptr_)
+      delete ptr_;
+    ptr_ = ptr;
+  }
+
+  T* Get() { return ptr_; }
+  T& operator*() { return *ptr_; }
+  T* operator->() { return ptr_; }
+
+ private:
+  T* ptr_;
+};
+
+// Return the base name from a file path. Important: this is a pointer
+// into the original string.
+const char* GetBaseNamePtr(const char* path);
+
+// Helper class used to implement a string. Similar to std::string
+// without all the crazy iterator / iostream stuff.
+//
+// Required because crazy linker should only link against the system
+// libstdc++ that only provides new/delete.
+//
+class String {
+ public:
+  String();
+  String(const char* str, size_t len);
+  String(const String& other);
+  explicit String(const char* str);
+  explicit String(char ch);
+
+  ~String();
+
+  const char* c_str() const { return ptr_; }
+  char* ptr() { return ptr_; }
+  size_t size() const { return size_; }
+  size_t capacity() const { return capacity_; }
+
+  bool IsEmpty() const { return size_ == 0; }
+
+  char& operator[](size_t index) { return ptr_[index]; }
+
+  String& operator=(const String& other) {
+    Assign(other.ptr_, other.size_);
+    return *this;
+  }
+
+  String& operator=(const char* str) {
+    Assign(str, strlen(str));
+    return *this;
+  }
+
+  String& operator=(char ch) {
+    Assign(&ch, 1);
+    return *this;
+  }
+
+  String& operator+=(const String& other) {
+    Append(other);
+    return *this;
+  }
+
+  String& operator+=(const char* str) {
+    Append(str, strlen(str));
+    return *this;
+  }
+
+  String& operator+=(char ch) {
+    Append(&ch, 1);
+    return *this;
+  }
+
+  void Resize(size_t new_size);
+
+  void Reserve(size_t new_capacity);
+
+  void Assign(const char* str, size_t len);
+
+  void Assign(const String& other) { Assign(other.ptr_, other.size_); }
+
+  void Assign(const char* str) { Assign(str, strlen(str)); }
+
+  void Append(const char* str, size_t len);
+
+  void Append(const String& other) { Append(other.ptr_, other.size_); }
+
+  void Append(const char* str) { Append(str, strlen(str)); }
+
+ private:
+  void Init(void) {
+    ptr_ = const_cast<char*>(kEmpty);
+    size_ = 0;
+    capacity_ = 0;
+  }
+
+  static const char kEmpty[];
+
+  char* ptr_;
+  size_t size_;
+  size_t capacity_;
+};
+
+// Helper template used to implement a simple vector or POD-struct items.
+// I.e. this uses memmove() to move items during insertion / removal.
+//
+// Required because crazy linker should only link against the system
+// libstdc++ which only provides new/delete.
+//
+template <class T>
+class Vector {
+ public:
+  Vector() : items_(0), count_(0), capacity_(0) {}
+  ~Vector() { free(items_); }
+
+  T& operator[](size_t index) { return items_[index]; }
+
+  bool IsEmpty() const { return count_ == 0; }
+
+  void PushBack(T item) { InsertAt(static_cast<int>(count_), item); }
+
+  T PopFirst() {
+    T result = items_[0];
+    RemoveAt(0);
+    return result;
+  }
+
+  T PopLast() {
+    T result = items_[count_ - 1];
+    Resize(count_ - 1);
+    return result;
+  }
+
+  void Remove(T item) {
+    int index = IndexOf(item);
+    if (index >= 0)
+      RemoveAt(index);
+  }
+
+  void InsertAt(int index, T item);
+
+  void RemoveAt(int index);
+
+  int IndexOf(T item) const;
+
+  bool Has(T item) const { return IndexOf(item) >= 0; }
+
+  size_t GetCount() const { return count_; }
+
+  void Reserve(size_t new_capacity);
+
+  void Resize(size_t new_count);
+
+ private:
+  T* items_;
+  size_t count_;
+  size_t capacity_;
+};
+
+template <class T>
+int Vector<T>::IndexOf(T item) const {
+  for (size_t n = 0; n < count_; ++n) {
+    if (items_[n] == item)
+      return static_cast<int>(n);
+  }
+  return -1;
+}
+
+template <class T>
+void Vector<T>::InsertAt(int index, T item) {
+  if (count_ >= capacity_)
+    Reserve(capacity_ + (capacity_ >> 1) + 4);
+
+  if (index < 0)
+    index = 0;
+  size_t n = static_cast<size_t>(index);
+  if (n > count_)
+    n = count_;
+  else
+    memmove(items_ + n + 1, items_ + n, (count_ - n) * sizeof(T));
+
+  items_[n] = item;
+  count_++;
+}
+
+template <class T>
+void Vector<T>::RemoveAt(int index) {
+  if (index < 0)
+    return;
+
+  size_t n = static_cast<size_t>(index);
+  if (n >= count_)
+    return;
+
+  memmove(items_ + n, items_ + n + 1, (count_ - n - 1) * sizeof(T));
+  count_--;
+}
+
+template <class T>
+void Vector<T>::Reserve(size_t new_capacity) {
+  items_ = reinterpret_cast<T*>(realloc(items_, new_capacity * sizeof(T)));
+  capacity_ = new_capacity;
+  if (count_ > capacity_)
+    count_ = capacity_;
+}
+
+template <class T>
+void Vector<T>::Resize(size_t new_size) {
+  if (new_size > capacity_)
+    Reserve(new_size);
+
+  if (new_size > count_)
+    memset(items_ + count_, 0, (new_size - count_) * sizeof(T));
+
+  count_ = new_size;
+}
+
+// Helper template class to implement a set.
+// Given that the crazy linker doesn't expect to deal with hundreds
+// of libraries at the same time, implement it with a vector.
+template <class T>
+class Set {
+ public:
+  Set() : items_() {}
+  ~Set() {}
+
+  // Returns the number of items in the set.
+  size_t GetCount() const { return items_.GetCount(); }
+
+  bool IsEmpty() const { return items_.IsEmpty(); }
+
+  // Returns true iff the set contains a given item.
+  bool Has(T item) const { return items_.Has(item); }
+
+  // Add an item to the set. Returns false iff the item was already in it.
+  bool Add(T item);
+
+  // Delete an item from the set. Returns false iff the item was not in it.
+  bool Del(T item);
+
+ private:
+  Vector<T> items_;
+};
+
+template <class T>
+bool Set<T>::Add(T item) {
+  int idx = items_.IndexOf(item);
+  if (idx >= 0)
+    return false;
+
+  items_.PushBack(item);
+  return true;
+}
+
+template <class T>
+bool Set<T>::Del(T item) {
+  int idx = items_.IndexOf(item);
+  if (idx < 0)
+    return false;
+  items_.RemoveAt(idx);
+  return true;
+}
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_UTIL_H
diff --git a/sources/android/crazy_linker/src/crazy_linker_util_unittest.cpp b/sources/android/crazy_linker/src/crazy_linker_util_unittest.cpp
new file mode 100644
index 0000000..27affb8
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_util_unittest.cpp
@@ -0,0 +1,229 @@
+// 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_util.h"
+
+#include <minitest/minitest.h>
+
+namespace crazy {
+
+TEST(GetBaseNamePtr, Test) {
+  const char kString[] = "/tmp/foo";
+  EXPECT_EQ(kString + 5, GetBaseNamePtr(kString));
+}
+
+TEST(String, Empty) {
+  String s;
+  EXPECT_TRUE(s.IsEmpty());
+  EXPECT_EQ(0u, s.size());
+  EXPECT_EQ('\0', s.c_str()[0]);
+}
+
+TEST(String, CStringConstructor) {
+  String s("Simple");
+  EXPECT_STREQ("Simple", s.c_str());
+  EXPECT_EQ(6U, s.size());
+}
+
+TEST(String, CStringConstructorWithLength) {
+  String s2("Simple", 3);
+  EXPECT_STREQ("Sim", s2.c_str());
+  EXPECT_EQ(3U, s2.size());
+}
+
+TEST(String, CopyConstructor) {
+  String s1("Simple");
+  String s2(s1);
+
+  EXPECT_EQ(6U, s2.size());
+  EXPECT_STREQ(s2.c_str(), s1.c_str());
+}
+
+TEST(String, CharConstructor) {
+  String s('H');
+  EXPECT_EQ(1U, s.size());
+  EXPECT_STREQ("H", s.c_str());
+}
+
+TEST(String, AppendCString) {
+  String s("Foo");
+  s += "Bar";
+  EXPECT_STREQ("FooBar", s.c_str());
+  s += "Zoo";
+  EXPECT_STREQ("FooBarZoo", s.c_str());
+}
+
+TEST(String, AppendOther) {
+  String s("Foo");
+  String s2("Bar");
+  s += s2;
+  EXPECT_STREQ("FooBar", s.c_str());
+}
+
+TEST(String, ArrayAccess) {
+  String s("FooBar");
+  EXPECT_EQ('F', s[0]);
+  EXPECT_EQ('o', s[1]);
+  EXPECT_EQ('o', s[2]);
+  EXPECT_EQ('B', s[3]);
+  EXPECT_EQ('a', s[4]);
+  EXPECT_EQ('r', s[5]);
+  EXPECT_EQ('\0', s[6]);
+}
+
+TEST(String, Resize) {
+  String s("A very long string to have fun");
+  s.Resize(10);
+  EXPECT_EQ(10U, s.size());
+  EXPECT_STREQ("A very lon", s.c_str());
+}
+
+TEST(String, ResizeToZero) {
+  String s("Long string to force a dynamic allocation");
+  s.Resize(0);
+  EXPECT_EQ(0U, s.size());
+  EXPECT_STREQ("", s.c_str());
+}
+
+TEST(Vector, IsEmpty) {
+  Vector<void*> v;
+  EXPECT_TRUE(v.IsEmpty());
+}
+
+TEST(Vector, PushBack) {
+  Vector<int> v;
+  v.PushBack(12345);
+  EXPECT_FALSE(v.IsEmpty());
+  EXPECT_EQ(1U, v.GetCount());
+  EXPECT_EQ(12345, v[0]);
+}
+
+TEST(Vector, PushBack2) {
+  const int kMaxCount = 500;
+  Vector<int> v;
+  for (int n = 0; n < kMaxCount; ++n)
+    v.PushBack(n * 100);
+
+  EXPECT_FALSE(v.IsEmpty());
+  EXPECT_EQ(static_cast<size_t>(kMaxCount), v.GetCount());
+}
+
+TEST(Vector, At) {
+  const int kMaxCount = 500;
+  Vector<int> v;
+  for (int n = 0; n < kMaxCount; ++n)
+    v.PushBack(n * 100);
+
+  for (int n = 0; n < kMaxCount; ++n) {
+    TEST_TEXT << "Checking v[" << n << "]";
+    EXPECT_EQ(n * 100, v[n]);
+  }
+}
+
+TEST(Vector, IndexOf) {
+  const int kMaxCount = 500;
+  Vector<int> v;
+  for (int n = 0; n < kMaxCount; ++n)
+    v.PushBack(n * 100);
+
+  for (int n = 0; n < kMaxCount; ++n) {
+    TEST_TEXT << "Checking v.IndexOf(" << n * 100 << ")";
+    EXPECT_EQ(n, v.IndexOf(n * 100));
+  }
+}
+
+TEST(Vector, InsertAt) {
+  const int kMaxCount = 500;
+
+  for (size_t k = 0; k < kMaxCount; ++k) {
+    Vector<int> v;
+    for (int n = 0; n < kMaxCount; ++n)
+      v.PushBack(n * 100);
+
+    v.InsertAt(k, -1000);
+
+    EXPECT_EQ(kMaxCount + 1, v.GetCount());
+    for (int n = 0; n < v.GetCount(); ++n) {
+      TEST_TEXT << "Checking v[" << n << "]";
+      int expected;
+      if (n < k)
+        expected = n * 100;
+      else if (n == k)
+        expected = -1000;
+      else
+        expected = (n - 1) * 100;
+      EXPECT_EQ(expected, v[n]);
+    }
+  }
+}
+
+TEST(Vector, RemoveAt) {
+  const int kMaxCount = 500;
+
+  for (size_t k = 0; k < kMaxCount; ++k) {
+    Vector<int> v;
+    for (int n = 0; n < kMaxCount; ++n)
+      v.PushBack(n * 100);
+
+    v.RemoveAt(k);
+
+    EXPECT_EQ(kMaxCount - 1, v.GetCount());
+    for (int n = 0; n < kMaxCount - 1; ++n) {
+      TEST_TEXT << "Checking v[" << n << "]";
+      int expected = (n < k) ? (n * 100) : ((n + 1) * 100);
+      EXPECT_EQ(expected, v[n]);
+    }
+  }
+}
+
+TEST(Vector, PopFirst) {
+  const int kMaxCount = 500;
+  Vector<int> v;
+  for (int n = 0; n < kMaxCount; ++n)
+    v.PushBack(n * 100);
+
+  for (int n = 0; n < kMaxCount; ++n) {
+    int first = v.PopFirst();
+    TEST_TEXT << "Checking " << n << "-th PopFirst()";
+    EXPECT_EQ(n * 100, first);
+    EXPECT_EQ(kMaxCount - 1 - n, v.GetCount());
+  }
+  EXPECT_EQ(0u, v.GetCount());
+  EXPECT_TRUE(v.IsEmpty());
+}
+
+TEST(Set, Empty) {
+  Set<int> s;
+  EXPECT_TRUE(s.IsEmpty());
+  EXPECT_EQ(0U, s.GetCount());
+}
+
+TEST(Set, OneItem) {
+  Set<int> s;
+
+  EXPECT_FALSE(s.Has(0));
+
+  EXPECT_TRUE(s.Add(0));
+  EXPECT_TRUE(s.Has(0));
+  EXPECT_FALSE(s.IsEmpty());
+  EXPECT_EQ(1U, s.GetCount());
+}
+
+TEST(Set, ThreeItems) {
+  Set<int> s;
+
+  EXPECT_TRUE(s.Add(0));
+  EXPECT_TRUE(s.Add(1));
+  EXPECT_TRUE(s.Add(2));
+
+  EXPECT_FALSE(s.Has(-1));
+  EXPECT_TRUE(s.Has(0));
+  EXPECT_TRUE(s.Has(1));
+  EXPECT_TRUE(s.Has(2));
+  EXPECT_FALSE(s.Has(3));
+
+  EXPECT_EQ(3U, s.GetCount());
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_wrappers.cpp b/sources/android/crazy_linker/src/crazy_linker_wrappers.cpp
new file mode 100644
index 0000000..39cdf5f
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_wrappers.cpp
@@ -0,0 +1,279 @@
+// 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_wrappers.h"
+
+#include <dlfcn.h>
+#include <link.h>
+
+#include "crazy_linker_debug.h"
+#include "crazy_linker_globals.h"
+#include "crazy_linker_library_list.h"
+#include "crazy_linker_library_view.h"
+#include "crazy_linker_shared_library.h"
+#include "crazy_linker_thread.h"
+#include "crazy_linker_util.h"
+
+#ifdef __arm__
+// On ARM, this function is exported by the dynamic linker but never
+// declared in any official header. It is used at runtime to
+// find the base address of the .ARM.exidx section for the
+// shared library containing the instruction at |pc|, as well as
+// the number of 8-byte entries in that section, written into |*pcount|
+extern "C" _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr, int*);
+#else
+// On other architectures, this function is exported by the dynamic linker
+// but never declared in any official header. It is used at runtime to
+// iterate over all loaded libraries and call the |cb|. When the function
+// returns non-0, the iteration returns and the function returns its
+// value.
+extern "C" int dl_iterate_phdr(int (*cb)(dl_phdr_info* info,
+                                         size_t size,
+                                         void* data),
+                               void* data);
+#endif
+
+namespace crazy {
+
+namespace {
+
+#ifdef __arm__
+extern "C" int __cxa_atexit(void (*)(void*), void*, void*);
+
+// On ARM, this function is defined as a weak symbol by libc.so.
+// Unfortunately its address cannot be found through dlsym(), which will
+// always return NULL. To work-around this, define a copy here that does
+// exactly the same thing. The ARM EABI mandates the function's behaviour.
+// __cxa_atexit() is implemented by the C library, but not declared by
+// any official header. It's part of the low-level C++ support runtime.
+int __aeabi_atexit(void* object, void (*destructor)(void*), void* dso_handle) {
+  return __cxa_atexit(destructor, object, dso_handle);
+}
+#endif
+
+// Used to save the system dlerror() into our thread-specific data.
+void SaveSystemError() {
+  ThreadData* data = GetThreadData();
+  data->SetError(::dlerror());
+}
+
+char* WrapDlerror() {
+  ThreadData* data = GetThreadData();
+  const char* error = data->GetError();
+  data->SwapErrorBuffers();
+  // dlerror() returns a 'char*', but no sane client code should ever
+  // try to write to this location.
+  return const_cast<char*>(error);
+}
+
+void* WrapDlopen(const char* path, int mode) {
+  ScopedGlobalLock lock;
+
+  // NOTE: If |path| is NULL, the wrapper should return a handle
+  // corresponding to the current executable. This can't be a crazy
+  // library, so don't try to handle it with the crazy linker.
+  if (path) {
+    LibraryList* lib_list = Globals::GetLibraries();
+    Error error;
+    LibraryView* wrap = lib_list->LoadLibrary(path,
+                                              mode,
+                                              0U /* load_address */,
+                                              0U /* file_offset */,
+                                              Globals::GetSearchPaths(),
+                                              &error);
+    if (wrap)
+      return wrap;
+  }
+
+  // Try to load the executable with the system dlopen() instead.
+  ::dlerror();
+  void* system_lib = ::dlopen(path, mode);
+  if (system_lib == NULL) {
+    SaveSystemError();
+    return NULL;
+  }
+
+  LibraryView* wrap_lib = new LibraryView();
+  wrap_lib->SetSystem(system_lib, path ? path : "<executable>");
+  Globals::GetLibraries()->AddLibrary(wrap_lib);
+  return wrap_lib;
+}
+
+void* WrapDlsym(void* lib_handle, const char* symbol_name) {
+  LibraryView* wrap_lib = reinterpret_cast<LibraryView*>(lib_handle);
+
+  if (!symbol_name) {
+    SetLinkerError("dlsym: NULL symbol name");
+    return NULL;
+  }
+
+  if (!lib_handle) {
+    SetLinkerError("dlsym: NULL library handle");
+    return NULL;
+  }
+
+  // TODO(digit): Handle RTLD_DEFAULT / RTLD_NEXT
+  if (lib_handle == RTLD_DEFAULT || lib_handle == RTLD_NEXT) {
+    SetLinkerError("dlsym: RTLD_DEFAULT/RTLD_NEXT are not implemented!");
+    return NULL;
+  }
+
+  // NOTE: The Android dlsym() only looks inside the target library,
+  // while the GNU one will perform a breadth-first search into its
+  // dependency tree.
+
+  // This implementation performs a correct breadth-first search
+  // when |lib_handle| corresponds to a crazy library, except that
+  // it stops at system libraries that it depends on.
+
+  void* result = NULL;
+
+  if (wrap_lib->IsSystem()) {
+    // Note: the system dlsym() only looks into the target library,
+    // while the GNU linker performs a breadth-first search.
+    result = ::dlsym(wrap_lib->GetSystem(), symbol_name);
+    if (!result) {
+      SaveSystemError();
+      LOG("dlsym:%s: could not find symbol '%s' from system library\n%s",
+          wrap_lib->GetName(),
+          symbol_name,
+          GetThreadData()->GetError());
+    }
+    return result;
+  }
+
+  if (wrap_lib->IsCrazy()) {
+    ScopedGlobalLock lock;
+    LibraryList* lib_list = Globals::GetLibraries();
+    void* addr = lib_list->FindSymbolFrom(symbol_name, wrap_lib);
+    if (addr)
+      return addr;
+
+    SetLinkerError("dlsym: Could not find '%s' from library '%s'",
+                   symbol_name,
+                   wrap_lib->GetName());
+    return NULL;
+  }
+
+  SetLinkerError("dlsym: Invalid library handle %p looking for '%s'",
+                 lib_handle,
+                 symbol_name);
+  return NULL;
+}
+
+int WrapDladdr(void* address, Dl_info* info) {
+  // First, perform search in crazy libraries.
+  {
+    ScopedGlobalLock lock;
+    LibraryList* lib_list = Globals::GetLibraries();
+    LibraryView* wrap = lib_list->FindLibraryForAddress(address);
+    if (wrap && wrap->IsCrazy()) {
+      SharedLibrary* lib = wrap->GetCrazy();
+      ::memset(info, 0, sizeof(*info));
+      info->dli_fname = lib->base_name;
+      info->dli_fbase = reinterpret_cast<void*>(lib->base);
+
+      // Determine if any symbol in the library contains the specified address.
+      ELF::Sym* sym = lib->FindSymbolForAddress(address);
+      if (sym != NULL) {
+        info->dli_sname = lib->strtab + sym->st_name;
+        info->dli_saddr =
+            reinterpret_cast<void*>(lib->load_bias + sym->st_value);
+      }
+      return 0;
+    }
+  }
+  // Otherwise, use system version.
+  ::dlerror();
+  int ret = ::dladdr(address, info);
+  if (ret != 0)
+    SaveSystemError();
+  return ret;
+}
+
+int WrapDlclose(void* lib_handle) {
+  LibraryView* wrap_lib = reinterpret_cast<LibraryView*>(lib_handle);
+  if (!wrap_lib) {
+    SetLinkerError("NULL library handle");
+    return -1;
+  }
+
+  if (wrap_lib->IsSystem() || wrap_lib->IsCrazy()) {
+    ScopedGlobalLock lock;
+    LibraryList* lib_list = Globals::GetLibraries();
+    lib_list->UnloadLibrary(wrap_lib);
+    return 0;
+  }
+
+  // Invalid library handle!!
+  SetLinkerError("Invalid library handle %p", lib_handle);
+  return -1;
+}
+
+#ifdef __arm__
+_Unwind_Ptr WrapDl_unwind_find_exidx(_Unwind_Ptr pc, int* pcount) {
+  // First lookup in crazy libraries.
+  {
+    ScopedGlobalLock lock;
+    LibraryList* list = Globals::GetLibraries();
+    _Unwind_Ptr result = list->FindArmExIdx(pc, pcount);
+    if (result)
+      return result;
+  }
+  // Lookup in system libraries.
+  return ::dl_unwind_find_exidx(pc, pcount);
+}
+#else  // !__arm__
+int WrapDl_iterate_phdr(int (*cb)(dl_phdr_info*, size_t, void*), void* data) {
+  // First, iterate over crazy libraries.
+  {
+    ScopedGlobalLock lock;
+    LibraryList* list = Globals::GetLibraries();
+    int result = list->IteratePhdr(cb, data);
+    if (result)
+      return result;
+  }
+  // Then lookup through system ones.
+  return ::dl_iterate_phdr(cb, data);
+}
+#endif  // !__arm__
+
+}  // namespace
+
+void* WrapLinkerSymbol(const char* name) {
+  // Shortcut, since all names begin with 'dl'
+  // Take care of __aeabi_atexit on ARM though.
+  if (name[0] != 'd' || name[1] != 'l') {
+#ifdef __arm__
+    if (name[0] == '_' && !strcmp("__aeabi_atexit", name))
+      return reinterpret_cast<void*>(&__aeabi_atexit);
+#endif
+    return NULL;
+  }
+
+  static const struct {
+    const char* name;
+    void* address;
+  } kSymbols[] = {
+        {"dlopen", reinterpret_cast<void*>(&WrapDlopen)},
+        {"dlclose", reinterpret_cast<void*>(&WrapDlclose)},
+        {"dlerror", reinterpret_cast<void*>(&WrapDlerror)},
+        {"dlsym", reinterpret_cast<void*>(&WrapDlsym)},
+        {"dladdr", reinterpret_cast<void*>(&WrapDladdr)},
+#ifdef __arm__
+        {"dl_unwind_find_exidx",
+         reinterpret_cast<void*>(&WrapDl_unwind_find_exidx)},
+#else
+        {"dl_iterate_phdr", reinterpret_cast<void*>(&WrapDl_iterate_phdr)},
+#endif
+    };
+  static const size_t kCount = sizeof(kSymbols) / sizeof(kSymbols[0]);
+  for (size_t n = 0; n < kCount; ++n) {
+    if (!strcmp(kSymbols[n].name, name))
+      return kSymbols[n].address;
+  }
+  return NULL;
+}
+
+}  // namespace crazy
diff --git a/sources/android/crazy_linker/src/crazy_linker_wrappers.h b/sources/android/crazy_linker/src/crazy_linker_wrappers.h
new file mode 100644
index 0000000..f1b1dfc
--- /dev/null
+++ b/sources/android/crazy_linker/src/crazy_linker_wrappers.h
@@ -0,0 +1,17 @@
+// 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.
+
+#ifndef CRAZY_LINKER_WRAPPERS_H
+#define CRAZY_LINKER_WRAPPERS_H
+
+namespace crazy {
+
+// If |symbol_name| is the name of a linker-specific symbol name, like
+// 'dlopen' or 'dlsym', return the address of the corresponding wrapper.
+// Return NULL otherwise.
+void* WrapLinkerSymbol(const char* symbol_name);
+
+}  // namespace crazy
+
+#endif  // CRAZY_LINKER_WRAPPERS_H
diff --git a/sources/android/crazy_linker/src/elf_traits.h b/sources/android/crazy_linker/src/elf_traits.h
new file mode 100644
index 0000000..afd69da
--- /dev/null
+++ b/sources/android/crazy_linker/src/elf_traits.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef _ELF_TRAITS_H_
+#define _ELF_TRAITS_H_
+
+// NOTE: <stdint.h> is required here before <elf.h>. This is a NDK header bug.
+#include <stdint.h>
+#include <elf.h>
+#include <sys/exec_elf.h>
+
+// ELF is a traits structure used to provide convenient aliases for
+// 32/64 bit Elf types, depending on the target CPU bitness.
+#if __SIZEOF_POINTER__ == 4
+struct ELF {
+  typedef Elf32_Ehdr Ehdr;
+  typedef Elf32_Phdr Phdr;
+  typedef Elf32_Word Word;
+  typedef Elf32_Addr Addr;
+  typedef Elf32_Dyn Dyn;
+  typedef Elf32_Sym Sym;
+  typedef Elf32_Rel Rel;
+  typedef Elf32_auxv_t auxv_t;
+};
+#elif __SIZEOF_POINTER__ == 8
+struct ELF {
+  typedef Elf64_Ehdr Ehdr;
+  typedef Elf64_Phdr Phdr;
+  typedef Elf64_Word Word;
+  typedef Elf64_Addr Addr;
+  typedef Elf64_Dyn Dyn;
+  typedef Elf64_Sym Sym;
+  typedef Elf64_Rel Rel;
+  typedef Elf64_auxv_t auxv_t;
+};
+#else
+#error "Unsupported target CPU bitness"
+#endif
+
+#ifdef __arm__
+#define ELF_MACHINE EM_ARM
+#elif defined(__i386__)
+#define ELF_MACHINE EM_386
+#elif defined(__mips__)
+#define ELF_MACHINE EM_MIPS
+#else
+#error "Unsupported target CPU architecture"
+#endif
+
+#endif  // _ELF_TRAITS_H_
diff --git a/sources/android/crazy_linker/src/linker_phdr.cpp b/sources/android/crazy_linker/src/linker_phdr.cpp
new file mode 100644
index 0000000..4f0a5b4
--- /dev/null
+++ b/sources/android/crazy_linker/src/linker_phdr.cpp
@@ -0,0 +1,407 @@
+// 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.
+
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "linker_phdr.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#define PAGE_START(x) ((x) & PAGE_MASK)
+#define PAGE_OFFSET(x) ((x) & ~PAGE_MASK)
+#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
+
+// Missing exec_elf.h definitions.
+#ifndef PT_GNU_RELRO
+#define PT_GNU_RELRO 0x6474e552
+#endif
+
+/**
+  TECHNICAL NOTE ON ELF LOADING.
+
+  An ELF file's program header table contains one or more PT_LOAD
+  segments, which corresponds to portions of the file that need to
+  be mapped into the process' address space.
+
+  Each loadable segment has the following important properties:
+
+    p_offset  -> segment file offset
+    p_filesz  -> segment file size
+    p_memsz   -> segment memory size (always >= p_filesz)
+    p_vaddr   -> segment's virtual address
+    p_flags   -> segment flags (e.g. readable, writable, executable)
+
+  We will ignore the p_paddr and p_align fields of ELF::Phdr for now.
+
+  The loadable segments can be seen as a list of [p_vaddr ... p_vaddr+p_memsz)
+  ranges of virtual addresses. A few rules apply:
+
+  - the virtual address ranges should not overlap.
+
+  - if a segment's p_filesz is smaller than its p_memsz, the extra bytes
+    between them should always be initialized to 0.
+
+  - ranges do not necessarily start or end at page boundaries. Two distinct
+    segments can have their start and end on the same page. In this case, the
+    page inherits the mapping flags of the latter segment.
+
+  Finally, the real load addrs of each segment is not p_vaddr. Instead the
+  loader decides where to load the first segment, then will load all others
+  relative to the first one to respect the initial range layout.
+
+  For example, consider the following list:
+
+    [ offset:0,      filesz:0x4000, memsz:0x4000, vaddr:0x30000 ],
+    [ offset:0x4000, filesz:0x2000, memsz:0x8000, vaddr:0x40000 ],
+
+  This corresponds to two segments that cover these virtual address ranges:
+
+       0x30000...0x34000
+       0x40000...0x48000
+
+  If the loader decides to load the first segment at address 0xa0000000
+  then the segments' load address ranges will be:
+
+       0xa0030000...0xa0034000
+       0xa0040000...0xa0048000
+
+  In other words, all segments must be loaded at an address that has the same
+  constant offset from their p_vaddr value. This offset is computed as the
+  difference between the first segment's load address, and its p_vaddr value.
+
+  However, in practice, segments do _not_ start at page boundaries. Since we
+  can only memory-map at page boundaries, this means that the bias is
+  computed as:
+
+       load_bias = phdr0_load_address - PAGE_START(phdr0->p_vaddr)
+
+  (NOTE: The value must be used as a 32-bit unsigned integer, to deal with
+          possible wrap around UINT32_MAX for possible large p_vaddr values).
+
+  And that the phdr0_load_address must start at a page boundary, with
+  the segment's real content starting at:
+
+       phdr0_load_address + PAGE_OFFSET(phdr0->p_vaddr)
+
+  Note that ELF requires the following condition to make the mmap()-ing work:
+
+      PAGE_OFFSET(phdr0->p_vaddr) == PAGE_OFFSET(phdr0->p_offset)
+
+  The load_bias must be added to any p_vaddr value read from the ELF file to
+  determine the corresponding memory address.
+
+ **/
+
+#define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0)
+#define PFLAGS_TO_PROT(x)                 \
+  (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \
+   MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \
+   MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE))
+
+/* Returns the size of the extent of all the possibly non-contiguous
+ * loadable segments in an ELF program header table. This corresponds
+ * to the page-aligned size in bytes that needs to be reserved in the
+ * process' address space. If there are no loadable segments, 0 is
+ * returned.
+ *
+ * If out_min_vaddr or out_max_vaddr are non-NULL, they will be
+ * set to the minimum and maximum addresses of pages to be reserved,
+ * or 0 if there is nothing to load.
+ */
+size_t phdr_table_get_load_size(const ELF::Phdr* phdr_table,
+                                size_t phdr_count,
+                                ELF::Addr* out_min_vaddr,
+                                ELF::Addr* out_max_vaddr) {
+  ELF::Addr min_vaddr = 0xFFFFFFFFU;
+  ELF::Addr max_vaddr = 0x00000000U;
+
+  bool found_pt_load = false;
+  for (size_t i = 0; i < phdr_count; ++i) {
+    const ELF::Phdr* phdr = &phdr_table[i];
+
+    if (phdr->p_type != PT_LOAD) {
+      continue;
+    }
+    found_pt_load = true;
+
+    if (phdr->p_vaddr < min_vaddr) {
+      min_vaddr = phdr->p_vaddr;
+    }
+
+    if (phdr->p_vaddr + phdr->p_memsz > max_vaddr) {
+      max_vaddr = phdr->p_vaddr + phdr->p_memsz;
+    }
+  }
+  if (!found_pt_load) {
+    min_vaddr = 0x00000000U;
+  }
+
+  min_vaddr = PAGE_START(min_vaddr);
+  max_vaddr = PAGE_END(max_vaddr);
+
+  if (out_min_vaddr != NULL) {
+    *out_min_vaddr = min_vaddr;
+  }
+  if (out_max_vaddr != NULL) {
+    *out_max_vaddr = max_vaddr;
+  }
+  return max_vaddr - min_vaddr;
+}
+
+/* Used internally. Used to set the protection bits of all loaded segments
+ * with optional extra flags (i.e. really PROT_WRITE). Used by
+ * phdr_table_protect_segments and phdr_table_unprotect_segments.
+ */
+static int _phdr_table_set_load_prot(const ELF::Phdr* phdr_table,
+                                     int phdr_count,
+                                     ELF::Addr load_bias,
+                                     int extra_prot_flags) {
+  const ELF::Phdr* phdr = phdr_table;
+  const ELF::Phdr* phdr_limit = phdr + phdr_count;
+
+  for (; phdr < phdr_limit; phdr++) {
+    if (phdr->p_type != PT_LOAD || (phdr->p_flags & PF_W) != 0)
+      continue;
+
+    ELF::Addr seg_page_start = PAGE_START(phdr->p_vaddr) + load_bias;
+    ELF::Addr seg_page_end =
+        PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias;
+
+    int ret = mprotect((void*)seg_page_start,
+                       seg_page_end - seg_page_start,
+                       PFLAGS_TO_PROT(phdr->p_flags) | extra_prot_flags);
+    if (ret < 0) {
+      return -1;
+    }
+  }
+  return 0;
+}
+
+/* Restore the original protection modes for all loadable segments.
+ * You should only call this after phdr_table_unprotect_segments and
+ * applying all relocations.
+ *
+ * Input:
+ *   phdr_table  -> program header table
+ *   phdr_count  -> number of entries in tables
+ *   load_bias   -> load bias
+ * Return:
+ *   0 on error, -1 on failure (error code in errno).
+ */
+int phdr_table_protect_segments(const ELF::Phdr* phdr_table,
+                                int phdr_count,
+                                ELF::Addr load_bias) {
+  return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, 0);
+}
+
+/* Change the protection of all loaded segments in memory to writable.
+ * This is useful before performing relocations. Once completed, you
+ * will have to call phdr_table_protect_segments to restore the original
+ * protection flags on all segments.
+ *
+ * Note that some writable segments can also have their content turned
+ * to read-only by calling phdr_table_protect_gnu_relro. This is no
+ * performed here.
+ *
+ * Input:
+ *   phdr_table  -> program header table
+ *   phdr_count  -> number of entries in tables
+ *   load_bias   -> load bias
+ * Return:
+ *   0 on error, -1 on failure (error code in errno).
+ */
+int phdr_table_unprotect_segments(const ELF::Phdr* phdr_table,
+                                  int phdr_count,
+                                  ELF::Addr load_bias) {
+  return _phdr_table_set_load_prot(
+      phdr_table, phdr_count, load_bias, PROT_WRITE);
+}
+
+/* Return the extend of the GNU RELRO segment in a program header.
+ * On success, return 0 and sets |*relro_start| and |*relro_end|
+ * to the page-aligned extents of the RELRO section.
+ * On failure, return -1.
+ *
+ * NOTE: This assumes there is a single PT_GNU_RELRO segment in the
+ * program header, i.e. it will return the extents of the first entry.
+ */
+int phdr_table_get_relro_info(const ELF::Phdr* phdr_table,
+                              int phdr_count,
+                              ELF::Addr load_bias,
+                              ELF::Addr* relro_start,
+                              ELF::Addr* relro_size) {
+  const ELF::Phdr* phdr;
+  const ELF::Phdr* phdr_limit = phdr_table + phdr_count;
+
+  for (phdr = phdr_table; phdr < phdr_limit; ++phdr) {
+    if (phdr->p_type != PT_GNU_RELRO)
+      continue;
+
+    /* Tricky: what happens when the relro segment does not start
+     * or end at page boundaries?. We're going to be over-protective
+     * here and put every page touched by the segment as read-only.
+     *
+     * This seems to match Ian Lance Taylor's description of the
+     * feature at http://www.airs.com/blog/archives/189.
+     *
+     * Extract:
+     *    Note that the current dynamic linker code will only work
+     *    correctly if the PT_GNU_RELRO segment starts on a page
+     *    boundary. This is because the dynamic linker rounds the
+     *    p_vaddr field down to the previous page boundary. If
+     *    there is anything on the page which should not be read-only,
+     *    the program is likely to fail at runtime. So in effect the
+     *    linker must only emit a PT_GNU_RELRO segment if it ensures
+     *    that it starts on a page boundary.
+     */
+    *relro_start = PAGE_START(phdr->p_vaddr) + load_bias;
+    *relro_size =
+        PAGE_END(phdr->p_vaddr + phdr->p_memsz) + load_bias - *relro_start;
+    return 0;
+  }
+
+  return -1;
+}
+
+/* Apply GNU relro protection if specified by the program header. This will
+ * turn some of the pages of a writable PT_LOAD segment to read-only, as
+ * specified by one or more PT_GNU_RELRO segments. This must be always
+ * performed after relocations.
+ *
+ * The areas typically covered are .got and .data.rel.ro, these are
+ * read-only from the program's POV, but contain absolute addresses
+ * that need to be relocated before use.
+ *
+ * Input:
+ *   phdr_table  -> program header table
+ *   phdr_count  -> number of entries in tables
+ *   load_bias   -> load bias
+ * Return:
+ *   0 on error, -1 on failure (error code in errno).
+ */
+int phdr_table_protect_gnu_relro(const ELF::Phdr* phdr_table,
+                                 int phdr_count,
+                                 ELF::Addr load_bias) {
+  ELF::Addr relro_start, relro_size;
+
+  if (phdr_table_get_relro_info(
+          phdr_table, phdr_count, load_bias, &relro_start, &relro_size) < 0) {
+    return -1;
+  }
+
+  return mprotect((void*)relro_start, relro_size, PROT_READ);
+}
+
+#ifdef __arm__
+
+#ifndef PT_ARM_EXIDX
+#define PT_ARM_EXIDX 0x70000001 /* .ARM.exidx segment */
+#endif
+
+/* Return the address and size of the .ARM.exidx section in memory,
+ * if present.
+ *
+ * Input:
+ *   phdr_table  -> program header table
+ *   phdr_count  -> number of entries in tables
+ *   load_bias   -> load bias
+ * Output:
+ *   arm_exidx       -> address of table in memory (NULL on failure).
+ *   arm_exidx_count -> number of items in table (0 on failure).
+ * Return:
+ *   0 on error, -1 on failure (_no_ error code in errno)
+ */
+int phdr_table_get_arm_exidx(const ELF::Phdr* phdr_table,
+                             int phdr_count,
+                             ELF::Addr load_bias,
+                             ELF::Addr** arm_exidx,
+                             unsigned* arm_exidx_count) {
+  const ELF::Phdr* phdr = phdr_table;
+  const ELF::Phdr* phdr_limit = phdr + phdr_count;
+
+  for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
+    if (phdr->p_type != PT_ARM_EXIDX)
+      continue;
+
+    *arm_exidx = (ELF::Addr*)(load_bias + phdr->p_vaddr);
+    *arm_exidx_count = (unsigned)(phdr->p_memsz / 8);
+    return 0;
+  }
+  *arm_exidx = NULL;
+  *arm_exidx_count = 0;
+  return -1;
+}
+#endif  // __arm__
+
+/* Return the address and size of the ELF file's .dynamic section in memory,
+ * or NULL if missing.
+ *
+ * Input:
+ *   phdr_table  -> program header table
+ *   phdr_count  -> number of entries in tables
+ *   load_bias   -> load bias
+ * Output:
+ *   dynamic       -> address of table in memory (NULL on failure).
+ *   dynamic_count -> number of items in table (0 on failure).
+ *   dynamic_flags -> protection flags for section (unset on failure)
+ * Return:
+ *   void
+ */
+void phdr_table_get_dynamic_section(const ELF::Phdr* phdr_table,
+                                    int phdr_count,
+                                    ELF::Addr load_bias,
+                                    ELF::Dyn** dynamic,
+                                    size_t* dynamic_count,
+                                    ELF::Word* dynamic_flags) {
+  const ELF::Phdr* phdr = phdr_table;
+  const ELF::Phdr* phdr_limit = phdr + phdr_count;
+
+  for (phdr = phdr_table; phdr < phdr_limit; phdr++) {
+    if (phdr->p_type != PT_DYNAMIC) {
+      continue;
+    }
+
+    *dynamic = reinterpret_cast<ELF::Dyn*>(load_bias + phdr->p_vaddr);
+    if (dynamic_count) {
+      *dynamic_count = (unsigned)(phdr->p_memsz / sizeof(ELF::Dyn));
+    }
+    if (dynamic_flags) {
+      *dynamic_flags = phdr->p_flags;
+    }
+    return;
+  }
+  *dynamic = NULL;
+  if (dynamic_count) {
+    *dynamic_count = 0;
+  }
+}
diff --git a/sources/android/crazy_linker/src/linker_phdr.h b/sources/android/crazy_linker/src/linker_phdr.h
new file mode 100644
index 0000000..9ff02c7
--- /dev/null
+++ b/sources/android/crazy_linker/src/linker_phdr.h
@@ -0,0 +1,82 @@
+// 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.
+
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef LINKER_PHDR_H
+#define LINKER_PHDR_H
+
+/* Declarations related to the ELF program header table and segments.
+ *
+ * The design goal is to provide an API that is as close as possible
+ * to the ELF spec, and does not depend on linker-specific data
+ * structures (e.g. the exact layout of struct soinfo).
+ */
+
+#include "elf_traits.h"
+
+size_t phdr_table_get_load_size(const ELF::Phdr* phdr_table,
+                                size_t phdr_count,
+                                ELF::Addr* min_vaddr = NULL,
+                                ELF::Addr* max_vaddr = NULL);
+
+int phdr_table_protect_segments(const ELF::Phdr* phdr_table,
+                                int phdr_count,
+                                ELF::Addr load_bias);
+
+int phdr_table_unprotect_segments(const ELF::Phdr* phdr_table,
+                                  int phdr_count,
+                                  ELF::Addr load_bias);
+
+int phdr_table_get_relro_info(const ELF::Phdr* phdr_table,
+                              int phdr_count,
+                              ELF::Addr load_bias,
+                              ELF::Addr* relro_start,
+                              ELF::Addr* relro_size);
+
+int phdr_table_protect_gnu_relro(const ELF::Phdr* phdr_table,
+                                 int phdr_count,
+                                 ELF::Addr load_bias);
+
+#ifdef __arm__
+int phdr_table_get_arm_exidx(const ELF::Phdr* phdr_table,
+                             int phdr_count,
+                             ELF::Addr load_bias,
+                             ELF::Addr** arm_exidx,
+                             unsigned* arm_exidix_count);
+#endif
+
+void phdr_table_get_dynamic_section(const ELF::Phdr* phdr_table,
+                                    int phdr_count,
+                                    ELF::Addr load_bias,
+                                    ELF::Dyn** dynamic,
+                                    size_t* dynamic_count,
+                                    ELF::Word* dynamic_flags);
+
+#endif /* LINKER_PHDR_H */
diff --git a/sources/android/crazy_linker/tests/Android.mk b/sources/android/crazy_linker/tests/Android.mk
new file mode 100644
index 0000000..25d3603
--- /dev/null
+++ b/sources/android/crazy_linker/tests/Android.mk
@@ -0,0 +1,114 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libfoo
+LOCAL_SRC_FILES := foo.cpp
+LOCAL_LDLIBS := -llog
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libfoo2
+LOCAL_SRC_FILES := foo2.cpp
+LOCAL_LDLIBS := -llog
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libfoo_with_static_constructor
+LOCAL_SRC_FILES := foo_with_static_constructor.cpp
+LOCAL_LDLIBS := -llog
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libfoo_with_relro
+LOCAL_SRC_FILES := foo_with_relro.cpp
+LOCAL_LDLIBS := -llog
+include $(BUILD_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbar
+LOCAL_SRC_FILES := bar.cpp
+LOCAL_SHARED_LIBRARIES := libfoo
+LOCAL_LDLIBS := -llog
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbar_with_relro
+LOCAL_SRC_FILES := bar_with_relro.cpp
+LOCAL_SHARED_LIBRARIES := libfoo_with_relro
+LOCAL_LDLIBS := -llog
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libzoo
+LOCAL_SRC_FILES := zoo.cpp
+LOCAL_LDLIBS := -ldl
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libjni_lib
+LOCAL_SRC_FILES := jni_lib.cpp
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := test_load_library
+LOCAL_SRC_FILES := test_load_library.cpp
+LOCAL_STATIC_LIBRARIES := crazy_linker
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := test_load_library_depends
+LOCAL_SRC_FILES := test_load_library_depends.cpp
+LOCAL_STATIC_LIBRARIES := crazy_linker
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := test_dl_wrappers
+LOCAL_SRC_FILES := test_dl_wrappers.cpp
+LOCAL_STATIC_LIBRARIES := crazy_linker
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := test_constructors_destructors
+LOCAL_SRC_FILES := test_constructors_destructors.cpp
+LOCAL_STATIC_LIBRARIES := crazy_linker
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := test_relro_sharing
+LOCAL_SRC_FILES := test_relro_sharing.cpp
+LOCAL_STATIC_LIBRARIES := crazy_linker
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := test_relro_sharing_two_libs
+LOCAL_SRC_FILES := test_relro_sharing_two_libs.cpp
+LOCAL_STATIC_LIBRARIES := crazy_linker
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := test_search_path_list
+LOCAL_SRC_FILES := test_search_path_list.cpp
+LOCAL_STATIC_LIBRARIES := crazy_linker
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := test_jni_hooks
+LOCAL_SRC_FILES := test_jni_hooks.cpp
+LOCAL_STATIC_LIBRARIES := crazy_linker
+include $(BUILD_EXECUTABLE)
+
+
+ifneq (,$(strip $(CRAZY_BENCH)))
+include $(CLEAR_VARS)
+LOCAL_MODULE := bench_load_library
+LOCAL_SRC_FILES := $(LOCAL_MODULE).cpp
+LOCAL_STATIC_LIBRARIES := crazy_linker
+include $(BUILD_EXECUTABLE)
+endif
+
+include $(LOCAL_PATH)/../Android.mk
diff --git a/sources/android/crazy_linker/tests/bar.cpp b/sources/android/crazy_linker/tests/bar.cpp
new file mode 100644
index 0000000..f6cd21c
--- /dev/null
+++ b/sources/android/crazy_linker/tests/bar.cpp
@@ -0,0 +1,19 @@
+// 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 <android/log.h>
+#include <stdio.h>
+
+extern "C" void Foo();
+
+extern "C" void Bar() {
+  printf("%s: Entering\n", __FUNCTION__);
+  __android_log_print(ANDROID_LOG_INFO, "bar", "Hi There!");
+  fprintf(stderr, "Hi There! from Bar\n");
+
+  printf("%s: Calling Foo()\n", __FUNCTION__);
+  Foo();
+
+  printf("%s: Exiting\n", __FUNCTION__);
+}
diff --git a/sources/android/crazy_linker/tests/bar_with_relro.cpp b/sources/android/crazy_linker/tests/bar_with_relro.cpp
new file mode 100644
index 0000000..46a05f6
--- /dev/null
+++ b/sources/android/crazy_linker/tests/bar_with_relro.cpp
@@ -0,0 +1,42 @@
+// 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 <android/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// A variant of bar.cpp that also includes a large RELRO section.
+// Used to test RELRO sharing with two different libraries at the
+// same time.
+
+// This is a large table that contains pointers to ensure that it
+// gets put inside the RELRO section.
+#define LINE "another example string",
+#define LINE8 LINE LINE LINE LINE LINE LINE LINE LINE
+#define LINE64 LINE8 LINE8 LINE8 LINE8 LINE8 LINE8 LINE8 LINE8
+#define LINE512 LINE64 LINE64 LINE64 LINE64 LINE64 LINE64 LINE64 LINE64
+#define LINE4096 LINE512 LINE512 LINE512 LINE512 LINE512 LINE512 LINE512 LINE512
+
+const char* const kStrings[] = {LINE4096 LINE4096 LINE4096 LINE4096};
+
+extern "C" void Foo();
+
+extern "C" void Bar() {
+  printf("%s: Entering\n", __FUNCTION__);
+  __android_log_print(ANDROID_LOG_INFO, "bar", "Hi There!");
+  fprintf(stderr, "Hi There! from Bar\n");
+
+  for (size_t n = 0; n < sizeof(kStrings) / sizeof(kStrings[0]); ++n) {
+    const char* ptr = kStrings[n];
+    if (strcmp(ptr, "another example string")) {
+      printf("%s: Bad string at offset=%d\n", __FUNCTION__, n);
+      exit(1);
+    }
+  }
+
+  printf("%s: Calling Foo()\n", __FUNCTION__);
+  Foo();
+
+  printf("%s: Exiting\n", __FUNCTION__);
+}
diff --git a/sources/android/crazy_linker/tests/bench_load_library.cpp b/sources/android/crazy_linker/tests/bench_load_library.cpp
new file mode 100644
index 0000000..2f9a053
--- /dev/null
+++ b/sources/android/crazy_linker/tests/bench_load_library.cpp
@@ -0,0 +1,150 @@
+// 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.
+
+// A crazy linker test to:
+// - Load a library (libfoo.so) with the linker.
+// - Find the address of the "Foo" function in it.
+// - Call the function.
+// - Close the library.
+
+#include <crazy_linker.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static void Panic(const char* fmt, ...) {
+  va_list args;
+  fprintf(stderr, "PANIC: ");
+  va_start(args, fmt);
+  vfprintf(stderr, fmt, args);
+  va_end(args);
+  exit(1);
+}
+
+static double now_ms() {
+  struct timespec ts;
+  clock_gettime(CLOCK_MONOTONIC, &ts);
+  return (ts.tv_sec * 1000.) + (ts.tv_nsec / 1000000.);
+}
+
+static void drop_caches() {
+  int fd = open("/proc/sys/vm/drop_caches", O_RDWR);
+  if (fd < 0) {
+    fprintf(stderr,
+            "Could not drop caches! Please run this program as root!\n");
+    return;
+  }
+  write(fd, "3\n", 2);
+  close(fd);
+}
+
+class ScopedTimer {
+ public:
+  ScopedTimer(const char* name) {
+    name_ = name;
+    start_ms_ = now_ms();
+  }
+
+  ~ScopedTimer() {
+    double elapsed_ms = now_ms() - start_ms_;
+    printf("Timer %s: %.1f\n", name_, elapsed_ms);
+  }
+
+ private:
+  const char* name_;
+  double start_ms_;
+};
+
+int main(int argc, char** argv) {
+  const char* library_path = "libfoo.so";
+  if (argc >= 2)
+    library_path = argv[1];
+
+  { ScopedTimer null_timer("empty"); }
+
+  // Load the library with dlopen().
+  void* lib;
+  drop_caches();
+  {
+    ScopedTimer timer("dlopen");
+    lib = dlopen(library_path, RTLD_NOW);
+  }
+  if (!lib)
+    Panic("Could not load library with dlopen(): %s\n", dlerror());
+
+  dlclose(lib);
+
+  crazy_library_t* library;
+  crazy_context_t* context = crazy_context_create();
+
+  // Ensure the program looks in its own directory too.
+  crazy_context_add_search_path_for_address(context,
+                                            reinterpret_cast<void*>(&main));
+
+  // Load the library with the crazy linker.
+  drop_caches();
+  {
+    ScopedTimer timer("crazy_linker");
+    // Load libfoo.so
+    if (!crazy_library_open(&library, library_path, context)) {
+      Panic("Could not open library: %s\n", crazy_context_get_error(context));
+    }
+  }
+  crazy_library_close(library);
+
+  // Load the library with the crazy linker. Preload libOpenSLES.so
+  drop_caches();
+  void* sles_lib = dlopen("libOpenSLES.so", RTLD_NOW);
+  {
+    ScopedTimer timer("crazy_linker (preload libOpenSLES.so)");
+    // Load libfoo.so
+    if (!crazy_library_open(&library, library_path, context)) {
+      Panic("Could not open library: %s\n", crazy_context_get_error(context));
+    }
+  }
+  crazy_library_close(library);
+  dlclose(sles_lib);
+
+  // Load the library with the crazy linker. Preload libOpenSLES.so
+  {
+    drop_caches();
+    void* sys1_lib = dlopen("libandroid.so", RTLD_NOW);
+    void* sys2_lib = dlopen("libjnigraphics.so", RTLD_NOW);
+    void* sys3_lib = dlopen("libOpenSLES.so", RTLD_NOW);
+    {
+      ScopedTimer timer("crazy_linker (preload 3 system libs)");
+      // Load libfoo.so
+      if (!crazy_library_open(&library, library_path, context)) {
+        Panic("Could not open library: %s\n", crazy_context_get_error(context));
+      }
+    }
+    crazy_library_close(library);
+    dlclose(sys3_lib);
+    dlclose(sys2_lib);
+    dlclose(sys1_lib);
+  }
+
+  // Load the library with the crazy linker. Create a shared RELRO as well.
+  drop_caches();
+  {
+    ScopedTimer timer("crazy_linker (with RELRO)");
+    // Load libfoo.so
+    if (!crazy_library_open(&library, library_path, context)) {
+      Panic("Could not open library: %s\n", crazy_context_get_error(context));
+    }
+
+    if (!crazy_library_enable_relro_sharing(library, context)) {
+      Panic("Could not create shared RELRO: %s\n",
+            crazy_context_get_error(context));
+    }
+  }
+  crazy_library_close(library);
+
+  printf("OK\n");
+  return 0;
+}
\ No newline at end of file
diff --git a/sources/android/crazy_linker/tests/foo.cpp b/sources/android/crazy_linker/tests/foo.cpp
new file mode 100644
index 0000000..5bd5c2a
--- /dev/null
+++ b/sources/android/crazy_linker/tests/foo.cpp
@@ -0,0 +1,13 @@
+// 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 <android/log.h>
+#include <stdio.h>
+
+extern "C" void Foo() {
+  printf("%s: Entering\n", __FUNCTION__);
+  __android_log_write(ANDROID_LOG_INFO, "foo", "Hello World!");
+  fprintf(stderr, "Hello World from Foo!\n");
+  printf("%s: Exiting\n", __FUNCTION__);
+}
diff --git a/sources/android/crazy_linker/tests/foo2.cpp b/sources/android/crazy_linker/tests/foo2.cpp
new file mode 100644
index 0000000..c35e315
--- /dev/null
+++ b/sources/android/crazy_linker/tests/foo2.cpp
@@ -0,0 +1,15 @@
+// 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 <android/log.h>
+#include <stdio.h>
+
+// Same as foo.cpp, byt exposes Foo2() instead of Foo().
+
+extern "C" void Foo2() {
+  printf("%s: Entering\n", __FUNCTION__);
+  __android_log_write(ANDROID_LOG_INFO, "foo", "Hello World!");
+  fprintf(stderr, "Hello World from Foo!\n");
+  printf("%s: Exiting\n", __FUNCTION__);
+}
diff --git a/sources/android/crazy_linker/tests/foo_with_relro.cpp b/sources/android/crazy_linker/tests/foo_with_relro.cpp
new file mode 100644
index 0000000..67ef98e
--- /dev/null
+++ b/sources/android/crazy_linker/tests/foo_with_relro.cpp
@@ -0,0 +1,29 @@
+// 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 <android/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// This is a large table that contains pointers to ensure that it
+// gets put inside the RELRO section.
+#define LINE "some example string",
+#define LINE8 LINE LINE LINE LINE LINE LINE LINE LINE
+#define LINE64 LINE8 LINE8 LINE8 LINE8 LINE8 LINE8 LINE8 LINE8
+#define LINE512 LINE64 LINE64 LINE64 LINE64 LINE64 LINE64 LINE64 LINE64
+#define LINE4096 LINE512 LINE512 LINE512 LINE512 LINE512 LINE512 LINE512 LINE512
+
+const char* const kStrings[] = {LINE4096 LINE4096 LINE4096 LINE4096};
+
+extern "C" void Foo() {
+  printf("%s: Entering\n", __FUNCTION__);
+  for (size_t n = 0; n < sizeof(kStrings) / sizeof(kStrings[0]); ++n) {
+    const char* ptr = kStrings[n];
+    if (strcmp(ptr, "some example string")) {
+      printf("%s: Bad string at offset=%d\n", __FUNCTION__, n);
+      exit(1);
+    }
+  }
+  printf("%s: Exiting\n", __FUNCTION__);
+}
diff --git a/sources/android/crazy_linker/tests/foo_with_static_constructor.cpp b/sources/android/crazy_linker/tests/foo_with_static_constructor.cpp
new file mode 100644
index 0000000..f9d291a
--- /dev/null
+++ b/sources/android/crazy_linker/tests/foo_with_static_constructor.cpp
@@ -0,0 +1,41 @@
+// 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.
+
+// This source file must contain a static destructor to check that
+// the crazy linker can resolve weak symbols from the C library,
+// like __aeabi_atexit(), which are not normally returned by
+// a call to dlsym().
+
+#include <stdlib.h>
+
+extern "C" void __aeabi_atexit(void*);
+
+class A {
+ public:
+  A() {
+    x_ = rand();
+    const char* env = getenv("TEST_VAR");
+    if (!env || strcmp(env, "INIT"))
+      putenv("TEST_VAR=LOAD_ERROR");
+    else
+      putenv("TEST_VAR=LOADED");
+  }
+
+  ~A() {
+    const char* env = getenv("TEST_VAR");
+    if (!env || strcmp(env, "LOADED"))
+      putenv("TEST_VAR=UNLOAD_ERROR");
+    else
+      putenv("TEST_VAR=UNLOADED");
+  }
+
+  int Get() const { return x_; }
+
+ private:
+  int x_;
+};
+
+A s_a;
+
+extern "C" int Foo() { return s_a.Get(); }
diff --git a/sources/android/crazy_linker/tests/jni_lib.cpp b/sources/android/crazy_linker/tests/jni_lib.cpp
new file mode 100644
index 0000000..3686da0
--- /dev/null
+++ b/sources/android/crazy_linker/tests/jni_lib.cpp
@@ -0,0 +1,43 @@
+// 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.
+
+// A simple library that provides JNI_OnLoad() and JNI_OnUnload() hooks.
+// Used by test_java_vm.cpp
+
+#include <jni.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define VARNAME "TEST_VAR"
+
+extern "C" int JNI_OnLoad(JavaVM* vm, void* reserved) {
+  printf("%s: Entering\n", __FUNCTION__);
+  const char* env = getenv(VARNAME);
+  if (!env || strcmp(env, "INIT")) {
+    fprintf(stderr,
+            "%s: Env variable %s has invalid value: %s (expected INIT)\n",
+            __FUNCTION__,
+            VARNAME,
+            env);
+    exit(1);
+  }
+  setenv(VARNAME, "LOADED", 1);
+  printf("%s: Exiting\n", __FUNCTION__);
+  return JNI_VERSION_1_4;
+}
+
+extern "C" void JNI_OnUnload(JavaVM* vm, void* reserved) {
+  printf("%s: Entering\n", __FUNCTION__);
+  const char* env = getenv(VARNAME);
+  if (!env || strcmp(env, "LOADED")) {
+    fprintf(stderr,
+            "%s: Env variable %s has invalid value: %s (expected LOADED)\n",
+            __FUNCTION__,
+            VARNAME,
+            env);
+    exit(1);
+  }
+  setenv(VARNAME, "UNLOADED", 1);
+  printf("%s: Exiting\n", __FUNCTION__);
+}
diff --git a/sources/android/crazy_linker/tests/test_constructors_destructors.cpp b/sources/android/crazy_linker/tests/test_constructors_destructors.cpp
new file mode 100644
index 0000000..d81b679
--- /dev/null
+++ b/sources/android/crazy_linker/tests/test_constructors_destructors.cpp
@@ -0,0 +1,65 @@
+// 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.
+
+// A crazy linker test to:
+// - Load a library (libfoo_with_static_constructor.so) with the linker.\
+//
+// - This shall execute a static constructor that will change the value
+//   of the TEST_VAR environment variable to "LOADED'.
+//
+// - Close the library, this shall execute a static destructor that will
+//   change the value of TEST_VAR to "UNLOADED'.
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <crazy_linker.h>
+
+#include "test_util.h"
+
+typedef void (*FunctionPtr)();
+
+int main() {
+  crazy_context_t* context = crazy_context_create();
+  crazy_library_t* library;
+
+  putenv("TEST_VAR=INIT");
+
+  // DEBUG
+  crazy_context_set_load_address(context, 0x20000000);
+
+  // Load libfoo.so
+  if (!crazy_library_open(
+           &library, "libfoo_with_static_constructor.so", context)) {
+    Panic("Could not open library: %s\n", crazy_context_get_error(context));
+  }
+
+  const char* env = getenv("TEST_VAR");
+  if (!env || strcmp(env, "LOADED"))
+    Panic(
+        "Constructors not run when loading libfoo_with_static_constructor.so");
+
+  // Find the "Foo" symbol.
+  FunctionPtr foo_func;
+  if (!crazy_library_find_symbol(
+           library, "Foo", reinterpret_cast<void**>(&foo_func))) {
+    Panic("Could not find 'Foo' in libfoo.so\n");
+  }
+
+  // Call it.
+  (*foo_func)();
+
+  // Close the library.
+  printf("Closing libfoo_with_static_constructor.so\n");
+  crazy_library_close(library);
+
+  env = getenv("TEST_VAR");
+  if (!env || strcmp(env, "UNLOADED"))
+    Panic(
+        "Destructors not run when unloading libfoo_with_static_constructor.so");
+
+  crazy_context_destroy(context);
+
+  return 0;
+}
\ No newline at end of file
diff --git a/sources/android/crazy_linker/tests/test_dl_wrappers.cpp b/sources/android/crazy_linker/tests/test_dl_wrappers.cpp
new file mode 100644
index 0000000..a2aa0c7
--- /dev/null
+++ b/sources/android/crazy_linker/tests/test_dl_wrappers.cpp
@@ -0,0 +1,50 @@
+// 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.
+
+// A crazy linker test to:
+// - Load a library (libzoo.so) with the linker.
+// - Find the address of the "Zoo" function in libzoo.so.
+// - Call the Zoo() function, which will use dlopen() / dlsym() to
+//   find libbar.so (which depends on libfoo.so).
+// - Close the library.
+
+// This tests the dlopen/dlsym/dlclose wrappers provided by the crazy
+// linker to loaded libraries.
+
+#include <stdio.h>
+#include <crazy_linker.h>
+
+#include "test_util.h"
+
+typedef bool (*FunctionPtr)();
+
+int main() {
+  crazy_context_t* context = crazy_context_create();
+  crazy_library_t* library;
+
+  // Load libzoo.so
+  if (!crazy_library_open(&library, "libzoo.so", context)) {
+    Panic("Could not open library: %s\n", crazy_context_get_error(context));
+  }
+
+  // Find the "Zoo" symbol.
+  FunctionPtr zoo_func;
+  if (!crazy_library_find_symbol(
+           library, "Zoo", reinterpret_cast<void**>(&zoo_func))) {
+    Panic("Could not find 'Zoo' in libzoo.so\n");
+  }
+
+  // Call it.
+  bool ret = (*zoo_func)();
+  if (!ret)
+    Panic("'Zoo' function failed!");
+
+  // Close the library.
+  printf("Closing libzoo.so\n");
+  crazy_library_close(library);
+
+  crazy_context_destroy(context);
+
+  return 0;
+}
\ No newline at end of file
diff --git a/sources/android/crazy_linker/tests/test_jni_hooks.cpp b/sources/android/crazy_linker/tests/test_jni_hooks.cpp
new file mode 100644
index 0000000..be3f245
--- /dev/null
+++ b/sources/android/crazy_linker/tests/test_jni_hooks.cpp
@@ -0,0 +1,72 @@
+// 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.
+
+// A crazy linker test to test crazy_context_set_java_vm().
+
+#include <jni.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <crazy_linker.h>
+
+#include "test_util.h"
+
+#define VARNAME "TEST_VAR"
+
+static const char kJniLibName[] = "libjni_lib.so";
+static void* kJavaVM = (void*)0xdeadcafe;
+
+int main() {
+  crazy_context_t* context = crazy_context_create();
+  crazy_library_t* library;
+
+  // Expect to find the library in the same directory than this executable.
+  crazy_context_add_search_path_for_address(context, (void*)&main);
+
+  crazy_context_set_java_vm(context, kJavaVM, JNI_VERSION_1_2);
+
+  // Load libjni_lib.so, this should invoke its JNI_OnLoad() function
+  // automatically.
+  setenv(VARNAME, "INIT", 1);
+  if (!crazy_library_open(&library, kJniLibName, context))
+    Panic("Could not open library: %s\n", crazy_context_get_error(context));
+
+  const char* env = getenv(VARNAME);
+  if (strcmp(env, "LOADED"))
+    Panic("JNI_OnLoad() hook was not called! %s is %s\n", VARNAME, env);
+
+  crazy_library_close(library);
+  env = getenv(VARNAME);
+  if (strcmp(env, "UNLOADED"))
+    Panic("JNI_OnUnload() hook was not called! %s is %s\n", VARNAME, env);
+
+  // Now, change the minimum JNI version to JNI_VERSION_1_6, which should
+  // prevent loading the library properly, since it only supports 1.2.
+  crazy_context_set_java_vm(context, kJavaVM, JNI_VERSION_1_6);
+
+  setenv(VARNAME, "INIT", 1);
+  if (crazy_library_open(&library, kJniLibName, context))
+    Panic("Could load the library with JNI_VERSION_1_6 > JNI_VERSION_1_2.");
+
+  // Disable the feature, this shall load the library, but not call the
+  // JNI_OnLoad() hook.
+  crazy_context_set_java_vm(context, NULL, 0);
+
+  setenv(VARNAME, "INIT", 1);
+  if (!crazy_library_open(&library, kJniLibName, context))
+    Panic("Could not load the library without a JavaVM handle !?\n");
+
+  env = getenv(VARNAME);
+  if (strcmp(env, "INIT"))
+    Panic("JNI_OnLoad() was called, %s is %s (expected INIT)\n", VARNAME, env);
+
+  crazy_library_close(library);
+  env = getenv(VARNAME);
+  if (strcmp(env, "INIT"))
+    Panic(
+        "JNI_OnUnload() was called, %s is %s (expected INIT)\n", VARNAME, env);
+
+  crazy_context_destroy(context);
+
+  return 0;
+}
diff --git a/sources/android/crazy_linker/tests/test_load_library.cpp b/sources/android/crazy_linker/tests/test_load_library.cpp
new file mode 100644
index 0000000..ffaf5c8
--- /dev/null
+++ b/sources/android/crazy_linker/tests/test_load_library.cpp
@@ -0,0 +1,47 @@
+// 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.
+
+// A crazy linker test to:
+// - Load a library (libfoo.so) with the linker.
+// - Find the address of the "Foo" function in it.
+// - Call the function.
+// - Close the library.
+
+#include <stdio.h>
+#include <crazy_linker.h>
+
+#include "test_util.h"
+
+typedef void (*FunctionPtr)();
+
+int main() {
+  crazy_context_t* context = crazy_context_create();
+  crazy_library_t* library;
+
+  // DEBUG
+  crazy_context_set_load_address(context, 0x20000000);
+
+  // Load libfoo.so
+  if (!crazy_library_open(&library, "libfoo.so", context)) {
+    Panic("Could not open library: %s\n", crazy_context_get_error(context));
+  }
+
+  // Find the "Foo" symbol.
+  FunctionPtr foo_func;
+  if (!crazy_library_find_symbol(
+           library, "Foo", reinterpret_cast<void**>(&foo_func))) {
+    Panic("Could not find 'Foo' in libfoo.so\n");
+  }
+
+  // Call it.
+  (*foo_func)();
+
+  // Close the library.
+  printf("Closing libfoo.so\n");
+  crazy_library_close(library);
+
+  crazy_context_destroy(context);
+
+  return 0;
+}
\ No newline at end of file
diff --git a/sources/android/crazy_linker/tests/test_load_library_depends.cpp b/sources/android/crazy_linker/tests/test_load_library_depends.cpp
new file mode 100644
index 0000000..6e2c94d
--- /dev/null
+++ b/sources/android/crazy_linker/tests/test_load_library_depends.cpp
@@ -0,0 +1,55 @@
+// 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.
+
+// A crazy linker test to:
+// - Load a library (libbar.so) with the linker, which depends on
+//   another library (libfoo.so)
+// - Find the address of the "Bar" function in libbar.so.
+// - Call the Bar() function, which ends up calling Foo() in libfoo.so
+// - Close the library.
+
+#include <stdio.h>
+#include <crazy_linker.h>
+
+#include "test_util.h"
+
+typedef void (*FunctionPtr)();
+
+int main() {
+  crazy_context_t* context = crazy_context_create();
+  crazy_library_t* library;
+
+  // DEBUG
+  crazy_context_set_load_address(context, 0x20000000);
+
+  // Load libbar.so
+  if (!crazy_library_open(&library, "libbar.so", context)) {
+    Panic("Could not open library: %s\n", crazy_context_get_error(context));
+  }
+
+  // Find the "Bar" symbol.
+  FunctionPtr bar_func;
+  if (!crazy_library_find_symbol(
+           library, "Bar", reinterpret_cast<void**>(&bar_func))) {
+    Panic("Could not find 'Bar' in libbar.so\n");
+  }
+
+  // Call it.
+  (*bar_func)();
+
+  // Find the "Foo" symbol from libbar.so
+  FunctionPtr foo_func;
+  if (!crazy_library_find_symbol(
+           library, "Foo", reinterpret_cast<void**>(&foo_func))) {
+    Panic("Could not find 'Foo' from libbar.so\n");
+  }
+
+  // Close the library.
+  printf("Closing libbar.so\n");
+  crazy_library_close(library);
+
+  crazy_context_destroy(context);
+
+  return 0;
+}
\ No newline at end of file
diff --git a/sources/android/crazy_linker/tests/test_relro_sharing.cpp b/sources/android/crazy_linker/tests/test_relro_sharing.cpp
new file mode 100644
index 0000000..aebe723
--- /dev/null
+++ b/sources/android/crazy_linker/tests/test_relro_sharing.cpp
@@ -0,0 +1,154 @@
+// 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.
+
+// A crazy linker test to:
+// - Load a library (libfoo.so) with the linker.
+// - Find the address of the "Foo" function in it.
+// - Call the function.
+// - Close the library.
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <crazy_linker.h>
+
+#include "test_util.h"
+
+typedef void (*FunctionPtr)();
+
+int main() {
+
+  if (!crazy_system_can_share_relro()) {
+    fprintf(stderr, "WARNING: Test ignored due to broken kernel!!\n");
+    return 0;
+  }
+
+  crazy_context_t* context = crazy_context_create();
+  crazy_library_t* library;
+
+  // Load at fixed address to simplify testing.
+  crazy_context_set_load_address(context, 0x20000000);
+
+  // Load libfoo_with_relro.so
+  if (!crazy_library_open(&library, "libfoo_with_relro.so", context)) {
+    Panic("Could not open library: %s\n", crazy_context_get_error(context));
+  }
+
+  printf("Library loaded\n");
+
+  int pipes[2];
+  if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipes) < 0)
+    Panic("Could not create socket pair: %s", strerror(errno));
+
+  pid_t child = fork();
+  if (child < 0)
+    Panic("Could not fork test program!");
+
+  if (child == 0) {
+    // In the child.
+    crazy_library_info_t info;
+
+    printf("Child waiting for relro fd\n");
+    // Receive relro information from parent.
+    int relro_fd = -1;
+    if (ReceiveFd(pipes[0], &relro_fd) < 0)
+      Panic("Could not receive relro descriptor from parent");
+
+    printf("Child received relro fd %d\n", relro_fd);
+
+    int ret = TEMP_FAILURE_RETRY(::read(pipes[0], &info, sizeof(info)));
+    if (ret != static_cast<int>(sizeof(info)))
+      Panic("Could not receive relro information from parent");
+
+    info.relro_fd = relro_fd;
+    printf("Child received relro load=%p start=%p size=%p\n",
+           (void*)info.load_address,
+           (void*)info.relro_start,
+           (void*)info.relro_size);
+
+    if (!crazy_library_use_relro_sharing(library,
+                                         info.relro_start,
+                                         info.relro_size,
+                                         info.relro_fd,
+                                         context)) {
+      Panic("Could not use RELRO sharing: %s",
+            crazy_context_get_error(context));
+    }
+
+    printf("RELRO used in child process\n");
+
+    CheckRelroMaps(1);
+
+    FunctionPtr foo_func;
+    if (!crazy_library_find_symbol(
+             library, "Foo", reinterpret_cast<void**>(&foo_func)))
+      Panic("Could not find 'Foo' in library");
+
+    printf("Calling Foo()\n");
+    (*foo_func)();
+
+    printf("Foo called, exiting\n");
+
+    exit(0);
+
+  } else {
+    // In the parent.
+    crazy_library_info_t info;
+
+    printf("Parent enabling RELRO sharing\n");
+
+    // Enable RELRO sharing.
+    if (!crazy_library_enable_relro_sharing(library, context))
+      Panic("Could not enable RELRO sharing: %s",
+            crazy_context_get_error(context));
+
+    if (!crazy_library_get_info(library, context, &info))
+      Panic("Could not get library info: %s", crazy_context_get_error(context));
+
+    printf(
+        "Parent relro info load_addr=%p load_size=%p relro_start=%p "
+        "relro_size=%p relro_fd=%d\n",
+        (void*)info.load_address,
+        (void*)info.load_size,
+        (void*)info.relro_start,
+        (void*)info.relro_size,
+        info.relro_fd);
+
+    CheckRelroMaps(1);
+
+    if (SendFd(pipes[1], info.relro_fd) < 0)
+      Panic("Parent could not send RELRO fd: %s", strerror(errno));
+
+    int ret = TEMP_FAILURE_RETRY(::write(pipes[1], &info, sizeof(info)));
+    if (ret != static_cast<int>(sizeof(info)))
+      Panic("Parent could not send RELRO info: %s", strerror(errno));
+
+    printf("Parent waiting for child\n");
+
+    // Wait for child to complete.
+    int status;
+    waitpid(child, &status, 0);
+
+    if (WIFSIGNALED(status))
+      Panic("Child terminated by signal!!\n");
+    else if (WIFEXITED(status)) {
+      int child_status = WEXITSTATUS(status);
+      if (child_status != 0)
+        Panic("Child terminated with status=%d\n", child_status);
+    } else
+      Panic("Child exited for unknown reason!!\n");
+  }
+
+  printf("Closing library\n");
+  crazy_library_close(library);
+
+  crazy_context_destroy(context);
+  return 0;
+}
diff --git a/sources/android/crazy_linker/tests/test_relro_sharing_two_libs.cpp b/sources/android/crazy_linker/tests/test_relro_sharing_two_libs.cpp
new file mode 100644
index 0000000..19aa70e
--- /dev/null
+++ b/sources/android/crazy_linker/tests/test_relro_sharing_two_libs.cpp
@@ -0,0 +1,208 @@
+// 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.
+
+// Same as test_relro_sharing.cpp, but uses two libraries at the same
+// time (libfoo_with_relro.so and libbar_with_relro.so), each one of
+// them gets its own shared RELRO.
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <crazy_linker.h>
+
+#include "test_util.h"
+
+typedef void (*FunctionPtr)();
+
+struct Library {
+  const char* name;
+  crazy_library_t* library;
+  crazy_library_info_t info;
+
+  void Init(const char* name, crazy_context_t* context) {
+    printf("Loading %s\n", name);
+    this->name = name;
+    if (!crazy_library_open(&this->library, name, context)) {
+      Panic("Could not open %s: %s\n", name, crazy_context_get_error(context));
+    }
+  }
+
+  void Close() { crazy_library_close(this->library); }
+
+  void EnableSharedRelro(crazy_context_t* context) {
+    if (!crazy_library_enable_relro_sharing(this->library, context)) {
+      Panic("Could not enable %s RELRO sharing: %s",
+            this->name,
+            crazy_context_get_error(context));
+    }
+
+    if (!crazy_library_get_info(this->library, context, &this->info))
+      Panic("Could not get %s library info: %s",
+            this->name,
+            crazy_context_get_error(context));
+
+    printf(
+        "Parent %s relro info load_addr=%p load_size=%p"
+        " relro_start=%p relro_size=%p relro_fd=%d\n",
+        this->name,
+        (void*)this->info.load_address,
+        (void*)this->info.load_size,
+        (void*)this->info.relro_start,
+        (void*)this->info.relro_size,
+        this->info.relro_fd);
+  }
+
+  void SendRelroInfo(int fd) {
+    if (SendFd(fd, this->info.relro_fd) < 0) {
+      Panic("Could not send %s RELRO fd: %s", this->name, strerror(errno));
+    }
+
+    int ret = TEMP_FAILURE_RETRY(::write(fd, &this->info, sizeof(this->info)));
+    if (ret != static_cast<int>(sizeof(this->info))) {
+      Panic("Parent could not send %s RELRO info: %s",
+            this->name,
+            strerror(errno));
+    }
+  }
+
+  void ReceiveRelroInfo(int fd) {
+    // Receive relro information from parent.
+    int relro_fd = -1;
+    if (ReceiveFd(fd, &relro_fd) < 0) {
+      Panic("Could not receive %s relro descriptor from parent", this->name);
+    }
+
+    printf("Child received %s relro fd %d\n", this->name, relro_fd);
+
+    int ret = TEMP_FAILURE_RETRY(::read(fd, &this->info, sizeof(this->info)));
+    if (ret != static_cast<int>(sizeof(this->info))) {
+      Panic("Could not receive %s relro information from parent", this->name);
+    }
+
+    this->info.relro_fd = relro_fd;
+    printf("Child received %s relro load=%p start=%p size=%p\n",
+           this->name,
+           (void*)this->info.load_address,
+           (void*)this->info.relro_start,
+           (void*)this->info.relro_size);
+  }
+
+  void UseSharedRelro(crazy_context_t* context) {
+    if (!crazy_library_use_relro_sharing(this->library,
+                                         this->info.relro_start,
+                                         this->info.relro_size,
+                                         this->info.relro_fd,
+                                         context)) {
+      Panic("Could not use %s shared RELRO: %s\n",
+            this->name,
+            crazy_context_get_error(context));
+    }
+  }
+};
+
+int main() {
+
+  if (!crazy_system_can_share_relro()) {
+    fprintf(stderr, "WARNING: Test ignored due to broken kernel!!\n");
+    return 0;
+  }
+
+  crazy_context_t* context = crazy_context_create();
+
+  Library foo;
+  Library bar;
+
+  crazy_context_add_search_path_for_address(context, (void*)&main);
+
+  // Load libfoo_with_relro.so
+  crazy_context_set_load_address(context, 0x20000000);
+  foo.Init("libfoo_with_relro.so", context);
+
+  crazy_context_set_load_address(context, 0x20800000);
+  bar.Init("libbar_with_relro.so", context);
+
+  printf("Libraries loaded\n");
+
+  int pipes[2];
+  if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipes) < 0)
+    Panic("Could not create socket pair: %s", strerror(errno));
+
+  pid_t child = fork();
+  if (child < 0)
+    Panic("Could not fork test program!");
+
+  if (child == 0) {
+    // In the child.
+    printf("Child waiting for foo relro fd\n");
+
+    foo.ReceiveRelroInfo(pipes[0]);
+    foo.UseSharedRelro(context);
+
+    printf("Child waiting for bar relro fd\n");
+    bar.ReceiveRelroInfo(pipes[0]);
+    bar.UseSharedRelro(context);
+
+    printf("RELROs used in child process\n");
+
+    CheckRelroMaps(2);
+
+    FunctionPtr bar_func;
+    if (!crazy_library_find_symbol(
+             bar.library, "Bar", reinterpret_cast<void**>(&bar_func)))
+      Panic("Could not find 'Bar' in library");
+
+    printf("Calling Bar()\n");
+    (*bar_func)();
+
+    printf("Bar() called, exiting\n");
+
+    exit(0);
+
+  } else {
+    // In the parent.
+
+    printf("Parent enabling foo RELRO sharing\n");
+
+    foo.EnableSharedRelro(context);
+    foo.SendRelroInfo(pipes[1]);
+
+    printf("Parent enabling bar RELRO sharing\n");
+
+    bar.EnableSharedRelro(context);
+    bar.SendRelroInfo(pipes[1]);
+
+    printf("RELROs enabled and sent to child\n");
+
+    CheckRelroMaps(2);
+
+    printf("Parent waiting for child\n");
+
+    // Wait for child to complete.
+    int status;
+    waitpid(child, &status, 0);
+
+    if (WIFSIGNALED(status))
+      Panic("Child terminated by signal!!\n");
+    else if (WIFEXITED(status)) {
+      int child_status = WEXITSTATUS(status);
+      if (child_status != 0)
+        Panic("Child terminated with status=%d\n", child_status);
+    } else
+      Panic("Child exited for unknown reason!!\n");
+  }
+
+  printf("Closing libraries\n");
+  bar.Close();
+  foo.Close();
+
+  crazy_context_destroy(context);
+  return 0;
+}
diff --git a/sources/android/crazy_linker/tests/test_search_path_list.cpp b/sources/android/crazy_linker/tests/test_search_path_list.cpp
new file mode 100644
index 0000000..17ffa58
--- /dev/null
+++ b/sources/android/crazy_linker/tests/test_search_path_list.cpp
@@ -0,0 +1,134 @@
+// 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.
+
+// A crazy linker test to check that paths added with
+// crazy_context_add_search_path() or
+// crazy_context_add_search_path_from_address()
+// have higher priority than the default ones (from LD_LIBRARY_PATH).
+//
+// This requires creating two temporary directories and placing there libraries
+// with the same name, but different content
+//
+//   $TMPDIR1/libfoo.so   -> a copy of libfoo.so that contains the 'Foo' symbol.
+//   $TMPDIR2/libfoo.so   -> a copy of libfoo2.so that contains the 'Foo2'
+// symbol.
+//
+
+#include <crazy_linker.h>
+
+#include "test_util.h"
+
+namespace {
+
+void CheckLibraryCantLoad(const char* library_name, crazy_context_t* context) {
+  crazy_library_t* library;
+
+  if (crazy_library_open(&library, library_name, context))
+    Panic("Could load library %s, expected this not to be possible\n",
+          library_name);
+}
+
+// Loads a library named |library_name| and checks that it contains
+// the symbols listed in |wanted_symbols|, and none of the symbols
+// listed in |unwanted_symbols|. After that, close the library and exit.
+//
+// Both |wanted_symbols| and |unwanted_symbols| are NULL-terminated
+// arrays of strings.
+void CheckLibrary(const char* library_name,
+                  const char* const* wanted_symbols,
+                  const char* const* unwanted_symbols,
+                  crazy_context_t* context) {
+  crazy_library_t* library;
+
+  if (!crazy_library_open(&library, library_name, context))
+    Panic("Could not open library %s: %s\n", crazy_context_get_error(context));
+
+  size_t failures = 0;
+
+  if (wanted_symbols) {
+    for (; *wanted_symbols; ++wanted_symbols) {
+      const char* symbol_name = *wanted_symbols;
+      void* symbol_addr;
+      if (!crazy_library_find_symbol(library, symbol_name, &symbol_addr)) {
+        fprintf(stderr,
+                "Could not find symbol '%s' in library '%s'!\n",
+                symbol_name,
+                library_name);
+        failures += 1;
+      }
+    }
+  }
+
+  if (unwanted_symbols) {
+    for (; *unwanted_symbols; ++unwanted_symbols) {
+      const char* symbol_name = *unwanted_symbols;
+      void* symbol_addr;
+      if (crazy_library_find_symbol(library, symbol_name, &symbol_addr)) {
+        fprintf(stderr,
+                "Found symbol '%s' in library '%s', none expected!\n",
+                symbol_name,
+                library_name);
+        failures += 1;
+      }
+    }
+  }
+
+  if (failures > 0)
+    Panic("Found %d symbol failures in library %s\n", failures, library_name);
+
+  crazy_library_close(library);
+}
+
+}  // namespace
+
+int main() {
+  String exe_path = GetCurrentExecutableDirectory();
+
+  TempDirectory temp_dir_1;
+  TempDirectory temp_dir_2;
+
+  // List of symbols in original libfoo.so and libfoo2.so, respectively.
+  static const char* const kFooSymbols[] = {"Foo", NULL};
+  static const char* const kFoo2Symbols[] = {"Foo2", NULL};
+
+  // Copy libfoo.so to $TMPDIR1/libfoo.so
+  CopyFile("libfoo.so", exe_path.c_str(), "libfoo.so", temp_dir_1.path());
+
+  // Copy libfoo2.so to $TMPDIR2/libfoo.so
+  CopyFile("libfoo2.so", exe_path.c_str(), "libfoo.so", temp_dir_2.path());
+
+  // Create a new context object.
+  crazy_context_t* context = crazy_context_create();
+  crazy_library_t* library;
+
+  // Reset search paths to a non-existing directory. Check that the library
+  // can't be loaded.
+  setenv("LD_LIBRARY_PATH", "/this-directory-does-not-exist", 1);
+  crazy_context_reset_search_paths(context);
+  CheckLibraryCantLoad("libfoo.so", context);
+
+  // Add the search path to the current executable, this should load the
+  // original
+  // libfoo.so.
+  crazy_context_add_search_path_for_address(context, (void*)&main);
+  CheckLibrary("libfoo.so", kFooSymbols, kFoo2Symbols, context);
+
+  // Reset search paths to use $TMPDIR2 then $TMPDIR1
+  setenv("LD_LIBRARY_PATH", temp_dir_1.path(), 1);
+  crazy_context_reset_search_paths(context);
+  crazy_context_add_search_path(context, temp_dir_2.path());
+
+  // Check that the copy of libfoo2.so is loaded.
+  CheckLibrary("libfoo.so", kFoo2Symbols, kFooSymbols, context);
+
+  // Reset search paths to use only $TMPDIR1
+  crazy_context_reset_search_paths(context);
+
+  // Check that the copy of libfoo.so is loaded.
+  CheckLibrary("libfoo.so", kFooSymbols, kFoo2Symbols, context);
+
+  crazy_context_destroy(context);
+
+  return 0;
+}
\ No newline at end of file
diff --git a/sources/android/crazy_linker/tests/test_util.h b/sources/android/crazy_linker/tests/test_util.h
new file mode 100644
index 0000000..c236752
--- /dev/null
+++ b/sources/android/crazy_linker/tests/test_util.h
@@ -0,0 +1,370 @@
+// 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.
+
+// A set of common helper functions used by crazy_linker tests.
+// IMPORTANT: ALL FUNCTIONS HERE ARE INLINED. This avoids adding a
+// dependency on another source file for all tests that include this
+// header.
+
+#ifndef TEST_UTIL_H
+#define TEST_UTIL_H
+
+#include <errno.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+namespace {
+
+// Print an error message and exit the process.
+// Message must be terminated by a newline.
+inline void Panic(const char* fmt, ...) {
+  va_list args;
+  fprintf(stderr, "PANIC: ");
+  va_start(args, fmt);
+  vfprintf(stderr, fmt, args);
+  va_end(args);
+  exit(1);
+}
+
+// Print an error message, the errno message, then exit the process.
+// Message must not be terminated by a newline.
+inline void PanicErrno(const char* fmt, ...) {
+  int old_errno = errno;
+  va_list args;
+  fprintf(stderr, "PANIC: ");
+  va_start(args, fmt);
+  vfprintf(stderr, fmt, args);
+  va_end(args);
+  fprintf(stderr, ": %s\n", strerror(old_errno));
+  exit(1);
+}
+
+// Simple string class.
+class String {
+ public:
+  String() : str_(NULL), len_(0) {}
+
+  String(const String& other) { String(other.str_, other.len_); }
+
+  String(const char* str) { String(str, strlen(str)); }
+
+  String(const char* str, size_t len) : str_(NULL), len_(0) {
+    Append(str, len);
+  }
+
+  ~String() {
+    if (str_) {
+      free(str_);
+      str_ = NULL;
+    }
+  }
+
+  String& operator+=(const char* str) {
+    Append(str, strlen(str));
+    return *this;
+  }
+
+  String& operator+=(const String& other) {
+    Append(other.str_, other.len_);
+    return *this;
+  }
+
+  String& operator+=(char ch) {
+    Append(&ch, 1);
+    return *this;
+  }
+
+  const char* c_str() const { return len_ ? str_ : ""; }
+  char* ptr() { return str_; }
+  size_t size() const { return len_; }
+
+  void Append(const char* str, size_t len) {
+    size_t old_len = len_;
+    Resize(len_ + len);
+    memcpy(str_ + old_len, str, len);
+  }
+
+  void Resize(size_t len) {
+    str_ = reinterpret_cast<char*>(realloc(str_, len + 1));
+    if (len > len_)
+      memset(str_ + len_, '\0', len - len_);
+    str_[len] = '\0';
+    len_ = len;
+  }
+
+  void Format(const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    Resize(128);
+    for (;;) {
+      va_list args2;
+      va_copy(args2, args);
+      int ret = vsnprintf(str_, len_ + 1, fmt, args2);
+      va_end(args2);
+      if (ret < static_cast<int>(len_ + 1))
+        break;
+
+      Resize(len_ * 2);
+    }
+  }
+
+ private:
+  char* str_;
+  size_t len_;
+};
+
+// Helper class to create a temporary directory that gets deleted on scope exit,
+// as well as all regular files it contains.
+class TempDirectory {
+ public:
+  TempDirectory() {
+    snprintf(path_, sizeof path_, "/data/local/tmp/temp-XXXXXX");
+    if (!mktemp(path_))
+      Panic("Could not create temporary directory name: %s\n", strerror(errno));
+    if (mkdir(path_, 0700) < 0)
+      Panic("Could not create temporary directory %s: %s\n", strerror(errno));
+  }
+
+  ~TempDirectory() {
+    // Remove any file in this directory.
+    DIR* d = opendir(path_);
+    if (!d)
+      Panic("Could not open directory %s: %s\n", strerror(errno));
+
+    struct dirent* entry;
+    while ((entry = readdir(d)) != NULL) {
+      if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
+        continue;
+      String file_path;
+      file_path.Format("%s/%s", path_, entry->d_name);
+      if (unlink(file_path.c_str()) < 0)
+        Panic("Could not remove %s: %s\n", file_path.c_str(), strerror(errno));
+    }
+    closedir(d);
+
+    if (rmdir(path_) < 0)
+      Panic("Could not remove dir %s: %s\n", path_, strerror(errno));
+  }
+
+  const char* path() const { return path_; }
+
+ private:
+  char path_[PATH_MAX];
+};
+
+// Scoped FILE* class. Always closed on destruction.
+class ScopedFILE {
+ public:
+  ScopedFILE() : file_(NULL) {}
+
+  ~ScopedFILE() {
+    if (file_) {
+      fclose(file_);
+      file_ = NULL;
+    }
+  }
+
+  void Open(const char* path, const char* mode) {
+    file_ = fopen(path, mode);
+    if (!file_)
+      Panic("Could not open file for reading: %s: %s\n", path, strerror(errno));
+  }
+
+  FILE* file() const { return file_; }
+
+ private:
+  FILE* file_;
+};
+
+// Retrieve current executable path as a String.
+inline String GetCurrentExecutable() {
+  String path;
+  path.Resize(PATH_MAX);
+  ssize_t ret =
+      TEMP_FAILURE_RETRY(readlink("/proc/self/exe", path.ptr(), path.size()));
+  if (ret < 0)
+    Panic("Could not read /proc/self/exe: %s\n", strerror(errno));
+
+  return path;
+}
+
+// Retrieve current executable directory as a String.
+inline String GetCurrentExecutableDirectory() {
+  String path = GetCurrentExecutable();
+  // Find basename.
+  const char* p = reinterpret_cast<const char*>(strrchr(path.c_str(), '/'));
+  if (p == NULL)
+    Panic("Current executable does not have directory root?: %s\n",
+          path.c_str());
+
+  path.Resize(p - path.c_str());
+  return path;
+}
+
+// Copy a file named |src_file_name| in directory |src_file_dir| into
+// a file named |dst_file_name| in directory |dst_file_dir|. Panics on error.
+inline void CopyFile(const char* src_file_name,
+                     const char* src_file_dir,
+                     const char* dst_file_name,
+                     const char* dst_file_dir) {
+  String src_path;
+  src_path.Format("%s/%s", src_file_dir, src_file_name);
+
+  ScopedFILE src_file;
+  src_file.Open(src_path.c_str(), "rb");
+
+  String dst_path;
+  dst_path.Format("%s/%s", dst_file_dir, dst_file_name);
+  ScopedFILE dst_file;
+  dst_file.Open(dst_path.c_str(), "wb");
+
+  char buffer[8192];
+  for (;;) {
+    size_t read = fread(buffer, 1, sizeof buffer, src_file.file());
+    if (read > 0) {
+      size_t written = fwrite(buffer, 1, read, dst_file.file());
+      if (written != read)
+        Panic("Wrote %d bytes instead of %d into %s\n",
+              written,
+              read,
+              dst_path.c_str());
+    }
+    if (read < sizeof buffer)
+      break;
+  }
+}
+
+// Send a file descriptor |fd| through |socket|.
+// Return 0 on success, -1/errno on failure.
+inline int SendFd(int socket, int fd) {
+  struct iovec iov;
+
+  char buffer[1];
+  buffer[0] = 0;
+
+  iov.iov_base = buffer;
+  iov.iov_len = 1;
+
+  struct msghdr msg;
+  struct cmsghdr* cmsg;
+  char cms[CMSG_SPACE(sizeof(int))];
+
+  ::memset(&msg, 0, sizeof(msg));
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = reinterpret_cast<caddr_t>(cms);
+  msg.msg_controllen = CMSG_LEN(sizeof(int));
+
+  cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  ::memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+
+  int ret = sendmsg(socket, &msg, 0);
+  if (ret < 0)
+    return -1;
+
+  if (ret != iov.iov_len) {
+    errno = EIO;
+    return -1;
+  }
+
+  return 0;
+}
+
+inline int ReceiveFd(int socket, int* fd) {
+  char buffer[1];
+  struct iovec iov;
+
+  iov.iov_base = buffer;
+  iov.iov_len = 1;
+
+  struct msghdr msg;
+  struct cmsghdr* cmsg;
+  char cms[CMSG_SPACE(sizeof(int))];
+
+  ::memset(&msg, 0, sizeof msg);
+  msg.msg_name = 0;
+  msg.msg_namelen = 0;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+
+  msg.msg_control = reinterpret_cast<caddr_t>(cms);
+  msg.msg_controllen = sizeof(cms);
+
+  int ret = recvmsg(socket, &msg, 0);
+  if (ret < 0)
+    return -1;
+  if (ret == 0) {
+    errno = EIO;
+    return -1;
+  }
+
+  cmsg = CMSG_FIRSTHDR(&msg);
+  ::memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+  return 0;
+}
+
+// Check that there are exactly |expected_count| memory mappings in
+// /proc/self/maps that point to a RELRO ashmem region.
+inline void CheckRelroMaps(int expected_count) {
+  printf("Checking for %d RELROs in /proc/self/maps\n", expected_count);
+
+  FILE* file = fopen("/proc/self/maps", "rb");
+  if (!file)
+    Panic("Could not open /proc/self/maps (pid %d): %s\n",
+          getpid(),
+          strerror(errno));
+
+  char line[512];
+  int count_relros = 0;
+  printf("proc/%d/maps:\n", getpid());
+  while (fgets(line, sizeof line, file)) {
+    if (strstr(line, "with_relro")) {
+      // The supported library names are "lib<name>_with_relro.so".
+      printf("%s", line);
+      if (strstr(line, "/dev/ashmem/RELRO:")) {
+        count_relros++;
+        // Check that they are read-only mappings.
+        if (!strstr(line, " r--"))
+          Panic("Shared RELRO mapping is not readonly!\n");
+        // Check that they can't be remapped read-write.
+        unsigned vma_start, vma_end;
+        if (sscanf(line, "%x-%x", &vma_start, &vma_end) != 2)
+          Panic("Could not parse VM address range!\n");
+        int ret = ::mprotect(
+            (void*)vma_start, vma_end - vma_start, PROT_READ | PROT_WRITE);
+        if (ret == 0)
+          Panic("Could remap shared RELRO as writable, should not happen!\n");
+
+        if (errno != EACCES)
+          Panic("remapping shared RELRO to writable failed with: %s\n",
+                strerror(errno));
+      }
+    }
+  }
+  fclose(file);
+
+  if (count_relros != expected_count)
+    Panic(
+        "Invalid shared RELRO sections in /proc/self/maps: %d"
+        " (expected %d)\n",
+        count_relros,
+        expected_count);
+
+  printf("RELRO count check ok!\n");
+}
+
+}  // namespace
+
+#endif  // TEST_UTIL_H
diff --git a/sources/android/crazy_linker/tests/zoo.cpp b/sources/android/crazy_linker/tests/zoo.cpp
new file mode 100644
index 0000000..f070933
--- /dev/null
+++ b/sources/android/crazy_linker/tests/zoo.cpp
@@ -0,0 +1,35 @@
+// 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 <dlfcn.h>
+#include <stdio.h>
+
+extern "C" bool Zoo() {
+  printf("%s: Entering\n", __FUNCTION__);
+  void* bar_lib = dlopen("libbar.so", RTLD_NOW);
+  if (!bar_lib) {
+    fprintf(stderr, "Could not libbar.so: %s\n", dlerror());
+    return false;
+  }
+  printf("%s: Opened libbar.so @%p\n", __FUNCTION__, bar_lib);
+
+  void (*bar_func)();
+
+  bar_func = reinterpret_cast<void (*)()>(dlsym(bar_lib, "Bar"));
+  if (!bar_func) {
+    fprintf(stderr, "Could not find 'Bar' symbol in libbar.so\n");
+    return false;
+  }
+  printf("%s: Found 'Bar' symbol at @%p\n", __FUNCTION__, bar_func);
+
+  // Call it.
+  printf("%s: Calling Bar()\n", __FUNCTION__);
+  (*bar_func)();
+
+  printf("%s: Closing libbar.so\n", __FUNCTION__);
+  dlclose(bar_lib);
+
+  printf("%s: Exiting\n", __FUNCTION__);
+  return true;
+}
diff --git a/sources/android/support/tests/minitest/minitest.cc b/sources/android/support/tests/minitest/minitest.cc
index 7b87e66..b918006 100644
--- a/sources/android/support/tests/minitest/minitest.cc
+++ b/sources/android/support/tests/minitest/minitest.cc
@@ -1,9 +1,9 @@
 #include "minitest.h"
 
-#include <wchar.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <wchar.h>
 
 namespace {
 
@@ -71,12 +71,12 @@
 }
 
 #define MINITEST_STRING_OPERATOR_LL_(ParamType, Format) \
-String& String::operator<<(ParamType v) { \
-  char buf[20]; \
-  ::snprintf(buf, sizeof(buf), Format, v); \
-  (*this) += buf; \
-  return *this; \
-}
+  String& String::operator<<(ParamType v) {             \
+    char buf[20];                                       \
+    ::snprintf(buf, sizeof(buf), Format, v);            \
+    (*this) += buf;                                     \
+    return *this;                                       \
+  }
 
 MINITEST_STRING_OPERATOR_LL_(signed char, "%hhd")
 MINITEST_STRING_OPERATOR_LL_(unsigned char, "%hhu")
@@ -131,13 +131,13 @@
   int len;
   for (;;) {
     va_copy(args2, args);
-    len = snprintf(&result[0], result.size(), format, args2);
+    len = vsnprintf(&result[0], result.size(), format, args2);
     va_end(args2);
     // On Windows, snprintf() returns -1 on truncation. On other
     // platforms, it returns the size of the string, without truncation.
     if (len >= 0 && static_cast<size_t>(len) <= result.size())
       break;
-    result.Resize(result.size()*2);
+    result.Resize(result.size() * 2);
   }
   va_end(args);
   return result;
@@ -184,7 +184,7 @@
   TestInfo* info = g_test_infos;
   unsigned num_failures = 0;
   unsigned num_tests = 0;
-  for ( ; info != NULL; info = info->next) {
+  for (; info != NULL; info = info->next) {
     minitest::TestCase testcase;
     printf("[ RUNNING   ] %s.%s\n", info->test_name, info->case_name);
     num_tests += 1;
@@ -209,4 +209,3 @@
 
   return (num_failures > 0);
 }
-
diff --git a/sources/android/support/tests/minitest/minitest.h b/sources/android/support/tests/minitest/minitest.h
index e375305..4d9a533 100644
--- a/sources/android/support/tests/minitest/minitest.h
+++ b/sources/android/support/tests/minitest/minitest.h
@@ -117,26 +117,20 @@
 //    str << minitest::Format("Hex value %08x\n", x);
 //
 class String {
-public:
+ public:
   String() : str_(NULL), size_(0), capacity_(0) {}
   String(const char* str, size_t len);
 
-  explicit String(const char* str) {
-    String(str, ::strlen(str));
-  }
+  explicit String(const char* str) { String(str, ::strlen(str)); }
 
-  String(const String& other) {
-    String(other.str_, other.size_);
-  }
+  String(const String& other) { String(other.str_, other.size_); }
 
   String& operator=(const String& other) {
     (*this) += other;
     return *this;
   }
 
-  char& operator[](size_t index) {
-    return str_[index];
-  }
+  char& operator[](size_t index) { return str_[index]; }
 
   ~String() { Clear(); }
 
@@ -165,8 +159,7 @@
   String& operator<<(const String& other);
   String& operator<<(const char* str);
 
-#define MINITEST_OPERATOR_LL_(ParamType) \
-    String& operator<<(ParamType v)
+#define MINITEST_OPERATOR_LL_(ParamType) String& operator<<(ParamType v)
 
   MINITEST_OPERATOR_LL_(bool);
   MINITEST_OPERATOR_LL_(char);
@@ -189,7 +182,7 @@
   void Clear();
   void Resize(size_t new_size);
 
-private:
+ private:
   void Reserve(size_t new_capacity);
   char* str_;
   size_t size_;
@@ -209,7 +202,7 @@
 internal::String Format(const char* format, ...);
 
 class TestCase {
-public:
+ public:
   enum Result {
     PASS = 0,
     FAIL,
@@ -226,13 +219,13 @@
 
   internal::String& GetText();
 
-private:
+ private:
   Result result_;
   internal::String text_;
 };
 
 // Type of a function defined through the TEST(<test>,<case>) macro.
-typedef void (TestFunction)(TestCase* testcase);
+typedef void(TestFunction)(TestCase* testcase);
 
 // Used internally to register new test functions.
 void RegisterTest(const char* test_name,
@@ -240,20 +233,20 @@
                   TestFunction* test_function);
 
 #define MINITEST_TEST_FUNCTION(testname, casename) \
-    MINITEST_TEST_FUNCTION_(testname, casename)
+  MINITEST_TEST_FUNCTION_(testname, casename)
 
 #define MINITEST_TEST_FUNCTION_(testname, casename) \
-    minitest_##testname##_##casename
+  minitest_##testname##_##casename
 
-#define TEST(testname, casename) \
+#define TEST(testname, casename)                                               \
   static void MINITEST_TEST_FUNCTION(testname, casename)(minitest::TestCase*); \
-  static void __attribute__((constructor)) \
-  RegisterMiniTest##testname##_##casename() { \
-    minitest::RegisterTest(#testname, #casename, \
-                           &MINITEST_TEST_FUNCTION(testname, casename)); \
-  } \
-  void MINITEST_TEST_FUNCTION(testname, casename)(\
-      minitest::TestCase* minitest_testcase)
+  static void                                                                  \
+      __attribute__((constructor)) RegisterMiniTest##testname##_##casename() { \
+    minitest::RegisterTest(                                                    \
+        #testname, #casename, &MINITEST_TEST_FUNCTION(testname, casename));    \
+  }                                                                            \
+  void MINITEST_TEST_FUNCTION(testname,                                        \
+                              casename)(minitest::TestCase* minitest_testcase)
 
 // Use this macro to add context text before an EXPECT or ASSERT statement
 // For example:
@@ -270,147 +263,240 @@
 
 // EXPECT_TRUE() must evaluate to something that supports the << operator
 // to receive debugging strings only in case of failure.
-#define EXPECT_TRUE(expression) \
-  do { \
-    if (!(expression)) { \
-      printf("EXPECT_TRUE:%s:%d: expression '%s' returned 'false', expected 'true'\n", \
-             __FILE__, __LINE__, #expression); \
-      minitest_testcase->Failure(); \
-    } \
+#define EXPECT_TRUE(expression)                                            \
+  do {                                                                     \
+    if (!(expression)) {                                                   \
+      printf(                                                              \
+          "EXPECT_TRUE:%s:%d: expression '%s' returned 'false', expected " \
+          "'true'\n",                                                      \
+          __FILE__,                                                        \
+          __LINE__,                                                        \
+          #expression);                                                    \
+      minitest_testcase->Failure();                                        \
+    }                                                                      \
   } while (0)
 
-#define EXPECT_FALSE(expression) \
-  do { \
-    if (!!(expression)) { \
-      printf("EXPECT_FALSE:%s:%d: expression '%s' returned 'true', expected 'false'\n", \
-             __FILE__, __LINE__, #expression); \
-      minitest_testcase->Failure(); \
-    } \
+#define EXPECT_FALSE(expression)                                           \
+  do {                                                                     \
+    if (!!(expression)) {                                                  \
+      printf(                                                              \
+          "EXPECT_FALSE:%s:%d: expression '%s' returned 'true', expected " \
+          "'false'\n",                                                     \
+          __FILE__,                                                        \
+          __LINE__,                                                        \
+          #expression);                                                    \
+      minitest_testcase->Failure();                                        \
+    }                                                                      \
   } while (0)
 
-#define MINITEST_DEFINE_LOCAL_EXPR_(varname, expr) \
-  typedef minitest::internal::AddConst<__typeof__(expr)>::type \
-    varname##Type; \
+#define MINITEST_DEFINE_LOCAL_EXPR_(varname, expr)                            \
+  typedef minitest::internal::AddConst<__typeof__(expr)>::type varname##Type; \
   const varname##Type varname = (expr);
 
-#define MINITEST_EXPECT_ASSERT_BINOP_(opname, op, expected, expression, is_assert) \
-  do { \
+#define MINITEST_EXPECT_ASSERT_BINOP_(                        \
+    opname, op, expected, expression, is_assert)              \
+  do {                                                        \
     MINITEST_DEFINE_LOCAL_EXPR_(minitest_expected, expected); \
     MINITEST_DEFINE_LOCAL_EXPR_(minitest_actual, expression); \
-    if (!(minitest_actual op minitest_expected)) { \
-      printf("%s" #opname ":%s:%d: with expression '%s'\n", \
-             is_assert ? "ASSERT_" : "EXPECT_", __FILE__, __LINE__, \
-             #expression); \
-      minitest::internal::String minitest_str; \
-      minitest_str << minitest_actual; \
-      printf("actual   : %s\n", minitest_str.c_str()); \
-      minitest_str.Clear(); \
-      minitest_str << minitest_expected; \
-      printf("expected : %s\n", minitest_str.c_str()); \
-      if (is_assert) { \
-        minitest_testcase->FatalFailure(); \
-        return; \
-      } \
-      minitest_testcase->Failure(); \
-    } \
+    if (!(minitest_actual op minitest_expected)) {            \
+      printf("%s" #opname ":%s:%d: with expression '%s'\n",   \
+             is_assert ? "ASSERT_" : "EXPECT_",               \
+             __FILE__,                                        \
+             __LINE__,                                        \
+             #expression);                                    \
+      minitest::internal::String minitest_str;                \
+      minitest_str << minitest_actual;                        \
+      printf("actual   : %s\n", minitest_str.c_str());        \
+      minitest_str.Clear();                                   \
+      minitest_str << minitest_expected;                      \
+      printf("expected : %s\n", minitest_str.c_str());        \
+      if (is_assert) {                                        \
+        minitest_testcase->FatalFailure();                    \
+        return;                                               \
+      }                                                       \
+      minitest_testcase->Failure();                           \
+    }                                                         \
   } while (0)
 
 #define MINITEST_EXPECT_BINOP_(opname, op, expected, expression) \
-    MINITEST_EXPECT_ASSERT_BINOP_(opname, op, expected, expression, false)
+  MINITEST_EXPECT_ASSERT_BINOP_(opname, op, expected, expression, false)
 
-#define EXPECT_EQ(expected, expression)  \
-    MINITEST_EXPECT_BINOP_(EQ, ==, expected, expression)
+#define EXPECT_EQ(expected, expression) \
+  MINITEST_EXPECT_BINOP_(EQ, ==, expected, expression)
 
-#define EXPECT_NE(expected, expression)  \
-    MINITEST_EXPECT_BINOP_(NE, !=, expected, expression)
+#define EXPECT_NE(expected, expression) \
+  MINITEST_EXPECT_BINOP_(NE, !=, expected, expression)
 
-#define EXPECT_LE(expected, expression)  \
-    MINITEST_EXPECT_BINOP_(LE, <=, expected, expression)
+#define EXPECT_LE(expected, expression) \
+  MINITEST_EXPECT_BINOP_(LE, <=, expected, expression)
 
-#define EXPECT_LT(expected, expression)  \
-    MINITEST_EXPECT_BINOP_(LT, <, expected, expression)
+#define EXPECT_LT(expected, expression) \
+  MINITEST_EXPECT_BINOP_(LT, <, expected, expression)
 
-#define EXPECT_GE(expected, expression)  \
-    MINITEST_EXPECT_BINOP_(GE, >=, expected, expression)
+#define EXPECT_GE(expected, expression) \
+  MINITEST_EXPECT_BINOP_(GE, >=, expected, expression)
 
-#define EXPECT_GT(expected, expression)  \
-    MINITEST_EXPECT_BINOP_(GT, >, expected, expression)
+#define EXPECT_GT(expected, expression) \
+  MINITEST_EXPECT_BINOP_(GT, >, expected, expression)
 
 #define MINITEST_EXPECT_ASSERT_STR_(expected, expression, is_eq, is_assert) \
-  do { \
-    const char* minitest_prefix = is_assert ? "ASSERT_STR" : "EXPECT_STR"; \
-    const char* minitest_suffix = is_eq ? "EQ" : "NEQ"; \
-    const char* minitest_expected = (expected); \
-    const char* minitest_actual = (expression); \
-    if (minitest_actual == NULL) { \
-      printf("%s%s:%s:%d: expression '%s' is NULL!\n", \
-             minitest_prefix, minitest_suffix, \
-             __FILE__, __LINE__, #expression); \
-    } else { \
-      bool minitest_eq = !strcmp(minitest_expected, minitest_actual); \
-      if (minitest_eq != is_eq) { \
-        printf("%s%s:%s:%d: with expression '%s'\n", \
-               minitest_prefix, minitest_suffix, \
-               __FILE__, __LINE__, #expression); \
-        printf("actual   : %s\n", minitest_actual); \
-        printf("expected : %s\n", minitest_expected); \
-        minitest_testcase->Failure(); \
-      } \
-    } \
+  do {                                                                      \
+    const char* minitest_prefix = is_assert ? "ASSERT_STR" : "EXPECT_STR";  \
+    const char* minitest_suffix = is_eq ? "EQ" : "NEQ";                     \
+    const char* minitest_expected = (expected);                             \
+    const char* minitest_actual = (expression);                             \
+    if (minitest_actual == NULL) {                                          \
+      printf("%s%s:%s:%d: expression '%s' is NULL!\n",                      \
+             minitest_prefix,                                               \
+             minitest_suffix,                                               \
+             __FILE__,                                                      \
+             __LINE__,                                                      \
+             #expression);                                                  \
+      minitest_testcase->Failure();                                         \
+    } else {                                                                \
+      bool minitest_eq = !strcmp(minitest_expected, minitest_actual);       \
+      if (minitest_eq != is_eq) {                                           \
+        printf("%s%s:%s:%d: with expression '%s'\n",                        \
+               minitest_prefix,                                             \
+               minitest_suffix,                                             \
+               __FILE__,                                                    \
+               __LINE__,                                                    \
+               #expression);                                                \
+        printf("actual   : %s\n", minitest_actual);                         \
+        printf("expected : %s\n", minitest_expected);                       \
+        minitest_testcase->Failure();                                       \
+      }                                                                     \
+    }                                                                       \
   } while (0)
 
 #define EXPECT_STREQ(expected, expression) \
   MINITEST_EXPECT_ASSERT_STR_(expected, expression, true, false)
 
 #define EXPECT_STRNEQ(expected, expression) \
-  MINITEST_EXPECT_ASSERT_STR_(expected, expression, false, true)
+  MINITEST_EXPECT_ASSERT_STR_(expected, expression, false, false)
 
-#define ASSERT_TRUE(expression) \
-  do { \
-    if (!(expression)) { \
-      printf("ASSERT_TRUE:%s:%d: expression '%s' return 'false', expected 'true'\n", \
-             __FILE__, __LINE__, #expression); \
-      minitest_testcase->FatalFailure(); \
-      return; \
-    } \
+#define MINITEST_EXPECT_ASSERT_MEM_(                                           \
+    expected, expected_len, expression, expression_len, is_eq, is_assert)      \
+  do {                                                                         \
+    const char* minitest_prefix = is_assert ? "ASSERT_MEM" : "EXPECT_MEM";     \
+    const char* minitest_suffix = is_eq ? "EQ" : "NEQ";                        \
+    const char* minitest_expected = (expected);                                \
+    size_t minitest_expected_len = static_cast<size_t>(expected_len);          \
+    const char* minitest_actual = (expression);                                \
+    size_t minitest_actual_len = static_cast<size_t>(expression_len);          \
+    if (minitest_actual == NULL) {                                             \
+      printf("%s%s:%s:%d: expression '%s' is NULL!\n",                         \
+             minitest_prefix,                                                  \
+             minitest_suffix,                                                  \
+             __FILE__,                                                         \
+             __LINE__,                                                         \
+             #expression);                                                     \
+      minitest_testcase->Failure();                                            \
+    } else if (minitest_actual_len != minitest_expected_len) {                 \
+      printf("%s:%s:%s:%d: size mistmatch for expression '%s'\n",              \
+             minitest_prefix,                                                  \
+             minitest_suffix,                                                  \
+             __FILE__,                                                         \
+             __LINE__,                                                         \
+             #expression);                                                     \
+      printf("actual size   : %d (0x%x)\n",                                    \
+             minitest_actual_len,                                              \
+             minitest_actual_len);                                             \
+      printf("expected size : %d (0x%x)\n",                                    \
+             minitest_expected_len,                                            \
+             minitest_expected_len);                                           \
+      minitest_testcase->Failure();                                            \
+    } else {                                                                   \
+      bool minitest_eq =                                                       \
+          !memcmp(minitest_expected, minitest_actual, minitest_expected_len);  \
+      if (minitest_eq != is_eq) {                                              \
+        printf("%s%s:%s:%d: with expression '%s' of %d bytes\n",               \
+               minitest_prefix,                                                \
+               minitest_suffix,                                                \
+               __FILE__,                                                       \
+               __LINE__,                                                       \
+               #expression,                                                    \
+               minitest_expected_len);                                         \
+        printf("actual   : '%.*s'\n", minitest_expected_len, minitest_actual); \
+        printf(                                                                \
+            "expected : '%.*s'\n", minitest_expected_len, minitest_expected);  \
+        minitest_testcase->Failure();                                          \
+      }                                                                        \
+    }                                                                          \
   } while (0)
 
-#define ASSERT_FALSE(expression) \
-  do { \
-    if (!!(expression)) { \
-      printf("ASSERT_FALSE:%s:%d: expression '%s' return 'true', expected 'false'\n", \
-             __FILE__, __LINE__, #expression); \
-      minitest_testcase->FatalFailure(); \
-      return; \
-    } \
+#define EXPECT_MEMEQ(expected, expected_len, expression, expression_len) \
+  MINITEST_EXPECT_ASSERT_MEM_(                                           \
+      expected, expected_len, expression, expression_len, true, false)
+
+#define EXPECT_MEMNEQ(expected, expected_len, expression, expression_len) \
+  MINITEST_EXPECT_ASSERT_MEM_(                                            \
+      expected, expected_len, expression, expression_len, false, false)
+
+#define ASSERT_TRUE(expression)                                          \
+  do {                                                                   \
+    if (!(expression)) {                                                 \
+      printf(                                                            \
+          "ASSERT_TRUE:%s:%d: expression '%s' return 'false', expected " \
+          "'true'\n",                                                    \
+          __FILE__,                                                      \
+          __LINE__,                                                      \
+          #expression);                                                  \
+      minitest_testcase->FatalFailure();                                 \
+      return;                                                            \
+    }                                                                    \
+  } while (0)
+
+#define ASSERT_FALSE(expression)                                         \
+  do {                                                                   \
+    if (!!(expression)) {                                                \
+      printf(                                                            \
+          "ASSERT_FALSE:%s:%d: expression '%s' return 'true', expected " \
+          "'false'\n",                                                   \
+          __FILE__,                                                      \
+          __LINE__,                                                      \
+          #expression);                                                  \
+      minitest_testcase->FatalFailure();                                 \
+      return;                                                            \
+    }                                                                    \
   } while (0)
 
 #define MINITEST_ASSERT_BINOP_(opname, op, expected, expression) \
-    MINITEST_EXPECT_ASSERT_BINOP_(opname, op, expected, expression, true)
+  MINITEST_EXPECT_ASSERT_BINOP_(opname, op, expected, expression, true)
 
-#define ASSERT_EQ(expected, expression)  \
-    MINITEST_ASSERT_BINOP_(EQ, ==, expected, expression)
+#define ASSERT_EQ(expected, expression) \
+  MINITEST_ASSERT_BINOP_(EQ, ==, expected, expression)
 
-#define ASSERT_NE(expected, expression)  \
-    MINITEST_ASSERT_BINOP_(NE, !=, expected, expression)
+#define ASSERT_NE(expected, expression) \
+  MINITEST_ASSERT_BINOP_(NE, !=, expected, expression)
 
-#define ASSERT_LE(expected, expression)  \
-    MINITEST_ASSERT_BINOP_(LE, <=, expected, expression)
+#define ASSERT_LE(expected, expression) \
+  MINITEST_ASSERT_BINOP_(LE, <=, expected, expression)
 
-#define ASSERT_LT(expected, expression)  \
-    MINITEST_ASSERT_BINOP_(LT, <, expected, expression)
+#define ASSERT_LT(expected, expression) \
+  MINITEST_ASSERT_BINOP_(LT, <, expected, expression)
 
-#define ASSERT_GE(expected, expression)  \
-    MINITEST_ASSERT_BINOP_(GE, >=, expected, expression)
+#define ASSERT_GE(expected, expression) \
+  MINITEST_ASSERT_BINOP_(GE, >=, expected, expression)
 
-#define ASSERT_GT(expected, expression)  \
-    MINITEST_ASSERT_BINOP_(GT, >, expected, expression)
+#define ASSERT_GT(expected, expression) \
+  MINITEST_ASSERT_BINOP_(GT, >, expected, expression)
 
 #define ASSERT_STREQ(expected, expression) \
-    MINITEST_EXPECT_ASSERT_STR_(expected, expression, true, true)
+  MINITEST_EXPECT_ASSERT_STR_(expected, expression, true, true)
 
 #define ASSERT_STRNEQ(expected, expression) \
-    MINITEST_EXPECT_ASSERT_STR_(expected, expression, false, true)
+  MINITEST_EXPECT_ASSERT_STR_(expected, expression, false, true)
+
+#define ASSERT_MEMEQ(expected, expected_len, expression, expression_len) \
+  MINITEST_EXPECT_ASSERT_MEM_(                                           \
+      expected, expected_len, expression, expression_len, true, true)
+
+#define ASSERT_MEMNEQ(expected, expected_len, expression, expression_len) \
+  MINITEST_EXPECT_ASSERT_MEM_(                                            \
+      expected, expected_len, expression, expression_len, false, true)
+
+#define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0]))
 
 }  // namespace minitest
 
diff --git a/tests/device/crazy_linker/jni/Android.mk b/tests/device/crazy_linker/jni/Android.mk
new file mode 100644
index 0000000..6a2d5ed
--- /dev/null
+++ b/tests/device/crazy_linker/jni/Android.mk
@@ -0,0 +1,6 @@
+# Define CRAZY_LINKER_SRC_DIR in your environment to redirect to a different
+# version of the sources. This is useful if you have your own copy of the
+# sources in a non-default location.
+CRAZY_LINKER_SRC_DIR ?= $(NDK_ROOT)/sources/android/crazy_linker
+
+include $(CRAZY_LINKER_SRC_DIR)/tests/Android.mk
diff --git a/tests/device/crazy_linker/jni/Application.mk b/tests/device/crazy_linker/jni/Application.mk
new file mode 100644
index 0000000..b448d58
--- /dev/null
+++ b/tests/device/crazy_linker/jni/Application.mk
@@ -0,0 +1,2 @@
+APP_ABI := all
+APP_PLATFORM := android-9