Merge "Add crazy_linker sources."
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