Update to Chromium //base at Chromium commit 01cb97b2e09618bbc3a60c7348f0a844eea20547.

This gets us to sometime during August 24, 2015.

TBR=jamesr@chromium.org

Review URL: https://codereview.chromium.org/2043183002 .
diff --git a/BUILD.gn b/BUILD.gn
index 2709e90..c646464 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -764,6 +764,11 @@
 
   # Windows.
   if (is_win) {
+    sources += [
+      "profiler/win32_stack_frame_unwinder.cc",
+      "profiler/win32_stack_frame_unwinder.h",
+    ]
+
     sources -= [
       "message_loop/message_pump_libevent.cc",
       "strings/string16.cc",
@@ -1247,6 +1252,7 @@
     "files/file_util_proxy_unittest.cc",
     "files/file_util_unittest.cc",
     "files/important_file_writer_unittest.cc",
+    "files/memory_mapped_file_unittest.cc",
     "files/scoped_temp_dir_unittest.cc",
     "gmock_unittest.cc",
     "guid_unittest.cc",
@@ -1306,6 +1312,7 @@
     "metrics/field_trial_unittest.cc",
     "metrics/histogram_base_unittest.cc",
     "metrics/histogram_delta_serialization_unittest.cc",
+    "metrics/histogram_macros_unittest.cc",
     "metrics/histogram_snapshot_manager_unittest.cc",
     "metrics/histogram_unittest.cc",
     "metrics/sample_map_unittest.cc",
@@ -1378,6 +1385,7 @@
     "test/histogram_tester_unittest.cc",
     "test/icu_test_util.cc",
     "test/icu_test_util.h",
+    "test/test_pending_task_unittest.cc",
     "test/test_reg_util_win_unittest.cc",
     "test/trace_event_analyzer_unittest.cc",
     "test/user_action_tester_unittest.cc",
diff --git a/DEPS b/DEPS
index c632e35..6d91c8d 100644
--- a/DEPS
+++ b/DEPS
@@ -4,6 +4,7 @@
   "+third_party/apple_apsl",
   "+third_party/libevent",
   "+third_party/dmg_fp",
+  "+third_party/lss",
   "+third_party/mach_override",
   "+third_party/modp_b64",
   "+third_party/tcmalloc",
diff --git a/allocator/BUILD.gn b/allocator/BUILD.gn
index 8944ad4..5a1e9df 100644
--- a/allocator/BUILD.gn
+++ b/allocator/BUILD.gn
@@ -4,6 +4,13 @@
 
 import("//build/config/allocator.gni")
 
+declare_args() {
+  # Provide a way to force disable debugallocation in Debug builds,
+  # e.g. for profiling (it's more rare to profile Debug builds,
+  # but people sometimes need to do that).
+  enable_debugallocation = is_debug
+}
+
 # Only executables and not libraries should depend on the allocator target;
 # only the application (the final executable) knows what allocator makes sense.
 # This "allocator" meta-target will forward to the default allocator according
@@ -32,6 +39,16 @@
   }
 }
 
+config("tcmalloc_flags") {
+  if (enable_debugallocation) {
+    defines = [
+      # Use debugallocation for Debug builds to catch problems early
+      # and cleanly, http://crbug.com/30715 .
+      "TCMALLOC_FOR_DEBUGALLOCATION",
+    ]
+  }
+}
+
 # This config and libc modification are only used on Windows.
 if (is_win) {
   import("//build/config/win/visual_studio_version.gni")
@@ -195,7 +212,10 @@
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
-    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [
+      "//build/config/compiler:no_chromium_code",
+      ":tcmalloc_flags",
+    ]
 
     deps = []
 
diff --git a/android/java/src/org/chromium/base/library_loader/LegacyLinker.java b/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
index da5ddab..8e6776a 100644
--- a/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
+++ b/android/java/src/org/chromium/base/library_loader/LegacyLinker.java
@@ -35,15 +35,10 @@
     // Log tag for this class.
     private static final String TAG = "cr.library_loader";
 
-    // Name of the library that contains our JNI code.
-    private static final String LINKER_JNI_LIBRARY = "chromium_android_linker";
-
     // Becomes true after linker initialization.
     private boolean mInitialized = false;
 
     // Set to true if this runs in the browser process. Disabled by initServiceProcess().
-    // TODO(petrcermak): This flag can be incorrectly set to false (even though this might run in
-    // the browser process) on low-memory devices.
     private boolean mInBrowserProcess = true;
 
     // Becomes true to indicate this process needs to wait for a shared RELRO in
@@ -57,19 +52,26 @@
     // The map of all RELRO sections either created or used in this process.
     private Bundle mSharedRelros = null;
 
-    // Current common random base load address.
-    private long mBaseLoadAddress = 0;
+    // Current common random base load address. A value of -1 indicates not yet initialized.
+    private long mBaseLoadAddress = -1;
 
     // Current fixed-location load address for the next library called by loadLibrary().
-    private long mCurrentLoadAddress = 0;
+    // A value of -1 indicates not yet initialized.
+    private long mCurrentLoadAddress = -1;
 
     // Becomes true once prepareLibraryLoad() has been called.
     private boolean mPrepareLibraryLoadCalled = false;
 
     // The map of libraries that are currently loaded in this process.
-    protected HashMap<String, LibInfo> mLoadedLibraries = null;
+    private HashMap<String, LibInfo> mLoadedLibraries = null;
 
-    // Used internally to initialize the linker's static data. Assume lock is held.
+    // Private singleton constructor, and singleton factory method.
+    private LegacyLinker() {}
+    static Linker create() {
+        return new LegacyLinker();
+    }
+
+    // Used internally to initialize the linker's data. Assume lock is held.
     private void ensureInitializedLocked() {
         assert Thread.holdsLock(mLock);
 
@@ -78,17 +80,8 @@
         }
 
         if (NativeLibraries.sUseLinker) {
-            if (DEBUG) {
-                Log.i(TAG, "Loading lib" + LINKER_JNI_LIBRARY + ".so");
-            }
-            try {
-                System.loadLibrary(LINKER_JNI_LIBRARY);
-            } catch (UnsatisfiedLinkError e) {
-                // In a component build, the ".cr" suffix is added to each library name.
-                Log.w(TAG, "Couldn't load lib" + LINKER_JNI_LIBRARY + ".so, "
-                                + "trying lib" + LINKER_JNI_LIBRARY + ".cr.so");
-                System.loadLibrary(LINKER_JNI_LIBRARY + ".cr");
-            }
+            // Load libchromium_android_linker.so.
+            loadLinkerJNILibrary();
 
             if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) {
                 if (SysUtils.isLowEndDevice()) {
@@ -137,7 +130,9 @@
         // Only GYP targets that are APKs and have the 'use_chromium_linker' variable
         // defined as 1 will use this linker. For all others (the default), the
         // auto-generated NativeLibraries.sUseLinker variable will be false.
-        if (!NativeLibraries.sUseLinker) return false;
+        if (!NativeLibraries.sUseLinker) {
+            return false;
+        }
 
         synchronized (mLock) {
             ensureInitializedLocked();
@@ -158,15 +153,6 @@
     }
 
     /**
-     * Call this method to determine if the chromium project must load
-     * the library directly from the zip file.
-     */
-    @Override
-    public boolean isInZipFile() {
-        return NativeLibraries.sUseLibraryInZipFile;
-    }
-
-    /**
      * Call this method just before loading any native shared libraries in this process.
      */
     @Override
@@ -175,6 +161,7 @@
             Log.i(TAG, "prepareLibraryLoad() called");
         }
         synchronized (mLock) {
+            ensureInitializedLocked();
             mPrepareLibraryLoadCalled = true;
 
             if (mInBrowserProcess) {
@@ -196,6 +183,7 @@
             Log.i(TAG, "finishLibraryLoad() called");
         }
         synchronized (mLock) {
+            ensureInitializedLocked();
             if (DEBUG) {
                 Log.i(TAG,
                         String.format(Locale.US,
@@ -232,7 +220,8 @@
                         try {
                             mLock.wait();
                         } catch (InterruptedException ie) {
-                            // no-op
+                            // Restore the thread's interrupt status.
+                            Thread.currentThread().interrupt();
                         }
                     }
                     useSharedRelrosLocked(mSharedRelros);
@@ -242,28 +231,9 @@
                 }
             }
 
-            if (NativeLibraries.sEnableLinkerTests && mTestRunnerClassName != null) {
-                // The TestRunner implementation must be instantiated _after_
-                // all libraries are loaded to ensure that its native methods
-                // are properly registered.
-                if (DEBUG) {
-                    Log.i(TAG, "Instantiating " + mTestRunnerClassName);
-                }
-                TestRunner testRunner = null;
-                try {
-                    testRunner = (TestRunner) Class.forName(mTestRunnerClassName).newInstance();
-                } catch (Exception e) {
-                    Log.e(TAG, "Could not extract test runner class name", e);
-                    testRunner = null;
-                }
-                if (testRunner != null) {
-                    if (!testRunner.runChecks(mMemoryDeviceConfig, mInBrowserProcess)) {
-                        Log.wtf(TAG, "Linker runtime tests failed in this process!!");
-                        assert false;
-                    } else {
-                        Log.i(TAG, "All linker tests passed!");
-                    }
-                }
+            // If testing, run tests now that all libraries are loaded and initialized.
+            if (NativeLibraries.sEnableLinkerTests) {
+                runTestRunnerClassForTesting(mMemoryDeviceConfig, mInBrowserProcess);
             }
         }
         if (DEBUG) {
@@ -276,6 +246,7 @@
      * used in this process. If initServiceProcess() was previously called,
      * finishLibraryLoad() will not exit until this method is called in another
      * thread with a non-null value.
+     *
      * @param bundle The Bundle instance containing a map of shared RELRO sections
      * to use in this process.
      */
@@ -309,6 +280,7 @@
     /**
      * Call this to retrieve the shared RELRO sections created in this process,
      * after loading all libraries.
+     *
      * @return a new Bundle instance, or null if RELRO sharing is disabled on
      * this system, or if initServiceProcess() was called previously.
      */
@@ -353,6 +325,7 @@
      * Call this method before loading any libraries to indicate that this
      * process is ready to reuse shared RELRO sections from another one.
      * Typically used when starting service processes.
+     *
      * @param baseLoadAddress the base library load address to use.
      */
     @Override
@@ -375,6 +348,7 @@
      * Retrieve the base load address of all shared RELRO sections.
      * This also enforces the creation of shared RELRO sections in
      * prepareLibraryLoad(), which can later be retrieved with getSharedRelros().
+     *
      * @return a common, random base load address, or 0 if RELRO sharing is
      * disabled.
      */
@@ -399,12 +373,11 @@
     // Used internally to lazily setup the common random base load address.
     private void setupBaseLoadAddressLocked() {
         assert Thread.holdsLock(mLock);
-        if (mBaseLoadAddress == 0) {
-            long address = computeRandomBaseLoadAddress();
-            mBaseLoadAddress = address;
-            mCurrentLoadAddress = address;
-            if (address == 0) {
-                // If the computed address is 0, there are issues with finding enough
+        if (mBaseLoadAddress == -1) {
+            mBaseLoadAddress = getRandomBaseLoadAddress();
+            mCurrentLoadAddress = mBaseLoadAddress;
+            if (mBaseLoadAddress == 0) {
+                // If the random address is 0 there are issues with finding enough
                 // free address space, so disable RELRO shared / fixed load addresses.
                 Log.w(TAG, "Disabling shared RELROs due address space pressure");
                 mBrowserUsesSharedRelro = false;
@@ -413,35 +386,6 @@
         }
     }
 
-    /**
-     * Compute a random base load address at which to place loaded libraries.
-     * @return new base load address, or 0 if the system does not support
-     * RELRO sharing.
-     */
-    private long computeRandomBaseLoadAddress() {
-        // nativeGetRandomBaseLoadAddress() returns an address at which it has previously
-        // successfully mapped an area of the given size, on the basis that we will be
-        // able, with high probability, to map our library into it.
-        //
-        // One issue with this is that we do not yet know the size of the library that
-        // we will load is. So here we pass a value that we expect will always be larger
-        // than that needed. If it is smaller the library mapping may still succeed. The
-        // other issue is that although highly unlikely, there is no guarantee that
-        // something else does not map into the area we are going to use between here and
-        // when we try to map into it.
-        //
-        // The above notes mean that all of this is probablistic. It is however okay to do
-        // because if, worst case and unlikely, we get unlucky in our choice of address,
-        // the back-out and retry without the shared RELRO in the ChildProcessService will
-        // keep things running.
-        final long maxExpectedBytes = 192 * 1024 * 1024;
-        final long address = nativeGetRandomBaseLoadAddress(maxExpectedBytes);
-        if (DEBUG) {
-            Log.i(TAG, String.format(Locale.US, "Random native base load address: 0x%x", address));
-        }
-        return address;
-    }
-
     // Used for debugging only.
     private void dumpBundle(Bundle bundle) {
         if (DEBUG) {
@@ -453,6 +397,7 @@
      * Use the shared RELRO section from a Bundle received form another process.
      * Call this after calling setBaseLoadAddress() then loading all libraries
      * with loadLibrary().
+     *
      * @param bundle Bundle instance generated with createSharedRelroBundle() in
      * another process.
      */
@@ -504,6 +449,8 @@
     }
 
     /**
+     * Implements loading a native shared library with the Chromium linker.
+     *
      * Load a native shared library with the Chromium linker. If the zip file
      * is not null, the shared library must be uncompressed and page aligned
      * inside the zipfile. Note the crazy linker treats libraries and files as
@@ -512,13 +459,16 @@
      *
      * @param zipFilePath The path of the zip file containing the library (or null).
      * @param libFilePath The path of the library (possibly in the zip file).
+     * @param isFixedAddressPermitted If true, uses a fixed load address if one was
+     * supplied, otherwise ignores the fixed address and loads wherever available.
      */
     @Override
-    public void loadLibrary(@Nullable String zipFilePath, String libFilePath) {
+    void loadLibraryImpl(
+            @Nullable String zipFilePath, String libFilePath, boolean isFixedAddressPermitted) {
         if (DEBUG) {
-            Log.i(TAG, "loadLibrary: " + zipFilePath + ", " + libFilePath);
+            Log.i(TAG, "loadLibraryImpl: " + zipFilePath + ", " + libFilePath + ", "
+                            + isFixedAddressPermitted);
         }
-
         synchronized (mLock) {
             ensureInitializedLocked();
 
@@ -541,9 +491,19 @@
 
             LibInfo libInfo = new LibInfo();
             long loadAddress = 0;
-            if ((mInBrowserProcess && mBrowserUsesSharedRelro) || mWaitForSharedRelros) {
-                // Load the library at a fixed address.
-                loadAddress = mCurrentLoadAddress;
+            if (isFixedAddressPermitted) {
+                if ((mInBrowserProcess && mBrowserUsesSharedRelro) || mWaitForSharedRelros) {
+                    // Load the library at a fixed address.
+                    loadAddress = mCurrentLoadAddress;
+
+                    // For multiple libraries, ensure we stay within reservation range.
+                    if (loadAddress > mBaseLoadAddress + ADDRESS_SPACE_RESERVATION) {
+                        String errorMessage =
+                                "Load address outside reservation, for: " + libFilePath;
+                        Log.e(TAG, errorMessage);
+                        throw new UnsatisfiedLinkError(errorMessage);
+                    }
+                }
             }
 
             String sharedRelRoName = libFilePath;
@@ -570,9 +530,10 @@
             // Where <library-name> is the library name, and <address> is the hexadecimal load
             // address.
             if (NativeLibraries.sEnableLinkerTests) {
-                Log.i(TAG, String.format(Locale.US, "%s_LIBRARY_ADDRESS: %s %x",
-                                   mInBrowserProcess ? "BROWSER" : "RENDERER", libFilePath,
-                                   libInfo.mLoadAddress));
+                String tag =
+                        mInBrowserProcess ? "BROWSER_LIBRARY_ADDRESS" : "RENDERER_LIBRARY_ADDRESS";
+                Log.i(TAG, String.format(
+                                   Locale.US, "%s: %s %x", tag, libFilePath, libInfo.mLoadAddress));
             }
 
             if (mInBrowserProcess) {
@@ -590,12 +551,13 @@
                 }
             }
 
-            if (mCurrentLoadAddress != 0) {
-                // Compute the next current load address. If mBaseLoadAddress
+            if (loadAddress != 0 && mCurrentLoadAddress != 0) {
+                // Compute the next current load address. If mCurrentLoadAddress
                 // is not 0, this is an explicit library load address. Otherwise,
                 // this is an explicit load address for relocated RELRO sections
                 // only.
-                mCurrentLoadAddress = libInfo.mLoadAddress + libInfo.mLoadSize;
+                mCurrentLoadAddress =
+                        libInfo.mLoadAddress + libInfo.mLoadSize + BREAKPAD_GUARD_REGION_BYTES;
             }
 
             mLoadedLibraries.put(sharedRelRoName, libInfo);
@@ -606,17 +568,8 @@
     }
 
     /**
-     * Determine whether a library is the linker library. Also deal with the
-     * component build that adds a .cr suffix to the name.
-     */
-    @Override
-    public boolean isChromiumLinkerLibrary(String library) {
-        return library.equals(LINKER_JNI_LIBRARY) || library.equals(LINKER_JNI_LIBRARY + ".cr");
-    }
-
-    /**
      * Move activity from the native thread to the main UI thread.
-     * Called from native code on its own thread.  Posts a callback from
+     * Called from native code on its own thread. Posts a callback from
      * the UI thread back to native code.
      *
      * @param opaque Opaque argument.
@@ -634,12 +587,14 @@
     /**
      * Native method to run callbacks on the main UI thread.
      * Supplied by the crazy linker and called by postCallbackOnMainThread.
+     *
      * @param opaque Opaque crazy linker arguments.
      */
     private static native void nativeRunCallbackOnUiThread(long opaque);
 
     /**
      * Native method used to load a library.
+     *
      * @param library Platform specific library name (e.g. libfoo.so)
      * @param loadAddress Explicit load address, or 0 for randomized one.
      * @param libInfo If not null, the mLoadAddress and mLoadSize fields
@@ -651,6 +606,7 @@
 
     /**
      * Native method used to load a library which is inside a zipfile.
+     *
      * @param zipfileName Filename of the zip file containing the library.
      * @param library Platform specific library name (e.g. libfoo.so)
      * @param loadAddress Explicit load address, or 0 for randomized one.
@@ -659,7 +615,7 @@
      * @return true for success, false otherwise.
      */
     private static native boolean nativeLoadLibraryInZipFile(
-            String zipfileName, String libraryName, long loadAddress, LibInfo libInfo);
+            @Nullable String zipfileName, String libraryName, long loadAddress, LibInfo libInfo);
 
     /**
      * Native method used to create a shared RELRO section.
@@ -667,6 +623,7 @@
      * nativeLoadLibrary(), this creates the RELRO for it. Otherwise,
      * this loads a new temporary library at the specified address,
      * creates and extracts the RELRO section from it, then unloads it.
+     *
      * @param library Library name.
      * @param loadAddress load address, which can be different from the one
      * used to load the library in the current process!
@@ -679,20 +636,10 @@
 
     /**
      * Native method used to use a shared RELRO section.
+     *
      * @param library Library name.
      * @param libInfo A LibInfo instance containing valid RELRO information
      * @return true on success.
      */
     private static native boolean nativeUseSharedRelro(String library, LibInfo libInfo);
-
-    /**
-     * Return a random address that should be free to be mapped with the given size.
-     * Maps an area of size bytes, and if successful then unmaps it and returns
-     * the address of the area allocated by the system (with ASLR). The idea is
-     * that this area should remain free of other mappings until we map our library
-     * into it.
-     * @param sizeBytes Size of area in bytes to search for.
-     * @return address to pass to future mmap, or 0 on error.
-     */
-    private static native long nativeGetRandomBaseLoadAddress(long sizeBytes);
 }
diff --git a/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index e4130ba..e581300 100644
--- a/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -186,7 +186,33 @@
         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
-    // Invoke System.loadLibrary(...), triggering JNI_OnLoad in native code
+    // Helper for loadAlreadyLocked(). Load a native shared library with the Chromium linker.
+    // Sets UMA flags depending on the results of loading.
+    private void loadLibrary(Linker linker, @Nullable String zipFilePath, String libFilePath) {
+        if (linker.isUsingBrowserSharedRelros()) {
+            // If the browser is set to attempt shared RELROs then we try first with shared
+            // RELROs enabled, and if that fails then retry without.
+            mIsUsingBrowserSharedRelros = true;
+            try {
+                linker.loadLibrary(zipFilePath, libFilePath);
+            } catch (UnsatisfiedLinkError e) {
+                Log.w(TAG, "Failed to load native library with shared RELRO, retrying without");
+                mLoadAtFixedAddressFailed = true;
+                linker.loadLibraryNoFixedAddress(zipFilePath, libFilePath);
+            }
+        } else {
+            // No attempt to use shared RELROs in the browser, so load as normal.
+            linker.loadLibrary(zipFilePath, libFilePath);
+        }
+
+        // Loaded successfully, so record if we loaded directly from an APK.
+        if (zipFilePath != null) {
+            mLibraryWasLoadedFromApk = true;
+        }
+    }
+
+    // Invoke either Linker.loadLibrary(...) or System.loadLibrary(...), triggering
+    // JNI_OnLoad in native code
     private void loadAlreadyLocked(Context context) throws ProcessInitException {
         try {
             if (!mLoaded) {
@@ -217,30 +243,14 @@
                         if (linker.isInZipFile()) {
                             // Load directly from the APK.
                             zipFilePath = apkFilePath;
-                            Log.i(TAG,
-                                    "Loading " + library + " directly from within " + apkFilePath);
+                            Log.i(TAG, "Loading " + library + " from within " + apkFilePath);
                         } else {
                             // The library is in its own file.
                             Log.i(TAG, "Loading " + library);
                         }
 
-                        // Load the library.
-                        boolean isLoaded = false;
-                        if (linker.isUsingBrowserSharedRelros()) {
-                            mIsUsingBrowserSharedRelros = true;
-                            try {
-                                loadLibrary(zipFilePath, libFilePath);
-                                isLoaded = true;
-                            } catch (UnsatisfiedLinkError e) {
-                                Log.w(TAG, "Failed to load native library with shared RELRO, "
-                                        + "retrying without");
-                                linker.disableSharedRelros();
-                                mLoadAtFixedAddressFailed = true;
-                            }
-                        }
-                        if (!isLoaded) {
-                            loadLibrary(zipFilePath, libFilePath);
-                        }
+                        // Load the library using this Linker. May throw UnsatisfiedLinkError.
+                        loadLibrary(linker, zipFilePath, libFilePath);
                     }
 
                     linker.finishLibraryLoad();
@@ -295,15 +305,6 @@
         return appInfo.sourceDir;
     }
 
-    // Load a native shared library with the Chromium linker. If the zip file
-    // path is not null, the library is loaded directly from the zip file.
-    private void loadLibrary(@Nullable String zipFilePath, String libFilePath) {
-        Linker.getInstance().loadLibrary(zipFilePath, libFilePath);
-        if (zipFilePath != null) {
-            mLibraryWasLoadedFromApk = true;
-        }
-    }
-
     // The WebView requires the Command Line to be switched over before
     // initialization is done. This is okay in the WebView's case since the
     // JNI is already loaded by this point.
diff --git a/android/java/src/org/chromium/base/library_loader/Linker.java b/android/java/src/org/chromium/base/library_loader/Linker.java
index c769339..ee62570 100644
--- a/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -56,7 +56,7 @@
  *
  * - Whether the browser process loads its native libraries at the same
  *   addresses as the service ones (to save RAM by sharing the RELRO too)
- *   depends on the configuration variable BROWSER_SHARED_RELRO_CONFIG below.
+ *   depends on the configuration variable BROWSER_SHARED_RELRO_CONFIG.
  *
  *   Not using fixed library addresses in the browser process is preferred
  *   for regular devices since it maintains the efficacy of ASLR as an
@@ -67,20 +67,14 @@
  *   it read-write (e.g. by calling mmap() or mprotect()) and modify its
  *   content, altering values seen in other service processes.
  *
- * - Unfortunately, certain Android systems use an old, buggy kernel, that
- *   doesn't check Ashmem region permissions correctly. See CVE-2011-1149
- *   for details. This linker probes the system on startup and will completely
- *   disable shared RELROs if it detects the problem. For the record, this is
- *   common for Android emulator system images (which are still based on 2.6.29)
- *
- * - Once the RELRO ashmem region is mapped into a service process' address
- *   space, the corresponding file descriptor is immediately closed. The
+ * - Once the RELRO ashmem region or file is mapped into a service process's
+ *   address space, the corresponding file descriptor is immediately closed. The
  *   file descriptor is kept opened in the browser process, because a copy needs
  *   to be sent to each new potential service process.
  *
  * - The common library load addresses are randomized for each instance of
- *   the program on the device. See computeRandomBaseLoadAddress() for more
- *   details on how this is computed.
+ *   the program on the device. See getRandomBaseLoadAddress() for more
+ *   details on how this is obtained.
  *
  * - When loading several libraries in service processes, a simple incremental
  *   approach from the original random base load address is used. This is
@@ -93,8 +87,8 @@
  * Here's an explanation of how this class is supposed to be used:
  *
  *  - Native shared libraries should be loaded with Linker.loadLibrary(),
- *    instead of System.loadLibrary(). The two functions take the same parameter
- *    and should behave the same (at a high level).
+ *    instead of System.loadLibrary(). The two functions should behave the same
+ *    (at a high level).
  *
  *  - Before loading any library, prepareLibraryLoad() should be called.
  *
@@ -143,9 +137,9 @@
  *    a Binder call (note that the Bundle includes file descriptors and cannot
  *    be added as an Intent extra).
  *
- *  - In a service process, finishLibraryLoad() will block until the RELRO
- *    section Bundle is received. This is typically done by calling
- *    useSharedRelros() from another thread.
+ *  - In a service process, finishLibraryLoad() and/or loadLibrary() may
+ *    block until the RELRO section Bundle is received. This is typically
+ *    done by calling useSharedRelros() from another thread.
  *
  *    This method also ensures the process uses the shared RELROs.
  */
@@ -153,6 +147,41 @@
     // Log tag for this class.
     private static final String TAG = "cr.library_loader";
 
+    // Constants used to control the behaviour of the browser process with
+    // regards to the shared RELRO section. Not applicable to ModernLinker.
+    //   NEVER        -> The browser never uses it itself.
+    //   LOW_RAM_ONLY -> It is only used on devices with low RAM.
+    //   ALWAYS       -> It is always used.
+    // NOTE: These names are known and expected by the Linker test scripts.
+    public static final int BROWSER_SHARED_RELRO_CONFIG_NEVER = 0;
+    public static final int BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY = 1;
+    public static final int BROWSER_SHARED_RELRO_CONFIG_ALWAYS = 2;
+
+    // Configuration variable used to control how the browser process uses the
+    // shared RELRO. Only change this while debugging linker-related issues.
+    // Not used by ModernLinker.
+    // NOTE: This variable's name is known and expected by the Linker test scripts.
+    public static final int BROWSER_SHARED_RELRO_CONFIG =
+            BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY;
+
+    // Constants used to control the memory device config. Can be set explicitly
+    // by setMemoryDeviceConfigForTesting(). Not applicable to ModernLinker.
+    //   INIT         -> Value is undetermined (will check at runtime).
+    //   LOW          -> This is a low-memory device.
+    //   NORMAL       -> This is not a low-memory device.
+    public static final int MEMORY_DEVICE_CONFIG_INIT = 0;
+    public static final int MEMORY_DEVICE_CONFIG_LOW = 1;
+    public static final int MEMORY_DEVICE_CONFIG_NORMAL = 2;
+
+    // Indicates if this is a low-memory device or not. The default is to
+    // determine this by probing the system at runtime, but this can be forced
+    // for testing by calling setMemoryDeviceConfigForTesting().
+    // Not used by ModernLinker.
+    protected int mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_INIT;
+
+    // Name of the library that contains our JNI code.
+    protected static final String LINKER_JNI_LIBRARY = "chromium_android_linker";
+
     // Set to true to enable debug logs.
     protected static final boolean DEBUG = false;
 
@@ -163,34 +192,26 @@
     // Guards all access to the linker.
     protected final Object mLock = new Object();
 
-    // Constants used to control the behaviour of the browser process with
-    // regards to the shared RELRO section.
-    //   NEVER        -> The browser never uses it itself.
-    //   LOW_RAM_ONLY -> It is only used on devices with low RAM.
-    //   ALWAYS       -> It is always used.
+    // The name of a class that implements TestRunner.
+    private String mTestRunnerClassName = null;
+
+    // Size of reserved Breakpad guard region. Should match the value of
+    // kBreakpadGuardRegionBytes on the JNI side. Used when computing the load
+    // addresses of multiple loaded libraries. Set to 0 to disable the guard.
+    protected static final int BREAKPAD_GUARD_REGION_BYTES = 16 * 1024 * 1024;
+
+    // Size of the area requested when using ASLR to obtain a random load address.
+    // Should match the value of kAddressSpaceReservationSize on the JNI side.
+    // Used when computing the load addresses of multiple loaded libraries to
+    // ensure that we don't try to load outside the area originally requested.
+    protected static final int ADDRESS_SPACE_RESERVATION = 192 * 1024 * 1024;
+
+    // Constants used to indicate a given Linker implementation, for testing.
+    //   LEGACY       -> Always uses the LegacyLinker implementation.
+    //   MODERN       -> Always uses the ModernLinker implementation.
     // NOTE: These names are known and expected by the Linker test scripts.
-    public static final int BROWSER_SHARED_RELRO_CONFIG_NEVER = 0;
-    public static final int BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY = 1;
-    public static final int BROWSER_SHARED_RELRO_CONFIG_ALWAYS = 2;
-
-    // Configuration variable used to control how the browser process uses the
-    // shared RELRO. Only change this while debugging linker-related issues.
-    // NOTE: This variable's name is known and expected by the Linker test scripts.
-    public static final int BROWSER_SHARED_RELRO_CONFIG =
-            BROWSER_SHARED_RELRO_CONFIG_LOW_RAM_ONLY;
-
-    // Constants used to control the value of sMemoryDeviceConfig.
-    //   INIT         -> Value is undetermined (will check at runtime).
-    //   LOW          -> This is a low-memory device.
-    //   NORMAL       -> This is not a low-memory device.
-    public static final int MEMORY_DEVICE_CONFIG_INIT = 0;
-    public static final int MEMORY_DEVICE_CONFIG_LOW = 1;
-    public static final int MEMORY_DEVICE_CONFIG_NORMAL = 2;
-
-    // Indicates if this is a low-memory device or not. The default is to
-    // determine this by probing the system at runtime, but this can be forced
-    // for testing by calling setMemoryDeviceConfig().
-    protected int mMemoryDeviceConfig = MEMORY_DEVICE_CONFIG_INIT;
+    public static final int LINKER_IMPLEMENTATION_LEGACY = 1;
+    public static final int LINKER_IMPLEMENTATION_MODERN = 2;
 
     // Singleton.
     private static Linker sSingleton = null;
@@ -199,19 +220,111 @@
     // Protected singleton constructor.
     protected Linker() {}
 
-    // Get singleton instance.
+    /**
+     * Get singleton instance. Returns either a LegacyLinker or a ModernLinker.
+     *
+     * @return the Linker implementation instance.
+     */
     public static final Linker getInstance() {
         synchronized (sSingletonLock) {
             if (sSingleton == null) {
-                // TODO(simonb): Extend later to return either a LegacyLinker
-                // or a ModernLinker instance.
-                sSingleton = new LegacyLinker();
+                // TODO(simonb): Check version once the Android M build version
+                // code becomes available.
+                // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.<ANDROID-M>) {
+                if (false) {
+                    sSingleton = ModernLinker.create();
+                } else {
+                    sSingleton = LegacyLinker.create();
+                }
+                Log.i(TAG, "Using linker: " + sSingleton.getClass().getName());
             }
             return sSingleton;
         }
     }
 
     /**
+     * Check that native library linker tests are enabled.
+     * If not enabled, calls to testing functions will fail with an assertion
+     * error.
+     *
+     * @return true if native library linker tests are enabled.
+     */
+    public static boolean areLinkerTestsEnabled() {
+        return NativeLibraries.sEnableLinkerTests;
+    }
+
+    /**
+     * Assert for testing.
+     * Hard assertion. Cannot be disabled. Used only by testing methods.
+     */
+    private static void assertForTesting(boolean flag) {
+        if (!flag) {
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * Assert NativeLibraries.sEnableLinkerTests is true.
+     * Hard assertion that we are in a testing context. Cannot be disabled. The
+     * test methods in this module permit injection of runnable code by class
+     * name. To protect against both malicious and accidental use of these
+     * methods, we ensure that NativeLibraries.sEnableLinkerTests is true when
+     * any is called.
+     */
+    private static void assertLinkerTestsAreEnabled() {
+        if (!NativeLibraries.sEnableLinkerTests) {
+            throw new AssertionError("Testing method called in non-testing context");
+        }
+    }
+
+    /**
+     * Set Linker implementation type.
+     * For testing. Sets either a LegacyLinker or a ModernLinker. Must be called
+     * before getInstance().
+     *
+     * @param type LINKER_IMPLEMENTATION_LEGACY or LINKER_IMPLEMENTATION_MODERN
+     */
+    public static final void setLinkerImplementationForTesting(int type) {
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
+        assertForTesting(
+                type == LINKER_IMPLEMENTATION_LEGACY || type == LINKER_IMPLEMENTATION_MODERN);
+
+        synchronized (sSingletonLock) {
+            assertForTesting(sSingleton == null);
+
+            if (type == LINKER_IMPLEMENTATION_MODERN) {
+                sSingleton = ModernLinker.create();
+            } else if (type == LINKER_IMPLEMENTATION_LEGACY) {
+                sSingleton = LegacyLinker.create();
+            } else {
+                return;
+            }
+            Log.i(TAG, "Forced linker: " + sSingleton.getClass().getName());
+        }
+    }
+
+    /**
+     * Get Linker implementation type.
+     * For testing.
+     *
+     * @return LINKER_IMPLEMENTATION_LEGACY or LINKER_IMPLEMENTATION_MODERN
+     */
+    public int getLinkerImplementationForTesting() {
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
+        assertForTesting(sSingleton != null);
+
+        if (sSingleton instanceof ModernLinker) {
+            return LINKER_IMPLEMENTATION_MODERN;
+        } else if (sSingleton instanceof LegacyLinker) {
+            return LINKER_IMPLEMENTATION_LEGACY;
+        }
+        Log.e(TAG, "Invalid linker: " + sSingleton.getClass().getName());
+        return 0;
+    }
+
+    /**
      * A public interface used to run runtime linker tests after loading
      * libraries. Should only be used to implement the linker unit tests,
      * which is controlled by the value of NativeLibraries.sEnableLinkerTests
@@ -220,32 +333,30 @@
     public interface TestRunner {
         /**
          * Run runtime checks and return true if they all pass.
+         *
          * @param memoryDeviceConfig The current memory device configuration.
          * @param inBrowserProcess true iff this is the browser process.
+         * @return true if all checks pass.
          */
         public boolean runChecks(int memoryDeviceConfig, boolean inBrowserProcess);
     }
 
-    // The name of a class that implements TestRunner.
-    String mTestRunnerClassName = null;
-
     /**
      * Set the TestRunner by its class name. It will be instantiated at
      * runtime after all libraries are loaded.
+     *
      * @param testRunnerClassName null or a String for the class name of the
      * TestRunner to use.
      */
-    public void setTestRunnerClassName(String testRunnerClassName) {
+    public void setTestRunnerClassNameForTesting(String testRunnerClassName) {
         if (DEBUG) {
-            Log.i(TAG, "setTestRunnerByClassName(" + testRunnerClassName + ") called");
+            Log.i(TAG, "setTestRunnerByClassNameForTesting(" + testRunnerClassName + ") called");
         }
-        if (!NativeLibraries.sEnableLinkerTests) {
-            // Ignore this in production code to prevent malevolent runtime injection.
-            return;
-        }
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
 
         synchronized (mLock) {
-            assert mTestRunnerClassName == null;
+            assertForTesting(mTestRunnerClassName == null);
             mTestRunnerClassName = testRunnerClassName;
         }
     }
@@ -254,42 +365,193 @@
      * Call this to retrieve the name of the current TestRunner class name
      * if any. This can be useful to pass it from the browser process to
      * child ones.
+     *
      * @return null or a String holding the name of the class implementing
-     * the TestRunner set by calling setTestRunnerClassName() previously.
+     * the TestRunner set by calling setTestRunnerClassNameForTesting() previously.
      */
-    public String getTestRunnerClassName() {
+    public String getTestRunnerClassNameForTesting() {
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
+
         synchronized (mLock) {
             return mTestRunnerClassName;
         }
     }
 
     /**
+     * Instantiate and run the current TestRunner, if any. The TestRunner implementation
+     * must be instantiated _after_ all libraries are loaded to ensure that its
+     * native methods are properly registered.
+     *
+     * @param memoryDeviceConfig LegacyLinker memory config, or 0 if unused
+     * @param inBrowserProcess true if in the browser process
+     */
+    protected void runTestRunnerClassForTesting(int memoryDeviceConfig, boolean inBrowserProcess) {
+        if (DEBUG) {
+            Log.i(TAG, "runTestRunnerClassForTesting called");
+        }
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
+
+        synchronized (mLock) {
+            if (mTestRunnerClassName == null) {
+                return;
+            }
+            if (DEBUG) {
+                Log.i(TAG, "Instantiating " + mTestRunnerClassName);
+            }
+            TestRunner testRunner = null;
+            try {
+                testRunner = (TestRunner) Class.forName(mTestRunnerClassName).newInstance();
+            } catch (Exception e) {
+                Log.e(TAG, "Could not instantiate test runner class by name", e);
+                testRunner = null;
+            }
+            if (testRunner != null) {
+                if (!testRunner.runChecks(memoryDeviceConfig, inBrowserProcess)) {
+                    Log.wtf(TAG, "Linker runtime tests failed in this process!!");
+                    assertForTesting(false);
+                } else {
+                    Log.i(TAG, "All linker tests passed!");
+                }
+            }
+        }
+    }
+
+    /**
      * Call this method before any other Linker method to force a specific
      * memory device configuration. Should only be used for testing.
-     * @param memoryDeviceConfig either MEMORY_DEVICE_CONFIG_LOW or MEMORY_DEVICE_CONFIG_NORMAL.
+     *
+     * @param memoryDeviceConfig MEMORY_DEVICE_CONFIG_LOW or MEMORY_DEVICE_CONFIG_NORMAL.
      */
-    public void setMemoryDeviceConfig(int memoryDeviceConfig) {
+    public void setMemoryDeviceConfigForTesting(int memoryDeviceConfig) {
         if (DEBUG) {
-            Log.i(TAG, "setMemoryDeviceConfig(" + memoryDeviceConfig + ") called");
+            Log.i(TAG, "setMemoryDeviceConfigForTesting(" + memoryDeviceConfig + ") called");
         }
-        // Sanity check. This method should only be called during tests.
-        assert NativeLibraries.sEnableLinkerTests;
+        // Sanity check. This method may only be called during tests.
+        assertLinkerTestsAreEnabled();
+        assertForTesting(memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW
+                || memoryDeviceConfig == MEMORY_DEVICE_CONFIG_NORMAL);
+
         synchronized (mLock) {
-            assert mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT;
-            assert memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW
-                   || memoryDeviceConfig == MEMORY_DEVICE_CONFIG_NORMAL;
+            assertForTesting(mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT);
+
+            mMemoryDeviceConfig = memoryDeviceConfig;
             if (DEBUG) {
-                if (memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) {
+                if (mMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW) {
                     Log.i(TAG, "Simulating a low-memory device");
                 } else {
                     Log.i(TAG, "Simulating a regular-memory device");
                 }
             }
-            mMemoryDeviceConfig = memoryDeviceConfig;
         }
     }
 
     /**
+     * Determine whether a library is the linker library. Also deal with the
+     * component build that adds a .cr suffix to the name.
+     *
+     * @param library the name of the library.
+     * @return true is the library is the Linker's own JNI library.
+     */
+    public boolean isChromiumLinkerLibrary(String library) {
+        return library.equals(LINKER_JNI_LIBRARY) || library.equals(LINKER_JNI_LIBRARY + ".cr");
+    }
+
+    /**
+     * Load the Linker JNI library. Throws UnsatisfiedLinkError on error.
+     * In a component build, the suffix ".cr" is added to each library name, so
+     * if the initial load fails we retry with a suffix.
+     */
+    protected void loadLinkerJNILibrary() {
+        String lib_name = "lib" + LINKER_JNI_LIBRARY + ".so";
+        if (DEBUG) {
+            Log.i(TAG, "Loading " + lib_name);
+        }
+        try {
+            System.loadLibrary(LINKER_JNI_LIBRARY);
+        } catch (UnsatisfiedLinkError e) {
+            Log.w(TAG, "Couldn't load " + lib_name + ", trying " + lib_name + ".so");
+            System.loadLibrary(LINKER_JNI_LIBRARY + ".cr");
+        }
+    }
+
+    /**
+     * Obtain a random base load address at which to place loaded libraries.
+     *
+     * @return new base load address
+     */
+    protected long getRandomBaseLoadAddress() {
+        // nativeGetRandomBaseLoadAddress() returns an address at which it has previously
+        // successfully mapped an area larger than the largest library we expect to load,
+        // on the basis that we will be able, with high probability, to map our library
+        // into it.
+        //
+        // One issue with this is that we do not yet know the size of the library that
+        // we will load is. If it is smaller than the size we used to obtain a random
+        // address the library mapping may still succeed. The other issue is that
+        // although highly unlikely, there is no guarantee that something else does not
+        // map into the area we are going to use between here and when we try to map into it.
+        //
+        // The above notes mean that all of this is probablistic. It is however okay to do
+        // because if, worst case and unlikely, we get unlucky in our choice of address,
+        // the back-out and retry without the shared RELRO in the ChildProcessService will
+        // keep things running.
+        final long address = nativeGetRandomBaseLoadAddress();
+        if (DEBUG) {
+            Log.i(TAG, String.format(Locale.US, "Random native base load address: 0x%x", address));
+        }
+        return address;
+    }
+
+    /**
+     * Load a native shared library with the Chromium linker. If the zip file
+     * is not null, the shared library must be uncompressed and page aligned
+     * inside the zipfile. Note the crazy linker treats libraries and files as
+     * equivalent, so you can only open one library in a given zip file. The
+     * library must not be the Chromium linker library.
+     *
+     * @param zipFilePath The path of the zip file containing the library (or null).
+     * @param libFilePath The path of the library (possibly in the zip file).
+     */
+    public void loadLibrary(@Nullable String zipFilePath, String libFilePath) {
+        if (DEBUG) {
+            Log.i(TAG, "loadLibrary: " + zipFilePath + ", " + libFilePath);
+        }
+        final boolean isFixedAddressPermitted = true;
+        loadLibraryImpl(zipFilePath, libFilePath, isFixedAddressPermitted);
+    }
+
+    /**
+     * Load a native shared library with the Chromium linker, ignoring any
+     * requested fixed address for RELRO sharing. If the zip file
+     * is not null, the shared library must be uncompressed and page aligned
+     * inside the zipfile. Note the crazy linker treats libraries and files as
+     * equivalent, so you can only open one library in a given zip file. The
+     * library must not be the Chromium linker library.
+     *
+     * @param zipFilePath The path of the zip file containing the library (or null).
+     * @param libFilePath The path of the library (possibly in the zip file).
+     */
+    public void loadLibraryNoFixedAddress(@Nullable String zipFilePath, String libFilePath) {
+        if (DEBUG) {
+            Log.i(TAG, "loadLibraryAtAnyAddress: " + zipFilePath + ", " + libFilePath);
+        }
+        final boolean isFixedAddressPermitted = false;
+        loadLibraryImpl(zipFilePath, libFilePath, isFixedAddressPermitted);
+    }
+
+    /**
+     * Call this method to determine if the chromium project must load the library
+     * directly from a zip file.
+     */
+    public boolean isInZipFile() {
+        // The auto-generated NativeLibraries.sUseLibraryInZipFile variable will be true
+        // if the library remains embedded in the APK zip file on the target.
+        return NativeLibraries.sUseLibraryInZipFile;
+    }
+
+    /**
      * Call this method to determine if this chromium project must
      * use this linker. If not, System.loadLibrary() should be used to load
      * libraries instead.
@@ -303,20 +565,12 @@
     public abstract boolean isUsingBrowserSharedRelros();
 
     /**
-     * Call this method to determine if the chromium project must load
-     * the library directly from the zip file.
-     */
-    public abstract boolean isInZipFile();
-
-    /**
      * Call this method just before loading any native shared libraries in this process.
      */
     public abstract void prepareLibraryLoad();
 
     /**
      * Call this method just after loading all native shared libraries in this process.
-     * Note that when in a service process, this will block until the RELRO bundle is
-     * received, i.e. when another thread calls useSharedRelros().
      */
     public abstract void finishLibraryLoad();
 
@@ -325,6 +579,7 @@
      * used in this process. If initServiceProcess() was previously called,
      * finishLibraryLoad() will not exit until this method is called in another
      * thread with a non-null value.
+     *
      * @param bundle The Bundle instance containing a map of shared RELRO sections
      * to use in this process.
      */
@@ -333,6 +588,7 @@
     /**
      * Call this to retrieve the shared RELRO sections created in this process,
      * after loading all libraries.
+     *
      * @return a new Bundle instance, or null if RELRO sharing is disabled on
      * this system, or if initServiceProcess() was called previously.
      */
@@ -348,6 +604,7 @@
      * Call this method before loading any libraries to indicate that this
      * process is ready to reuse shared RELRO sections from another one.
      * Typically used when starting service processes.
+     *
      * @param baseLoadAddress the base library load address to use.
      */
     public abstract void initServiceProcess(long baseLoadAddress);
@@ -356,34 +613,28 @@
      * Retrieve the base load address of all shared RELRO sections.
      * This also enforces the creation of shared RELRO sections in
      * prepareLibraryLoad(), which can later be retrieved with getSharedRelros().
+     *
      * @return a common, random base load address, or 0 if RELRO sharing is
      * disabled.
      */
     public abstract long getBaseLoadAddress();
 
     /**
-     * Load a native shared library with the Chromium linker. If the zip file
-     * is not null, the shared library must be uncompressed and page aligned
-     * inside the zipfile. Note the crazy linker treats libraries and files as
-     * equivalent, so you can only open one library in a given zip file. The
-     * library must not be the Chromium linker library.
+     * Implements loading a native shared library with the Chromium linker.
      *
      * @param zipFilePath The path of the zip file containing the library (or null).
      * @param libFilePath The path of the library (possibly in the zip file).
+     * @param isFixedAddressPermitted If true, uses a fixed load address if one was
+     * supplied, otherwise ignores the fixed address and loads wherever available.
      */
-    public abstract void loadLibrary(@Nullable String zipFilePath, String libFilePath);
-
-    /**
-     * Determine whether a library is the linker library. Also deal with the
-     * component build that adds a .cr suffix to the name.
-     */
-    public abstract boolean isChromiumLinkerLibrary(String library);
+    abstract void loadLibraryImpl(
+            @Nullable String zipFilePath, String libFilePath, boolean isFixedAddressPermitted);
 
     /**
      * Record information for a given library.
      * IMPORTANT: Native code knows about this class's fields, so
      * don't change them without modifying the corresponding C++ sources.
-     * Also, the LibInfo instance owns the ashmem file descriptor.
+     * Also, the LibInfo instance owns the shared RELRO file descriptor.
      */
     public static class LibInfo implements Parcelable {
 
@@ -432,7 +683,7 @@
                     fd.writeToParcel(out, 0);
                     fd.close();
                 } catch (java.io.IOException e) {
-                    Log.e(TAG, "Cant' write LibInfo file descriptor to parcel", e);
+                    Log.e(TAG, "Can't write LibInfo file descriptor to parcel", e);
                 }
             }
         }
@@ -479,7 +730,7 @@
         @AccessedByNative
         public long mRelroSize;   // page-aligned size in memory, or 0.
         @AccessedByNative
-        public int  mRelroFd;     // ashmem file descriptor, or -1
+        public int mRelroFd; // shared RELRO file descriptor, or -1
     }
 
     // Create a Bundle from a map of LibInfo objects.
@@ -488,7 +739,6 @@
         for (Map.Entry<String, LibInfo> entry : map.entrySet()) {
             bundle.putParcelable(entry.getKey(), entry.getValue());
         }
-
         return bundle;
     }
 
@@ -508,4 +758,15 @@
             entry.getValue().close();
         }
     }
+
+    /**
+     * Return a random address that should be free to be mapped with the given size.
+     * Maps an area large enough for the largest library we might attempt to load,
+     * and if successful then unmaps it and returns the address of the area allocated
+     * by the system (with ASLR). The idea is that this area should remain free of
+     * other mappings until we map our library into it.
+     *
+     * @return address to pass to future mmap, or 0 on error.
+     */
+    private static native long nativeGetRandomBaseLoadAddress();
 }
diff --git a/android/java/src/org/chromium/base/library_loader/ModernLinker.java b/android/java/src/org/chromium/base/library_loader/ModernLinker.java
new file mode 100644
index 0000000..9bbdaba
--- /dev/null
+++ b/android/java/src/org/chromium/base/library_loader/ModernLinker.java
@@ -0,0 +1,504 @@
+// Copyright 2015 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.
+
+package org.chromium.base.library_loader;
+
+import android.os.Bundle;
+import android.os.SystemClock;
+
+import org.chromium.base.Log;
+import org.chromium.base.PathUtils;
+
+import java.util.HashMap;
+import java.util.Locale;
+
+import javax.annotation.Nullable;
+
+/*
+ * For more, see Technical note, Security considerations, and the explanation
+ * of how this class is supposed to be used in Linker.java.
+ */
+
+/**
+ * Provides a concrete implementation of the Chromium Linker.
+ *
+ * This Linker implementation uses the Android M and later system linker to map and then
+ * run Chrome for Android.
+ *
+ * For more on the operations performed by the Linker, see {@link Linker}.
+ */
+class ModernLinker extends Linker {
+    // Log tag for this class.
+    private static final String TAG = "cr.library_loader";
+
+    // Becomes true after linker initialization.
+    private boolean mInitialized = false;
+
+    // Becomes true to indicate this process needs to wait for a shared RELRO in LibraryLoad().
+    private boolean mWaitForSharedRelros = false;
+
+    // The map of all RELRO sections either created or used in this process.
+    private HashMap<String, LibInfo> mSharedRelros = null;
+
+    // Cached Bundle representation of the RELRO sections map for transfer across processes.
+    private Bundle mSharedRelrosBundle = null;
+
+    // Set to true if this runs in the browser process. Disabled by initServiceProcess().
+    private boolean mInBrowserProcess = true;
+
+    // Current common random base load address. A value of -1 indicates not yet initialized.
+    private long mBaseLoadAddress = -1;
+
+    // Current fixed-location load address for the next library called by loadLibrary().
+    // Initialized to mBaseLoadAddress in prepareLibraryLoad(), and then adjusted as each
+    // library is loaded by loadLibrary().
+    private long mCurrentLoadAddress = -1;
+
+    // Becomes true once prepareLibraryLoad() has been called.
+    private boolean mPrepareLibraryLoadCalled = false;
+
+    // The map of libraries that are currently loaded in this process.
+    private HashMap<String, LibInfo> mLoadedLibraries = null;
+
+    // The directory used to hold shared RELRO data files. Set up by prepareLibraryLoad().
+    private String mDataDirectory = null;
+
+    // Private singleton constructor, and singleton factory method.
+    private ModernLinker() {}
+    static Linker create() {
+        return new ModernLinker();
+    }
+
+    // Used internally to initialize the linker's data. Assume lock is held.
+    private void ensureInitializedLocked() {
+        assert Thread.holdsLock(mLock);
+        assert NativeLibraries.sUseLinker;
+
+        // On first call, load libchromium_android_linker.so.
+        if (!mInitialized) {
+            loadLinkerJNILibrary();
+            mInitialized = true;
+        }
+    }
+
+    /**
+     * Call this method to determine if this chromium project must
+     * use this linker. If not, System.loadLibrary() should be used to load
+     * libraries instead.
+     */
+    @Override
+    public boolean isUsed() {
+        // Only GYP targets that are APKs and have the 'use_chromium_linker' variable
+        // defined as 1 will use this linker. For all others (the default), the
+        // auto-generated NativeLibraries.sUseLinker variable will be false.
+        return NativeLibraries.sUseLinker;
+    }
+
+    /**
+     * Call this method to determine if the linker will try to use shared RELROs
+     * for the browser process.
+     */
+    @Override
+    public boolean isUsingBrowserSharedRelros() {
+        // This Linker does not attempt to share RELROS between the browser and
+        // the renderers, but only between renderers.
+        return false;
+    }
+
+    /**
+     * Call this method just before loading any native shared libraries in this process.
+     * Loads the Linker's JNI library, and initializes the variables involved in the
+     * implementation of shared RELROs.
+     */
+    @Override
+    public void prepareLibraryLoad() {
+        if (DEBUG) {
+            Log.i(TAG, "prepareLibraryLoad() called");
+        }
+        assert NativeLibraries.sUseLinker;
+
+        synchronized (mLock) {
+            assert !mPrepareLibraryLoadCalled;
+            ensureInitializedLocked();
+
+            // If in the browser, generate a random base load address for service processes
+            // and create an empty shared RELROs map. For service processes, the shared
+            // RELROs map must remain null until set by useSharedRelros().
+            if (mInBrowserProcess) {
+                setupBaseLoadAddressLocked();
+                mSharedRelros = new HashMap<String, LibInfo>();
+            }
+
+            // Create an empty loaded libraries map.
+            mLoadedLibraries = new HashMap<String, LibInfo>();
+
+            // Retrieve the data directory from base.
+            mDataDirectory = PathUtils.getDataDirectory(null);
+
+            // Start the current load address at the base load address.
+            mCurrentLoadAddress = mBaseLoadAddress;
+
+            mPrepareLibraryLoadCalled = true;
+        }
+    }
+
+    /**
+     * Call this method just after loading all native shared libraries in this process.
+     * If not in the browser, closes the LibInfo entries used for RELRO sharing.
+     */
+    @Override
+    public void finishLibraryLoad() {
+        if (DEBUG) {
+            Log.i(TAG, "finishLibraryLoad() called");
+        }
+
+        synchronized (mLock) {
+            assert mPrepareLibraryLoadCalled;
+
+            // Close shared RELRO file descriptors if not in the browser.
+            if (!mInBrowserProcess && mSharedRelros != null) {
+                closeLibInfoMap(mSharedRelros);
+                mSharedRelros = null;
+            }
+
+            // If testing, run tests now that all libraries are loaded and initialized.
+            if (NativeLibraries.sEnableLinkerTests) {
+                runTestRunnerClassForTesting(0, mInBrowserProcess);
+            }
+        }
+    }
+
+    // Used internally to wait for shared RELROs. Returns once useSharedRelros() has been
+    // called to supply a valid shared RELROs bundle.
+    private void waitForSharedRelrosLocked() {
+        if (DEBUG) {
+            Log.i(TAG, "waitForSharedRelros called");
+        }
+        assert Thread.holdsLock(mLock);
+
+        // Return immediately if shared RELROs are already available.
+        if (mSharedRelros != null) {
+            return;
+        }
+
+        // Wait until notified by useSharedRelros() that shared RELROs have arrived.
+        long startTime = DEBUG ? SystemClock.uptimeMillis() : 0;
+        // Note: The additional synchronized block is present only to silence Findbugs.
+        // Without it, Findbugs reports a false positive:
+        // RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE: Redundant nullcheck of value known to be null
+        synchronized (mLock) {
+            while (mSharedRelros == null) {
+                try {
+                    mLock.wait();
+                } catch (InterruptedException e) {
+                    // Restore the thread's interrupt status.
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+
+        if (DEBUG) {
+            Log.i(TAG, String.format(Locale.US, "Time to wait for shared RELRO: %d ms",
+                               SystemClock.uptimeMillis() - startTime));
+        }
+    }
+
+    /**
+     * Call this to send a Bundle containing the shared RELRO sections to be
+     * used in this process. If initServiceProcess() was previously called,
+     * libraryLoad() will wait until this method is called in another
+     * thread with a non-null value.
+     *
+     * @param bundle The Bundle instance containing a map of shared RELRO sections
+     * to use in this process.
+     */
+    @Override
+    public void useSharedRelros(Bundle bundle) {
+        if (DEBUG) {
+            Log.i(TAG, "useSharedRelros() called with " + bundle);
+        }
+
+        synchronized (mLock) {
+            mSharedRelros = createLibInfoMapFromBundle(bundle);
+            mLock.notifyAll();
+        }
+    }
+
+    /**
+     * Call this to retrieve the shared RELRO sections created in this process,
+     * after loading all libraries.
+     *
+     * @return a new Bundle instance, or null if RELRO sharing is disabled on
+     * this system, or if initServiceProcess() was called previously.
+     */
+    @Override
+    public Bundle getSharedRelros() {
+        if (DEBUG) {
+            Log.i(TAG, "getSharedRelros() called");
+        }
+        synchronized (mLock) {
+            if (!mInBrowserProcess) {
+                if (DEBUG) {
+                    Log.i(TAG, "Not in browser, so returning null Bundle");
+                }
+                return null;
+            }
+
+            // Create a new Bundle containing RELRO section information for all the shared
+            // RELROs created while loading libraries.
+            if (mSharedRelrosBundle == null && mSharedRelros != null) {
+                mSharedRelrosBundle = createBundleFromLibInfoMap(mSharedRelros);
+                if (DEBUG) {
+                    Log.i(TAG, "Shared RELRO bundle created from map: " + mSharedRelrosBundle);
+                }
+            }
+            if (DEBUG) {
+                Log.i(TAG, "Returning " + mSharedRelrosBundle);
+            }
+            return mSharedRelrosBundle;
+        }
+    }
+
+    /**
+     * Call this method before loading any libraries to indicate that this
+     * process shall neither create or reuse shared RELRO sections.
+     */
+    @Override
+    public void disableSharedRelros() {
+        if (DEBUG) {
+            Log.i(TAG, "disableSharedRelros() called");
+        }
+        synchronized (mLock) {
+            assert !mPrepareLibraryLoadCalled;
+
+            // Mark this as a service process, and disable wait for shared RELRO.
+            mInBrowserProcess = false;
+            mWaitForSharedRelros = false;
+        }
+    }
+
+    /**
+     * Call this method before loading any libraries to indicate that this
+     * process is ready to reuse shared RELRO sections from another one.
+     * Typically used when starting service processes.
+     *
+     * @param baseLoadAddress the base library load address to use.
+     */
+    @Override
+    public void initServiceProcess(long baseLoadAddress) {
+        if (DEBUG) {
+            Log.i(TAG,
+                    String.format(Locale.US, "initServiceProcess(0x%x) called", baseLoadAddress));
+        }
+        synchronized (mLock) {
+            assert !mPrepareLibraryLoadCalled;
+
+            // Mark this as a service process, and flag wait for shared RELRO.
+            // Save the base load address passed in.
+            mInBrowserProcess = false;
+            mWaitForSharedRelros = true;
+            mBaseLoadAddress = baseLoadAddress;
+        }
+    }
+
+    /**
+     * Retrieve the base load address for libraries that share RELROs.
+     *
+     * @return a common, random base load address, or 0 if RELRO sharing is
+     * disabled.
+     */
+    @Override
+    public long getBaseLoadAddress() {
+        synchronized (mLock) {
+            ensureInitializedLocked();
+            setupBaseLoadAddressLocked();
+            if (DEBUG) {
+                Log.i(TAG, String.format(Locale.US, "getBaseLoadAddress() returns 0x%x",
+                                   mBaseLoadAddress));
+            }
+            return mBaseLoadAddress;
+        }
+    }
+
+    // Used internally to lazily setup the common random base load address.
+    private void setupBaseLoadAddressLocked() {
+        assert Thread.holdsLock(mLock);
+
+        // No-op if the base load address is already set up.
+        if (mBaseLoadAddress == -1) {
+            mBaseLoadAddress = getRandomBaseLoadAddress();
+        }
+        if (mBaseLoadAddress == 0) {
+            // If the random address is 0 there are issues with finding enough
+            // free address space, so disable RELRO shared / fixed load addresses.
+            Log.w(TAG, "Disabling shared RELROs due address space pressure");
+            mWaitForSharedRelros = false;
+        }
+    }
+
+    /**
+     * Load a native shared library with the Chromium linker. If the zip file
+     * is not null, the shared library must be uncompressed and page aligned
+     * inside the zipfile. Note the crazy linker treats libraries and files as
+     * equivalent, so you can only open one library in a given zip file. The
+     * library must not be the Chromium linker library.
+     *
+     * If asked to wait for shared RELROs, this function will block library loads
+     * until the shared RELROs bundle is received by useSharedRelros().
+     *
+     * @param zipFilePath The path of the zip file containing the library (or null).
+     * @param libFilePath The path of the library (possibly in the zip file).
+     * @param isFixedAddressPermitted If true, uses a fixed load address if one was
+     * supplied, otherwise ignores the fixed address and loads wherever available.
+     */
+    @Override
+    void loadLibraryImpl(
+            @Nullable String zipFilePath, String libFilePath, boolean isFixedAddressPermitted) {
+        if (DEBUG) {
+            Log.i(TAG, "loadLibraryImpl: " + zipFilePath + ", " + libFilePath + ", "
+                            + isFixedAddressPermitted);
+        }
+
+        synchronized (mLock) {
+            assert mPrepareLibraryLoadCalled;
+
+            String dlopenExtPath;
+            if (zipFilePath != null) {
+                // The android_dlopen_ext() function understands strings with the format
+                // <zip_path>!/<file_path> to represent the file_path element within the zip
+                // file at zip_path. This enables directly loading from APK. We add the
+                // "crazy." prefix to the path in the zip file to prevent the Android package
+                // manager from seeing this as a library and so extracting it from the APK.
+                String cpu_abi = nativeGetCpuAbi();
+                dlopenExtPath = zipFilePath + "!/lib/" + cpu_abi + "/crazy." + libFilePath;
+            } else {
+                // Not loading from APK directly, so simply pass the library name to
+                // android_dlopen_ext().
+                dlopenExtPath = libFilePath;
+            }
+
+            if (mLoadedLibraries.containsKey(dlopenExtPath)) {
+                if (DEBUG) {
+                    Log.i(TAG, "Not loading " + libFilePath + " twice");
+                }
+                return;
+            }
+
+            // If not in the browser, shared RELROs are not disabled, and fixed addresses
+            // have not been disallowed, load the library at a fixed address. Otherwise,
+            // load anywhere.
+            long loadAddress = 0;
+            if (!mInBrowserProcess && mWaitForSharedRelros && isFixedAddressPermitted) {
+                loadAddress = mCurrentLoadAddress;
+
+                // For multiple libraries, ensure we stay within reservation range.
+                if (loadAddress > mBaseLoadAddress + ADDRESS_SPACE_RESERVATION) {
+                    String errorMessage = "Load address outside reservation, for: " + libFilePath;
+                    Log.e(TAG, errorMessage);
+                    throw new UnsatisfiedLinkError(errorMessage);
+                }
+            }
+
+            LibInfo libInfo = new LibInfo();
+
+            if (mInBrowserProcess && mCurrentLoadAddress != 0) {
+                // We are in the browser, and with a current load address that indicates that
+                // there is enough address space for shared RELRO to operate. Create the
+                // shared RELRO, and store it in the map.
+                String relroPath = mDataDirectory + "/RELRO:" + libFilePath;
+                if (nativeCreateSharedRelro(
+                            dlopenExtPath, mCurrentLoadAddress, relroPath, libInfo)) {
+                    mSharedRelros.put(dlopenExtPath, libInfo);
+                } else {
+                    String errorMessage = "Unable to create shared relro: " + relroPath;
+                    Log.w(TAG, errorMessage);
+                }
+            } else if (!mInBrowserProcess && mCurrentLoadAddress != 0 && mWaitForSharedRelros) {
+                // We are in a service process, again with a current load address that is
+                // suitable for shared RELRO, and we are to wait for shared RELROs. So
+                // do that, then use the map we receive to provide libinfo for library load.
+                waitForSharedRelrosLocked();
+                if (mSharedRelros.containsKey(dlopenExtPath)) {
+                    libInfo = mSharedRelros.get(dlopenExtPath);
+                }
+            }
+
+            // Load the library. In the browser, loadAddress is 0, so nativeLoadLibrary()
+            // will load without shared RELRO. Otherwise, it uses shared RELRO if the attached
+            // libInfo is usable.
+            if (!nativeLoadLibrary(dlopenExtPath, loadAddress, libInfo)) {
+                String errorMessage = "Unable to load library: " + dlopenExtPath;
+                Log.e(TAG, errorMessage);
+                throw new UnsatisfiedLinkError(errorMessage);
+            }
+
+            // Print the load address to the logcat when testing the linker. The format
+            // of the string is expected by the Python test_runner script as one of:
+            //    BROWSER_LIBRARY_ADDRESS: <library-name> <address>
+            //    RENDERER_LIBRARY_ADDRESS: <library-name> <address>
+            // Where <library-name> is the library name, and <address> is the hexadecimal load
+            // address.
+            if (NativeLibraries.sEnableLinkerTests) {
+                String tag =
+                        mInBrowserProcess ? "BROWSER_LIBRARY_ADDRESS" : "RENDERER_LIBRARY_ADDRESS";
+                Log.i(TAG, String.format(
+                                   Locale.US, "%s: %s %x", tag, libFilePath, libInfo.mLoadAddress));
+            }
+
+            if (loadAddress != 0 && mCurrentLoadAddress != 0) {
+                // Compute the next current load address. If mCurrentLoadAddress
+                // is not 0, this is an explicit library load address.
+                mCurrentLoadAddress =
+                        libInfo.mLoadAddress + libInfo.mLoadSize + BREAKPAD_GUARD_REGION_BYTES;
+            }
+
+            mLoadedLibraries.put(dlopenExtPath, libInfo);
+            if (DEBUG) {
+                Log.i(TAG, "Library details " + libInfo.toString());
+            }
+        }
+    }
+
+    /**
+     * Native method to return the CPU ABI.
+     * Obtaining it from the linker's native code means that we always correctly
+     * match the loaded library's ABI to the linker's ABI.
+     *
+     * @return CPU ABI string.
+     */
+    private static native String nativeGetCpuAbi();
+
+    /**
+     * Native method used to load a library.
+     *
+     * @param dlopenExtPath For load from APK, the path to the enclosing
+     * zipfile concatenated with "!/" and the path to the library within the zipfile;
+     * otherwise the platform specific library name (e.g. libfoo.so).
+     * @param loadAddress Explicit load address, or 0 for randomized one.
+     * @param libInfo If not null, the mLoadAddress and mLoadSize fields
+     * of this LibInfo instance will set on success.
+     * @return true for success, false otherwise.
+     */
+    private static native boolean nativeLoadLibrary(
+            String dlopenExtPath, long loadAddress, LibInfo libInfo);
+
+    /**
+     * Native method used to create a shared RELRO section.
+     * Creates a shared RELRO file for the given library. Done by loading a
+     * a new temporary library at the specified address, saving the RELRO section
+     * from it, then unloading.
+     *
+     * @param dlopenExtPath For load from APK, the path to the enclosing
+     * zipfile concatenated with "!/" and the path to the library within the zipfile;
+     * otherwise the platform specific library name (e.g. libfoo.so).
+     * @param loadAddress load address, which can be different from the one
+     * used to load the library in the current process!
+     * @param relroPath Path to the shared RELRO file for this library.
+     * @param libInfo libInfo instance. On success, the mRelroStart, mRelroSize
+     * and mRelroFd will be set.
+     * @return true on success, false otherwise.
+     */
+    private static native boolean nativeCreateSharedRelro(
+            String dlopenExtPath, long loadAddress, String relroPath, LibInfo libInfo);
+}
diff --git a/android/javatests/src/org/chromium/base/ObserverListTest.java b/android/javatests/src/org/chromium/base/ObserverListTest.java
index 973682b..94a9c71 100644
--- a/android/javatests/src/org/chromium/base/ObserverListTest.java
+++ b/android/javatests/src/org/chromium/base/ObserverListTest.java
@@ -9,6 +9,7 @@
 
 import org.chromium.base.test.util.Feature;
 
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 
@@ -70,7 +71,9 @@
         }
     }
 
+    @SuppressWarnings("ElementsCountedInLoop")
     private static <T> int getSizeOfIterable(Iterable<T> iterable) {
+        if (iterable instanceof Collection<?>) return ((Collection<?>) iterable).size();
         int num = 0;
         for (T el : iterable) num++;
         return num;
diff --git a/android/linker/BUILD.gn b/android/linker/BUILD.gn
index 043bfc6..9c23a63 100644
--- a/android/linker/BUILD.gn
+++ b/android/linker/BUILD.gn
@@ -9,7 +9,13 @@
 # GYP: //base/base.gyp:chromium_android_linker
 shared_library("chromium_android_linker") {
   sources = [
+    "android_dlext.h",
     "legacy_linker_jni.cc",
+    "legacy_linker_jni.h",
+    "linker_jni.cc",
+    "linker_jni.h",
+    "modern_linker_jni.cc",
+    "modern_linker_jni.h",
   ]
 
   # The NDK contains the crazy_linker here:
diff --git a/android/linker/android_dlext.h b/android/linker/android_dlext.h
new file mode 100644
index 0000000..4900f21
--- /dev/null
+++ b/android/linker/android_dlext.h
@@ -0,0 +1,77 @@
+// Copyright 2015 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.
+
+// Definitions for android_dlopen_ext().
+//
+// This function was added for Android L-MR1 and made available in android-21
+// but we currently build Chromium with android-16. Placing the declarations
+// we need here allows code that uses android_dlopen_ext() to build with
+// android-16. At runtime we check the target's SDK_INT to ensure that we
+// are on a system new enough to offer this function, and also only access
+// it with dlsym so that the runtime linker on pre-Android L-MR1 targets will
+// not complain about a missing symbol when loading our library.
+//
+// Details below taken from:
+//   third_party/android_tools/ndk/platforms/android-21
+//       /arch-arm/usr/include/android/dlext.h
+//
+// Although taken specifically from arch-arm, there are no architecture-
+// specific elements in dlext.h. All android-21/arch-* directories contain
+// identical copies of dlext.h.
+
+#ifndef BASE_ANDROID_LINKER_ANDROID_DLEXT_H_
+#define BASE_ANDROID_LINKER_ANDROID_DLEXT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* bitfield definitions for android_dlextinfo.flags */
+enum {
+  /* When set, the reserved_addr and reserved_size fields must point to an
+   * already-reserved region of address space which will be used to load the
+   * library if it fits. If the reserved region is not large enough, the load
+   * will fail.
+   */
+  ANDROID_DLEXT_RESERVED_ADDRESS = 0x1,
+
+  /* As DLEXT_RESERVED_ADDRESS, but if the reserved region is not large enough,
+   * the linker will choose an available address instead.
+   */
+  ANDROID_DLEXT_RESERVED_ADDRESS_HINT = 0x2,
+
+  /* When set, write the GNU RELRO section of the mapped library to relro_fd
+   * after relocation has been performed, to allow it to be reused by another
+   * process loading the same library at the same address. This implies
+   * ANDROID_DLEXT_USE_RELRO.
+   */
+  ANDROID_DLEXT_WRITE_RELRO = 0x4,
+
+  /* When set, compare the GNU RELRO section of the mapped library to relro_fd
+   * after relocation has been performed, and replace any relocated pages that
+   * are identical with a version mapped from the file.
+   */
+  ANDROID_DLEXT_USE_RELRO = 0x8,
+
+  /* Instruct dlopen to use library_fd instead of opening file by name.
+   * The filename parameter is still used to identify the library.
+   */
+  ANDROID_DLEXT_USE_LIBRARY_FD = 0x10,
+
+  /* Mask of valid bits */
+  ANDROID_DLEXT_VALID_FLAG_BITS = ANDROID_DLEXT_RESERVED_ADDRESS |
+                                  ANDROID_DLEXT_RESERVED_ADDRESS_HINT |
+                                  ANDROID_DLEXT_WRITE_RELRO |
+                                  ANDROID_DLEXT_USE_RELRO |
+                                  ANDROID_DLEXT_USE_LIBRARY_FD,
+};
+
+typedef struct {
+  uint64_t flags;
+  void* reserved_addr;
+  size_t reserved_size;
+  int relro_fd;
+  int library_fd;
+} android_dlextinfo;
+
+#endif  // BASE_ANDROID_LINKER_ANDROID_DLEXT_H_
diff --git a/android/linker/legacy_linker_jni.cc b/android/linker/legacy_linker_jni.cc
index bc677c4..e4c0455 100644
--- a/android/linker/legacy_linker_jni.cc
+++ b/android/linker/legacy_linker_jni.cc
@@ -2,254 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// This is the Android-specific Chromium linker, a tiny shared library
-// implementing a custom dynamic linker that can be used to load the
-// real Chromium libraries (e.g. libcontentshell.so).
-
-// The main point of this linker is to be able to share the RELRO
-// section of libcontentshell.so (or equivalent) between the browser and
-// renderer process.
+// This is the version of the Android-specific Chromium linker that uses
+// the crazy linker to load libraries.
 
 // This source code *cannot* depend on anything from base/ or the C++
 // STL, to keep the final library small, and avoid ugly dependency issues.
 
-#include <android/log.h>
+#include "legacy_linker_jni.h"
+
 #include <crazy_linker.h>
 #include <fcntl.h>
 #include <jni.h>
 #include <limits.h>
 #include <stdlib.h>
-#include <sys/mman.h>
 #include <unistd.h>
 
-// See commentary in crazy_linker_elf_loader.cpp for the effect of setting
-// this.  If changing there, change here also.
-//
-// For more, see:
-//   https://crbug.com/504410
-#define RESERVE_BREAKPAD_GUARD_REGION 1
+#include "linker_jni.h"
 
-// Set this to 1 to enable debug traces to the Android log.
-// Note that LOG() from "base/logging.h" cannot be used, since it is
-// in base/ which hasn't been loaded yet.
-#define DEBUG 0
-
-#define TAG "chromium_android_linker"
-
-#if DEBUG
-#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
-#else
-#define LOG_INFO(...) ((void)0)
-#endif
-#define LOG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
-
-#define UNUSED __attribute__((unused))
-
+namespace chromium_android_linker {
 namespace {
 
-// A simply scoped UTF String class that can be initialized from
-// a Java jstring handle. Modeled like std::string, which cannot
-// be used here.
-class String {
- public:
-  String(JNIEnv* env, jstring str);
-
-  ~String() {
-    if (ptr_)
-      ::free(ptr_);
-  }
-
-  const char* c_str() const { return ptr_ ? ptr_ : ""; }
-  size_t size() const { return size_; }
-
- private:
-  char* ptr_;
-  size_t size_;
-};
-
-String::String(JNIEnv* env, jstring str) {
-  size_ = env->GetStringUTFLength(str);
-  ptr_ = static_cast<char*>(::malloc(size_ + 1));
-
-  // Note: This runs before browser native code is loaded, and so cannot
-  // rely on anything from base/. This means that we must use
-  // GetStringUTFChars() and not base::android::ConvertJavaStringToUTF8().
-  //
-  // GetStringUTFChars() suffices because the only strings used here are
-  // paths to APK files or names of shared libraries, all of which are
-  // plain ASCII, defined and hard-coded by the Chromium Android build.
-  //
-  // For more: see
-  //   https://crbug.com/508876
-  //
-  // Note: GetStringUTFChars() returns Java UTF-8 bytes. This is good
-  // enough for the linker though.
-  const char* bytes = env->GetStringUTFChars(str, NULL);
-  ::memcpy(ptr_, bytes, size_);
-  ptr_[size_] = '\0';
-
-  env->ReleaseStringUTFChars(str, bytes);
-}
-
-// Return true iff |address| is a valid address for the target CPU.
-bool IsValidAddress(jlong address) {
-  return static_cast<jlong>(static_cast<size_t>(address)) == address;
-}
-
-// Find the jclass JNI reference corresponding to a given |class_name|.
-// |env| is the current JNI environment handle.
-// On success, return true and set |*clazz|.
-bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz) {
-  *clazz = env->FindClass(class_name);
-  if (!*clazz) {
-    LOG_ERROR("Could not find class for %s", class_name);
-    return false;
-  }
-  return true;
-}
-
-// Initialize a jfieldID corresponding to the field of a given |clazz|,
-// with name |field_name| and signature |field_sig|.
-// |env| is the current JNI environment handle.
-// On success, return true and set |*field_id|.
-bool InitFieldId(JNIEnv* env,
-                 jclass clazz,
-                 const char* field_name,
-                 const char* field_sig,
-                 jfieldID* field_id) {
-  *field_id = env->GetFieldID(clazz, field_name, field_sig);
-  if (!*field_id) {
-    LOG_ERROR("Could not find ID for field '%s'", field_name);
-    return false;
-  }
-  LOG_INFO("%s: Found ID %p for field '%s'", __FUNCTION__, *field_id,
-           field_name);
-  return true;
-}
-
-// Initialize a jmethodID corresponding to the static method of a given
-// |clazz|, with name |method_name| and signature |method_sig|.
-// |env| is the current JNI environment handle.
-// On success, return true and set |*method_id|.
-bool InitStaticMethodId(JNIEnv* env,
-                        jclass clazz,
-                        const char* method_name,
-                        const char* method_sig,
-                        jmethodID* method_id) {
-  *method_id = env->GetStaticMethodID(clazz, method_name, method_sig);
-  if (!*method_id) {
-    LOG_ERROR("Could not find ID for static method '%s'", method_name);
-    return false;
-  }
-  LOG_INFO("%s: Found ID %p for static method '%s'", __FUNCTION__, *method_id,
-           method_name);
-  return true;
-}
-
-// Initialize a jfieldID corresponding to the static field of a given |clazz|,
-// with name |field_name| and signature |field_sig|.
-// |env| is the current JNI environment handle.
-// On success, return true and set |*field_id|.
-bool InitStaticFieldId(JNIEnv* env,
-                       jclass clazz,
-                       const char* field_name,
-                       const char* field_sig,
-                       jfieldID* field_id) {
-  *field_id = env->GetStaticFieldID(clazz, field_name, field_sig);
-  if (!*field_id) {
-    LOG_ERROR("Could not find ID for static field '%s'", field_name);
-    return false;
-  }
-  LOG_INFO("%s: Found ID %p for static field '%s'", __FUNCTION__, *field_id,
-           field_name);
-  return true;
-}
-
-// Initialize a jint corresponding to the static integer field of a class
-// with class name |class_name| and field name |field_name|.
-// |env| is the current JNI environment handle.
-// On success, return true and set |*value|.
-bool InitStaticInt(JNIEnv* env,
-                   const char* class_name,
-                   const char* field_name,
-                   jint* value) {
-  jclass clazz;
-  if (!InitClassReference(env, class_name, &clazz))
-    return false;
-
-  jfieldID field_id;
-  if (!InitStaticFieldId(env, clazz, field_name, "I", &field_id))
-    return false;
-
-  *value = env->GetStaticIntField(clazz, field_id);
-  LOG_INFO("%s: Found value %d for class '%s', static field '%s'", __FUNCTION__,
-           *value, class_name, field_name);
-
-  return true;
-}
-
-// A class used to model the field IDs of the org.chromium.base.Linker
-// LibInfo inner class, used to communicate data with the Java side
-// of the linker.
-struct LibInfo_class {
-  jfieldID load_address_id;
-  jfieldID load_size_id;
-  jfieldID relro_start_id;
-  jfieldID relro_size_id;
-  jfieldID relro_fd_id;
-
-  // Initialize an instance.
-  bool Init(JNIEnv* env) {
-    jclass clazz;
-    if (!InitClassReference(
-            env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) {
-      return false;
-    }
-
-    return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) &&
-           InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) &&
-           InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) &&
-           InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) &&
-           InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id);
-  }
-
-  void SetLoadInfo(JNIEnv* env,
-                   jobject library_info_obj,
-                   size_t load_address,
-                   size_t load_size) {
-    env->SetLongField(library_info_obj, load_address_id, load_address);
-    env->SetLongField(library_info_obj, load_size_id, load_size);
-  }
-
-  // Use this instance to convert a RelroInfo reference into
-  // a crazy_library_info_t.
-  void GetRelroInfo(JNIEnv* env,
-                    jobject library_info_obj,
-                    size_t* relro_start,
-                    size_t* relro_size,
-                    int* relro_fd) {
-    *relro_start = static_cast<size_t>(
-        env->GetLongField(library_info_obj, relro_start_id));
-
-    *relro_size =
-        static_cast<size_t>(env->GetLongField(library_info_obj, relro_size_id));
-
-    *relro_fd = env->GetIntField(library_info_obj, relro_fd_id);
-  }
-
-  void SetRelroInfo(JNIEnv* env,
-                    jobject library_info_obj,
-                    size_t relro_start,
-                    size_t relro_size,
-                    int relro_fd) {
-    env->SetLongField(library_info_obj, relro_start_id, relro_start);
-    env->SetLongField(library_info_obj, relro_size_id, relro_size);
-    env->SetIntField(library_info_obj, relro_fd_id, relro_fd);
-  }
-};
-
-static LibInfo_class s_lib_info_fields;
-
 // Retrieve the SDK build version and pass it into the crazy linker. This
 // needs to be done early in initialization, before any other crazy linker
 // code is run.
@@ -261,8 +33,7 @@
     return false;
 
   crazy_set_sdk_build_version(static_cast<int>(value));
-  LOG_INFO("%s: Set SDK build version to %d", __FUNCTION__,
-           static_cast<int>(value));
+  LOG_INFO("Set SDK build version to %d", static_cast<int>(value));
 
   return true;
 }
@@ -270,9 +41,9 @@
 // The linker uses a single crazy_context_t object created on demand.
 // There is no need to protect this against concurrent access, locking
 // is already handled on the Java side.
-static crazy_context_t* s_crazy_context;
-
 crazy_context_t* GetCrazyContext() {
+  static crazy_context_t* s_crazy_context = nullptr;
+
   if (!s_crazy_context) {
     // Create new context.
     s_crazy_context = crazy_context_create();
@@ -290,7 +61,7 @@
 // on scope exit, unless Release() has been called.
 class ScopedLibrary {
  public:
-  ScopedLibrary() : lib_(NULL) {}
+  ScopedLibrary() : lib_(nullptr) {}
 
   ~ScopedLibrary() {
     if (lib_)
@@ -303,7 +74,7 @@
 
   crazy_library_t* Release() {
     crazy_library_t* ret = lib_;
-    lib_ = NULL;
+    lib_ = nullptr;
     return ret;
   }
 
@@ -311,18 +82,17 @@
   crazy_library_t* lib_;
 };
 
-namespace {
-
 template <class LibraryOpener>
 bool GenericLoadLibrary(JNIEnv* env,
                         const char* library_name,
                         jlong load_address,
                         jobject lib_info_obj,
                         const LibraryOpener& opener) {
+  LOG_INFO("Called for %s, at address 0x%llx", library_name, load_address);
   crazy_context_t* context = GetCrazyContext();
 
   if (!IsValidAddress(load_address)) {
-    LOG_ERROR("%s: Invalid address 0x%llx", __FUNCTION__, load_address);
+    LOG_ERROR("Invalid address 0x%llx", load_address);
     return false;
   }
 
@@ -336,8 +106,8 @@
 
   crazy_library_info_t info;
   if (!crazy_library_get_info(library.Get(), context, &info)) {
-    LOG_ERROR("%s: Could not get library information for %s: %s", __FUNCTION__,
-              library_name, crazy_context_get_error(context));
+    LOG_ERROR("Could not get library information for %s: %s", library_name,
+              crazy_context_get_error(context));
     return false;
   }
 
@@ -346,7 +116,7 @@
 
   s_lib_info_fields.SetLoadInfo(env, lib_info_obj, info.load_address,
                                 info.load_size);
-  LOG_INFO("%s: Success loading library %s", __FUNCTION__, library_name);
+  LOG_INFO("Success loading library %s", library_name);
   return true;
 }
 
@@ -362,7 +132,7 @@
                              const char* library_name,
                              crazy_context_t* context) const {
   if (!crazy_library_open(library, library_name, context)) {
-    LOG_ERROR("%s: Could not open %s: %s", __FUNCTION__, library_name,
+    LOG_ERROR("Could not open %s: %s", library_name,
               crazy_context_get_error(context));
     return false;
   }
@@ -386,20 +156,19 @@
                             crazy_context_t* context) const {
   if (!crazy_library_open_in_zip_file(library, zip_file_, library_name,
                                       context)) {
-    LOG_ERROR("%s: Could not open %s in zip file %s: %s", __FUNCTION__,
-              library_name, zip_file_, crazy_context_get_error(context));
+    LOG_ERROR("Could not open %s in zip file %s: %s", library_name, zip_file_,
+              crazy_context_get_error(context));
     return false;
   }
   return true;
 }
 
-}  // unnamed namespace
-
 // Load a library with the chromium linker. This will also call its
 // JNI_OnLoad() method, which shall register its methods. Note that
 // lazy native method resolution will _not_ work after this, because
 // Dalvik uses the system's dlsym() which won't see the new library,
 // so explicit registration is mandatory.
+//
 // |env| is the current JNI environment handle.
 // |clazz| is the static class handle for org.chromium.base.Linker,
 // and is ignored here.
@@ -415,6 +184,7 @@
                      jobject lib_info_obj) {
   String lib_name(env, library_name);
   FileLibraryOpener opener;
+
   return GenericLoadLibrary(env, lib_name.c_str(),
                             static_cast<size_t>(load_address), lib_info_obj,
                             opener);
@@ -451,6 +221,7 @@
   String zipfile_name_str(env, zipfile_name);
   String lib_name(env, library_name);
   ZipLibraryOpener opener(zipfile_name_str.c_str());
+
   return GenericLoadLibrary(env, lib_name.c_str(),
                             static_cast<size_t>(load_address), lib_info_obj,
                             opener);
@@ -481,7 +252,7 @@
 void RunCallbackOnUiThread(JNIEnv* env, jclass clazz, jlong arg) {
   crazy_callback_t* callback = reinterpret_cast<crazy_callback_t*>(arg);
 
-  LOG_INFO("%s: Called back from java with handler %p, opaque %p", __FUNCTION__,
+  LOG_INFO("Called back from java with handler %p, opaque %p",
            callback->handler, callback->opaque);
 
   crazy_callback_run(callback);
@@ -517,8 +288,8 @@
   crazy_callback_t* callback = new crazy_callback_t();
   *callback = *callback_request;
 
-  LOG_INFO("%s: Calling back to java with handler %p, opaque %p", __FUNCTION__,
-           callback->handler, callback->opaque);
+  LOG_INFO("Calling back to java with handler %p, opaque %p", callback->handler,
+           callback->opaque);
 
   jlong arg = static_cast<jlong>(reinterpret_cast<uintptr_t>(callback));
 
@@ -543,16 +314,16 @@
                            jobject lib_info_obj) {
   String lib_name(env, library_name);
 
-  LOG_INFO("%s: Called for %s", __FUNCTION__, lib_name.c_str());
+  LOG_INFO("Called for %s", lib_name.c_str());
 
   if (!IsValidAddress(load_address)) {
-    LOG_ERROR("%s: Invalid address 0x%llx", __FUNCTION__, load_address);
+    LOG_ERROR("Invalid address 0x%llx", load_address);
     return false;
   }
 
   ScopedLibrary library;
   if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
-    LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str());
+    LOG_ERROR("Could not find %s", lib_name.c_str());
     return false;
   }
 
@@ -564,8 +335,8 @@
   if (!crazy_library_create_shared_relro(
           library.Get(), context, static_cast<size_t>(load_address),
           &relro_start, &relro_size, &relro_fd)) {
-    LOG_ERROR("%s: Could not create shared RELRO sharing for %s: %s\n",
-              __FUNCTION__, lib_name.c_str(), crazy_context_get_error(context));
+    LOG_ERROR("Could not create shared RELRO sharing for %s: %s\n",
+              lib_name.c_str(), crazy_context_get_error(context));
     return false;
   }
 
@@ -580,12 +351,11 @@
                         jobject lib_info_obj) {
   String lib_name(env, library_name);
 
-  LOG_INFO("%s: called for %s, lib_info_ref=%p", __FUNCTION__, lib_name.c_str(),
-           lib_info_obj);
+  LOG_INFO("Called for %s, lib_info_ref=%p", lib_name.c_str(), lib_info_obj);
 
   ScopedLibrary library;
   if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
-    LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str());
+    LOG_ERROR("Could not find %s", lib_name.c_str());
     return false;
   }
 
@@ -596,48 +366,21 @@
   s_lib_info_fields.GetRelroInfo(env, lib_info_obj, &relro_start, &relro_size,
                                  &relro_fd);
 
-  LOG_INFO("%s: library=%s relro start=%p size=%p fd=%d", __FUNCTION__,
-           lib_name.c_str(), (void*)relro_start, (void*)relro_size, relro_fd);
+  LOG_INFO("library=%s relro start=%p size=%p fd=%d", lib_name.c_str(),
+           (void*)relro_start, (void*)relro_size, relro_fd);
 
   if (!crazy_library_use_shared_relro(library.Get(), context, relro_start,
                                       relro_size, relro_fd)) {
-    LOG_ERROR("%s: Could not use shared RELRO for %s: %s", __FUNCTION__,
-              lib_name.c_str(), crazy_context_get_error(context));
+    LOG_ERROR("Could not use shared RELRO for %s: %s", lib_name.c_str(),
+              crazy_context_get_error(context));
     return false;
   }
 
-  LOG_INFO("%s: Library %s using shared RELRO section!", __FUNCTION__,
-           lib_name.c_str());
+  LOG_INFO("Library %s using shared RELRO section!", lib_name.c_str());
 
   return true;
 }
 
-jlong GetRandomBaseLoadAddress(JNIEnv* env, jclass clazz, jlong bytes) {
-#if RESERVE_BREAKPAD_GUARD_REGION
-  // Add a Breakpad guard region.  16Mb should be comfortably larger than
-  // the largest relocation packer saving we expect to encounter.
-  static const size_t kBreakpadGuardRegionBytes = 16 * 1024 * 1024;
-  bytes += kBreakpadGuardRegionBytes;
-#endif
-
-  void* address =
-      mmap(NULL, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-  if (address == MAP_FAILED) {
-    LOG_INFO("%s: Random base load address not determinable\n", __FUNCTION__);
-    return 0;
-  }
-  munmap(address, bytes);
-
-#if RESERVE_BREAKPAD_GUARD_REGION
-  // Allow for a Breakpad guard region ahead of the returned address.
-  address = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(address) +
-                                    kBreakpadGuardRegionBytes);
-#endif
-
-  LOG_INFO("%s: Random base load address is %p\n", __FUNCTION__, address);
-  return static_cast<jlong>(reinterpret_cast<uintptr_t>(address));
-}
-
 const JNINativeMethod kNativeMethods[] = {
     {"nativeLoadLibrary",
      "("
@@ -677,53 +420,32 @@
      ")"
      "Z",
      reinterpret_cast<void*>(&UseSharedRelro)},
-    {"nativeGetRandomBaseLoadAddress",
-     "("
-     "J"
-     ")"
-     "J",
-     reinterpret_cast<void*>(&GetRandomBaseLoadAddress)},
 };
 
 }  // namespace
 
-// JNI_OnLoad() hook called when the linker library is loaded through
-// the regular System.LoadLibrary) API. This shall save the Java VM
-// handle and initialize LibInfo fields.
-jint JNI_OnLoad(JavaVM* vm, void* reserved) {
-  LOG_INFO("%s: Entering", __FUNCTION__);
-  // Get new JNIEnv
-  JNIEnv* env;
-  if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) {
-    LOG_ERROR("Could not create JNIEnv");
-    return -1;
-  }
+bool LegacyLinkerJNIInit(JavaVM* vm, JNIEnv* env) {
+  LOG_INFO("Entering");
 
   // Initialize SDK version info.
-  LOG_INFO("%s: Retrieving SDK version info", __FUNCTION__);
+  LOG_INFO("Retrieving SDK version info");
   if (!InitSDKVersionInfo(env))
-    return -1;
+    return false;
 
   // Register native methods.
   jclass linker_class;
   if (!InitClassReference(env, "org/chromium/base/library_loader/LegacyLinker",
                           &linker_class))
-    return -1;
+    return false;
 
-  LOG_INFO("%s: Registering native methods", __FUNCTION__);
+  LOG_INFO("Registering native methods");
   env->RegisterNatives(linker_class, kNativeMethods,
                        sizeof(kNativeMethods) / sizeof(kNativeMethods[0]));
 
-  // Find LibInfo field ids.
-  LOG_INFO("%s: Caching field IDs", __FUNCTION__);
-  if (!s_lib_info_fields.Init(env)) {
-    return -1;
-  }
-
   // Resolve and save the Java side Linker callback class and method.
-  LOG_INFO("%s: Resolving callback bindings", __FUNCTION__);
+  LOG_INFO("Resolving callback bindings");
   if (!s_java_callback_bindings.Init(env, linker_class)) {
-    return -1;
+    return false;
   }
 
   // Save JavaVM* handle into context.
@@ -732,8 +454,9 @@
 
   // Register the function that the crazy linker can call to post code
   // for later execution.
-  crazy_context_set_callback_poster(context, &PostForLaterExecution, NULL);
+  crazy_context_set_callback_poster(context, &PostForLaterExecution, nullptr);
 
-  LOG_INFO("%s: Done", __FUNCTION__);
-  return JNI_VERSION_1_4;
+  return true;
 }
+
+}  // namespace chromium_android_linker
diff --git a/android/linker/legacy_linker_jni.h b/android/linker/legacy_linker_jni.h
new file mode 100644
index 0000000..fcacf12
--- /dev/null
+++ b/android/linker/legacy_linker_jni.h
@@ -0,0 +1,21 @@
+// Copyright 2015 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 BASE_ANDROID_LINKER_LEGACY_LINKER_JNI_H_
+#define BASE_ANDROID_LINKER_LEGACY_LINKER_JNI_H_
+
+#include <jni.h>
+
+namespace chromium_android_linker {
+
+// JNI_OnLoad() initialization hook for the legacy linker.
+// Sets up JNI and other initializations for native linker code.
+// |vm| is the Java VM handle passed to JNI_OnLoad().
+// |env| is the current JNI environment handle.
+// On success, returns true.
+extern bool LegacyLinkerJNIInit(JavaVM* vm, JNIEnv* env);
+
+}  // namespace chromium_android_linker
+
+#endif  // BASE_ANDROID_LINKER_LEGACY_LINKER_JNI_H_
diff --git a/android/linker/linker_jni.cc b/android/linker/linker_jni.cc
new file mode 100644
index 0000000..4db8c3f
--- /dev/null
+++ b/android/linker/linker_jni.cc
@@ -0,0 +1,237 @@
+// Copyright 2015 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 is the Android-specific Chromium linker, a tiny shared library
+// implementing a custom dynamic linker that can be used to load the
+// real Chromium libraries.
+
+// The main point of this linker is to be able to share the RELRO
+// section of libchrome.so (or equivalent) between renderer processes.
+
+// This source code *cannot* depend on anything from base/ or the C++
+// STL, to keep the final library small, and avoid ugly dependency issues.
+
+#include "linker_jni.h"
+
+#include <sys/mman.h>
+#include <jni.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "legacy_linker_jni.h"
+#include "modern_linker_jni.h"
+
+namespace chromium_android_linker {
+
+// Variable containing LibInfo for the loaded library.
+LibInfo_class s_lib_info_fields;
+
+// Simple scoped UTF String class constructor.
+String::String(JNIEnv* env, jstring str) {
+  size_ = env->GetStringUTFLength(str);
+  ptr_ = static_cast<char*>(::malloc(size_ + 1));
+
+  // Note: This runs before browser native code is loaded, and so cannot
+  // rely on anything from base/. This means that we must use
+  // GetStringUTFChars() and not base::android::ConvertJavaStringToUTF8().
+  //
+  // GetStringUTFChars() suffices because the only strings used here are
+  // paths to APK files or names of shared libraries, all of which are
+  // plain ASCII, defined and hard-coded by the Chromium Android build.
+  //
+  // For more: see
+  //   https://crbug.com/508876
+  //
+  // Note: GetStringUTFChars() returns Java UTF-8 bytes. This is good
+  // enough for the linker though.
+  const char* bytes = env->GetStringUTFChars(str, nullptr);
+  ::memcpy(ptr_, bytes, size_);
+  ptr_[size_] = '\0';
+
+  env->ReleaseStringUTFChars(str, bytes);
+}
+
+// Find the jclass JNI reference corresponding to a given |class_name|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*clazz|.
+bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz) {
+  *clazz = env->FindClass(class_name);
+  if (!*clazz) {
+    LOG_ERROR("Could not find class for %s", class_name);
+    return false;
+  }
+  return true;
+}
+
+// Initialize a jfieldID corresponding to the field of a given |clazz|,
+// with name |field_name| and signature |field_sig|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*field_id|.
+bool InitFieldId(JNIEnv* env,
+                 jclass clazz,
+                 const char* field_name,
+                 const char* field_sig,
+                 jfieldID* field_id) {
+  *field_id = env->GetFieldID(clazz, field_name, field_sig);
+  if (!*field_id) {
+    LOG_ERROR("Could not find ID for field '%s'", field_name);
+    return false;
+  }
+  LOG_INFO("Found ID %p for field '%s'", *field_id, field_name);
+  return true;
+}
+
+// Initialize a jmethodID corresponding to the static method of a given
+// |clazz|, with name |method_name| and signature |method_sig|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*method_id|.
+bool InitStaticMethodId(JNIEnv* env,
+                        jclass clazz,
+                        const char* method_name,
+                        const char* method_sig,
+                        jmethodID* method_id) {
+  *method_id = env->GetStaticMethodID(clazz, method_name, method_sig);
+  if (!*method_id) {
+    LOG_ERROR("Could not find ID for static method '%s'", method_name);
+    return false;
+  }
+  LOG_INFO("Found ID %p for static method '%s'", *method_id, method_name);
+  return true;
+}
+
+// Initialize a jfieldID corresponding to the static field of a given |clazz|,
+// with name |field_name| and signature |field_sig|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*field_id|.
+bool InitStaticFieldId(JNIEnv* env,
+                       jclass clazz,
+                       const char* field_name,
+                       const char* field_sig,
+                       jfieldID* field_id) {
+  *field_id = env->GetStaticFieldID(clazz, field_name, field_sig);
+  if (!*field_id) {
+    LOG_ERROR("Could not find ID for static field '%s'", field_name);
+    return false;
+  }
+  LOG_INFO("Found ID %p for static field '%s'", *field_id, field_name);
+  return true;
+}
+
+// Initialize a jint corresponding to the static integer field of a class
+// with class name |class_name| and field name |field_name|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*value|.
+bool InitStaticInt(JNIEnv* env,
+                   const char* class_name,
+                   const char* field_name,
+                   jint* value) {
+  jclass clazz;
+  if (!InitClassReference(env, class_name, &clazz))
+    return false;
+
+  jfieldID field_id;
+  if (!InitStaticFieldId(env, clazz, field_name, "I", &field_id))
+    return false;
+
+  *value = env->GetStaticIntField(clazz, field_id);
+  LOG_INFO("Found value %d for class '%s', static field '%s'", *value,
+           class_name, field_name);
+
+  return true;
+}
+
+// Use Android ASLR to create a random address into which we expect to be
+// able to load libraries. Note that this is probabilistic; we unmap the
+// address we get from mmap and assume we can re-map into it later. This
+// works the majority of the time. If it doesn't, client code backs out and
+// then loads the library normally at any available address.
+// |env| is the current JNI environment handle, and |clazz| a class.
+// Returns the address selected by ASLR, or 0 on error.
+jlong GetRandomBaseLoadAddress(JNIEnv* env, jclass clazz) {
+  size_t bytes = kAddressSpaceReservationSize;
+
+#if RESERVE_BREAKPAD_GUARD_REGION
+  // Pad the requested address space size for a Breakpad guard region.
+  bytes += kBreakpadGuardRegionBytes;
+#endif
+
+  void* address =
+      mmap(nullptr, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (address == MAP_FAILED) {
+    LOG_INFO("Random base load address not determinable");
+    return 0;
+  }
+  munmap(address, bytes);
+
+#if RESERVE_BREAKPAD_GUARD_REGION
+  // Allow for a Breakpad guard region ahead of the returned address.
+  address = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(address) +
+                                    kBreakpadGuardRegionBytes);
+#endif
+
+  LOG_INFO("Random base load address is %p", address);
+  return static_cast<jlong>(reinterpret_cast<uintptr_t>(address));
+}
+
+namespace {
+
+const JNINativeMethod kNativeMethods[] = {
+    {"nativeGetRandomBaseLoadAddress",
+     "("
+     ")"
+     "J",
+     reinterpret_cast<void*>(&GetRandomBaseLoadAddress)},
+};
+
+// JNI_OnLoad() initialization hook.
+bool LinkerJNIInit(JavaVM* vm, JNIEnv* env) {
+  LOG_INFO("Entering");
+
+  // Register native methods.
+  jclass linker_class;
+  if (!InitClassReference(env, "org/chromium/base/library_loader/Linker",
+                          &linker_class))
+    return false;
+
+  LOG_INFO("Registering native methods");
+  env->RegisterNatives(linker_class, kNativeMethods,
+                       sizeof(kNativeMethods) / sizeof(kNativeMethods[0]));
+
+  // Find LibInfo field ids.
+  LOG_INFO("Caching field IDs");
+  if (!s_lib_info_fields.Init(env)) {
+    return false;
+  }
+
+  return true;
+}
+
+// JNI_OnLoad() hook called when the linker library is loaded through
+// the regular System.LoadLibrary) API. This shall save the Java VM
+// handle and initialize LibInfo fields.
+jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  LOG_INFO("Entering");
+  // Get new JNIEnv
+  JNIEnv* env;
+  if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) {
+    LOG_ERROR("Could not create JNIEnv");
+    return -1;
+  }
+
+  // Initialize linker base and implementations.
+  if (!LinkerJNIInit(vm, env) || !LegacyLinkerJNIInit(vm, env) ||
+      !ModernLinkerJNIInit(vm, env)) {
+    return -1;
+  }
+
+  LOG_INFO("Done");
+  return JNI_VERSION_1_4;
+}
+
+}  // namespace
+}  // namespace chromium_android_linker
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  return chromium_android_linker::JNI_OnLoad(vm, reserved);
+}
diff --git a/android/linker/linker_jni.h b/android/linker/linker_jni.h
new file mode 100644
index 0000000..55dc6f9
--- /dev/null
+++ b/android/linker/linker_jni.h
@@ -0,0 +1,206 @@
+// Copyright 2015 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 is the Android-specific Chromium linker, a tiny shared library
+// implementing a custom dynamic linker that can be used to load the
+// real Chromium libraries.
+
+// The main point of this linker is to be able to share the RELRO
+// section of libcontentshell.so (or equivalent) between the browser and
+// renderer process.
+
+// This source code *cannot* depend on anything from base/ or the C++
+// STL, to keep the final library small, and avoid ugly dependency issues.
+
+#ifndef BASE_ANDROID_LINKER_LINKER_JNI_H_
+#define BASE_ANDROID_LINKER_LINKER_JNI_H_
+
+#include <android/log.h>
+#include <jni.h>
+#include <stdlib.h>
+
+// Set this to 1 to enable debug traces to the Android log.
+// Note that LOG() from "base/logging.h" cannot be used, since it is
+// in base/ which hasn't been loaded yet.
+#define DEBUG 0
+
+#define TAG "cr.chromium_android_linker"
+
+#if DEBUG
+#define LOG_INFO(FORMAT, ...)                                             \
+  __android_log_print(ANDROID_LOG_INFO, TAG, "%s: " FORMAT, __FUNCTION__, \
+                      ##__VA_ARGS__)
+#else
+#define LOG_INFO(FORMAT, ...) ((void)0)
+#endif
+#define LOG_ERROR(FORMAT, ...)                                             \
+  __android_log_print(ANDROID_LOG_ERROR, TAG, "%s: " FORMAT, __FUNCTION__, \
+                      ##__VA_ARGS__)
+
+#define UNUSED __attribute__((unused))
+
+// See commentary in crazy_linker_elf_loader.cpp for the effect of setting
+// this. If changing there, change here also.
+//
+// For more, see:
+//   https://crbug.com/504410
+#define RESERVE_BREAKPAD_GUARD_REGION 1
+
+namespace chromium_android_linker {
+
+// Larger than the largest library we might attempt to load.
+static const size_t kAddressSpaceReservationSize = 192 * 1024 * 1024;
+
+// Size of any Breakpad guard region. 16MB is comfortably larger than the
+// ~6MB relocation packing of the current 64-bit libchrome.so, the largest we
+// expect to encounter.
+#if RESERVE_BREAKPAD_GUARD_REGION
+static const size_t kBreakpadGuardRegionBytes = 16 * 1024 * 1024;
+#endif
+
+// A simple scoped UTF String class that can be initialized from
+// a Java jstring handle. Modeled like std::string, which cannot
+// be used here.
+class String {
+ public:
+  String(JNIEnv* env, jstring str);
+
+  inline ~String() { ::free(ptr_); }
+
+  inline const char* c_str() const { return ptr_ ? ptr_ : ""; }
+  inline size_t size() const { return size_; }
+
+ private:
+  char* ptr_;
+  size_t size_;
+};
+
+// Return true iff |address| is a valid address for the target CPU.
+inline bool IsValidAddress(jlong address) {
+  return static_cast<jlong>(static_cast<size_t>(address)) == address;
+}
+
+// Find the jclass JNI reference corresponding to a given |class_name|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*clazz|.
+extern bool InitClassReference(JNIEnv* env,
+                               const char* class_name,
+                               jclass* clazz);
+
+// Initialize a jfieldID corresponding to the field of a given |clazz|,
+// with name |field_name| and signature |field_sig|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*field_id|.
+extern bool InitFieldId(JNIEnv* env,
+                        jclass clazz,
+                        const char* field_name,
+                        const char* field_sig,
+                        jfieldID* field_id);
+
+// Initialize a jmethodID corresponding to the static method of a given
+// |clazz|, with name |method_name| and signature |method_sig|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*method_id|.
+extern bool InitStaticMethodId(JNIEnv* env,
+                               jclass clazz,
+                               const char* method_name,
+                               const char* method_sig,
+                               jmethodID* method_id);
+
+// Initialize a jfieldID corresponding to the static field of a given |clazz|,
+// with name |field_name| and signature |field_sig|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*field_id|.
+extern bool InitStaticFieldId(JNIEnv* env,
+                              jclass clazz,
+                              const char* field_name,
+                              const char* field_sig,
+                              jfieldID* field_id);
+
+// Initialize a jint corresponding to the static integer field of a class
+// with class name |class_name| and field name |field_name|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*value|.
+extern bool InitStaticInt(JNIEnv* env,
+                          const char* class_name,
+                          const char* field_name,
+                          jint* value);
+
+// Use Android ASLR to create a random library load address.
+// |env| is the current JNI environment handle, and |clazz| a class.
+// Returns the address selected by ASLR.
+extern jlong GetRandomBaseLoadAddress(JNIEnv* env, jclass clazz);
+
+// A class used to model the field IDs of the org.chromium.base.Linker
+// LibInfo inner class, used to communicate data with the Java side
+// of the linker.
+struct LibInfo_class {
+  jfieldID load_address_id;
+  jfieldID load_size_id;
+  jfieldID relro_start_id;
+  jfieldID relro_size_id;
+  jfieldID relro_fd_id;
+
+  // Initialize an instance.
+  bool Init(JNIEnv* env) {
+    jclass clazz;
+    if (!InitClassReference(
+            env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) {
+      return false;
+    }
+
+    return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) &&
+           InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) &&
+           InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) &&
+           InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) &&
+           InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id);
+  }
+
+  void SetLoadInfo(JNIEnv* env,
+                   jobject library_info_obj,
+                   size_t load_address,
+                   size_t load_size) {
+    env->SetLongField(library_info_obj, load_address_id, load_address);
+    env->SetLongField(library_info_obj, load_size_id, load_size);
+  }
+
+  void SetRelroInfo(JNIEnv* env,
+                    jobject library_info_obj,
+                    size_t relro_start,
+                    size_t relro_size,
+                    int relro_fd) {
+    env->SetLongField(library_info_obj, relro_start_id, relro_start);
+    env->SetLongField(library_info_obj, relro_size_id, relro_size);
+    env->SetIntField(library_info_obj, relro_fd_id, relro_fd);
+  }
+
+  // Use this instance to convert a RelroInfo reference into
+  // a crazy_library_info_t.
+  void GetRelroInfo(JNIEnv* env,
+                    jobject library_info_obj,
+                    size_t* relro_start,
+                    size_t* relro_size,
+                    int* relro_fd) {
+    if (relro_start) {
+      *relro_start = static_cast<size_t>(
+          env->GetLongField(library_info_obj, relro_start_id));
+    }
+
+    if (relro_size) {
+      *relro_size = static_cast<size_t>(
+          env->GetLongField(library_info_obj, relro_size_id));
+    }
+
+    if (relro_fd) {
+      *relro_fd = env->GetIntField(library_info_obj, relro_fd_id);
+    }
+  }
+};
+
+// Variable containing LibInfo for the loaded library.
+extern LibInfo_class s_lib_info_fields;
+
+}  // namespace chromium_android_linker
+
+#endif  // BASE_ANDROID_LINKER_LINKER_JNI_H_
diff --git a/android/linker/modern_linker_jni.cc b/android/linker/modern_linker_jni.cc
new file mode 100644
index 0000000..8661636
--- /dev/null
+++ b/android/linker/modern_linker_jni.cc
@@ -0,0 +1,595 @@
+// Copyright 2015 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 is the version of the Android-specific Chromium linker that uses
+// the Android M and later system linker to load libraries.
+
+// This source code *cannot* depend on anything from base/ or the C++
+// STL, to keep the final library small, and avoid ugly dependency issues.
+
+#include "modern_linker_jni.h"
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <jni.h>
+#include <limits.h>
+#include <link.h>
+#include <string.h>
+
+#include "android_dlext.h"
+#include "linker_jni.h"
+
+#define PAGE_START(x) ((x)&PAGE_MASK)
+#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE - 1))
+
+namespace chromium_android_linker {
+namespace {
+
+// Record of the Java VM passed to JNI_OnLoad().
+static JavaVM* s_java_vm = nullptr;
+
+// Get the CPU ABI string for which the linker is running.
+//
+// The returned string is used to construct the path to libchrome.so when
+// loading directly from APK.
+//
+// |env| is the current JNI environment handle.
+// |clazz| is the static class handle for org.chromium.base.Linker,
+// and is ignored here.
+// Returns the CPU ABI string for which the linker is running.
+jstring GetCpuAbi(JNIEnv* env, jclass clazz) {
+#if defined(__arm__) && defined(__ARM_ARCH_7A__)
+  static const char* kCurrentABI = "armeabi-v7a";
+#elif defined(__arm__)
+  static const char* kCurrentABI = "armeabi";
+#elif defined(__i386__)
+  static const char* kCurrentABI = "x86";
+#elif defined(__mips__)
+  static const char* kCurrentABI = "mips";
+#elif defined(__x86_64__)
+  static const char* kCurrentABI = "x86_64";
+#elif defined(__aarch64__)
+  static const char* kCurrentABI = "arm64-v8a";
+#else
+#error "Unsupported target abi"
+#endif
+  return env->NewStringUTF(kCurrentABI);
+}
+
+// Convenience wrapper around dlsym() on the main executable. Returns
+// the address of the requested symbol, or nullptr if not found. Status
+// is available from dlerror().
+void* Dlsym(const char* symbol_name) {
+  static void* handle = nullptr;
+
+  if (!handle)
+    handle = dlopen(nullptr, RTLD_NOW);
+
+  void* result = dlsym(handle, symbol_name);
+  return result;
+}
+
+// dl_iterate_phdr() wrapper, accessed via dlsym lookup. Done this way.
+// so that this code compiles for Android versions that are too early to
+// offer it. Checks in LibraryLoader.java should ensure that we
+// never reach here at runtime on Android versions that are too old to
+// supply dl_iterate_phdr; that is, earlier than Android M. Returns
+// false if no dl_iterate_phdr() is available, otherwise true with the
+// return value from dl_iterate_phdr() in |status|.
+bool DlIteratePhdr(int (*callback)(dl_phdr_info*, size_t, void*),
+                   void* data,
+                   int* status) {
+  using DlIteratePhdrCallback = int (*)(dl_phdr_info*, size_t, void*);
+  using DlIteratePhdrFunctionPtr = int (*)(DlIteratePhdrCallback, void*);
+  static DlIteratePhdrFunctionPtr function_ptr = nullptr;
+
+  if (!function_ptr) {
+    function_ptr =
+        reinterpret_cast<DlIteratePhdrFunctionPtr>(Dlsym("dl_iterate_phdr"));
+    if (!function_ptr) {
+      LOG_ERROR("dlsym: dl_iterate_phdr: %s", dlerror());
+      return false;
+    }
+  }
+
+  *status = (*function_ptr)(callback, data);
+  return true;
+}
+
+// Convenience struct wrapper round android_dlextinfo.
+struct AndroidDlextinfo {
+  AndroidDlextinfo(int flags,
+                   void* reserved_addr,
+                   size_t reserved_size,
+                   int relro_fd) {
+    memset(&extinfo, 0, sizeof(extinfo));
+    extinfo.flags = flags;
+    extinfo.reserved_addr = reserved_addr;
+    extinfo.reserved_size = reserved_size;
+    extinfo.relro_fd = relro_fd;
+  }
+
+  android_dlextinfo extinfo;
+};
+
+// android_dlopen_ext() wrapper, accessed via dlsym lookup. Returns false
+// if no android_dlopen_ext() is available, otherwise true with the return
+// value from android_dlopen_ext() in |status|.
+bool AndroidDlopenExt(const char* filename,
+                      int flag,
+                      const AndroidDlextinfo* dlextinfo,
+                      void** status) {
+  using DlopenExtFunctionPtr =
+      void* (*)(const char*, int, const android_dlextinfo*);
+  static DlopenExtFunctionPtr function_ptr = nullptr;
+
+  if (!function_ptr) {
+    function_ptr =
+        reinterpret_cast<DlopenExtFunctionPtr>(Dlsym("android_dlopen_ext"));
+    if (!function_ptr) {
+      LOG_ERROR("dlsym: android_dlopen_ext: %s", dlerror());
+      return false;
+    }
+  }
+
+  const android_dlextinfo* extinfo = &dlextinfo->extinfo;
+  LOG_INFO(
+      "android_dlopen_ext:"
+      " flags=0x%llx, reserved_addr=%p, reserved_size=%d, relro_fd=%d",
+      extinfo->flags, extinfo->reserved_addr, extinfo->reserved_size,
+      extinfo->relro_fd);
+
+  *status = (*function_ptr)(filename, flag, extinfo);
+  return true;
+}
+
+// Callback data for FindLoadedLibrarySize().
+struct CallbackData {
+  explicit CallbackData(void* address) : load_address(address), load_size(0) {}
+
+  const void* load_address;
+  size_t load_size;
+};
+
+// Callback for dl_iterate_phdr(). Read phdrs to identify whether or not
+// this library's load address matches the |load_address| passed in
+// |data|. If yes, pass back load size via |data|. A non-zero return value
+// terminates iteration.
+int FindLoadedLibrarySize(dl_phdr_info* info, size_t size UNUSED, void* data) {
+  CallbackData* callback_data = reinterpret_cast<CallbackData*>(data);
+
+  // Use max and min vaddr to compute the library's load size.
+  ElfW(Addr) min_vaddr = ~0;
+  ElfW(Addr) max_vaddr = 0;
+
+  bool is_matching = false;
+  for (size_t i = 0; i < info->dlpi_phnum; ++i) {
+    const ElfW(Phdr)* phdr = &info->dlpi_phdr[i];
+    if (phdr->p_type != PT_LOAD)
+      continue;
+
+    // See if this segment's load address matches what we passed to
+    // android_dlopen_ext as extinfo.reserved_addr.
+    void* load_addr = reinterpret_cast<void*>(info->dlpi_addr + phdr->p_vaddr);
+    if (load_addr == callback_data->load_address)
+      is_matching = 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 this library matches what we seek, return its load size.
+  if (is_matching) {
+    callback_data->load_size = PAGE_END(max_vaddr) - PAGE_START(min_vaddr);
+    return true;
+  }
+
+  return false;
+}
+
+// Helper class for anonymous memory mapping.
+class ScopedAnonymousMmap {
+ public:
+  ScopedAnonymousMmap(void* addr, size_t size);
+
+  ~ScopedAnonymousMmap() { munmap(addr_, size_); }
+
+  void* GetAddr() const { return effective_addr_; }
+  void Release() {
+    addr_ = nullptr;
+    size_ = 0;
+    effective_addr_ = nullptr;
+  }
+
+ private:
+  void* addr_;
+  size_t size_;
+
+  // The effective_addr_ is the address seen by client code. It may or may
+  // not be the same as addr_, the real start of the anonymous mapping.
+  void* effective_addr_;
+};
+
+// ScopedAnonymousMmap constructor. |addr| is a requested mapping address, or
+// zero if any address will do, and |size| is the size of mapping required.
+ScopedAnonymousMmap::ScopedAnonymousMmap(void* addr, size_t size) {
+#if RESERVE_BREAKPAD_GUARD_REGION
+  // Increase size to extend the address reservation mapping so that it will
+  // also include a guard region from load_bias_ to start_addr. If loading
+  // at a fixed address, move our requested address back by the guard region
+  // size.
+  size += kBreakpadGuardRegionBytes;
+  if (addr) {
+    if (addr < reinterpret_cast<void*>(kBreakpadGuardRegionBytes)) {
+      LOG_ERROR("Fixed address %p is too low to accommodate Breakpad guard",
+                addr);
+      addr_ = MAP_FAILED;
+      size_ = 0;
+      return;
+    }
+    addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) -
+                                   kBreakpadGuardRegionBytes);
+  }
+  LOG_INFO("Added %d to size, for Breakpad guard", kBreakpadGuardRegionBytes);
+#endif
+
+  addr_ = mmap(addr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (addr_ != MAP_FAILED) {
+    size_ = size;
+  } else {
+    LOG_INFO("mmap failed: %s", strerror(errno));
+    size_ = 0;
+  }
+  effective_addr_ = addr_;
+
+#if RESERVE_BREAKPAD_GUARD_REGION
+  // If we increased size to accommodate a Breakpad guard region, move
+  // the effective address, if valid, upwards by the size of the guard region.
+  if (addr_ == MAP_FAILED)
+    return;
+  if (addr_ < reinterpret_cast<void*>(kBreakpadGuardRegionBytes)) {
+    LOG_ERROR("Map address %p is too low to accommodate Breakpad guard", addr_);
+    effective_addr_ = MAP_FAILED;
+  } else {
+    effective_addr_ = reinterpret_cast<void*>(
+        reinterpret_cast<uintptr_t>(addr_) + kBreakpadGuardRegionBytes);
+  }
+#endif
+}
+
+// Helper for LoadLibrary(). Return the actual size of the library loaded
+// at |addr| in |load_size|. Returns false if the library appears not to
+// be loaded.
+bool GetLibraryLoadSize(void* addr, size_t* load_size) {
+  LOG_INFO("Called for %p", addr);
+
+  // Find the real load size for the library loaded at |addr|.
+  CallbackData callback_data(addr);
+  int status = 0;
+  if (!DlIteratePhdr(&FindLoadedLibrarySize, &callback_data, &status)) {
+    LOG_ERROR("No dl_iterate_phdr function found");
+    return false;
+  }
+  if (!status) {
+    LOG_ERROR("Failed to find library at address %p", addr);
+    return false;
+  }
+
+  *load_size = callback_data.load_size;
+  return true;
+}
+
+// Helper for LoadLibrary(). We reserve an address space larger than
+// needed. After library loading we want to trim that reservation to only
+// what is needed.
+bool ResizeReservedAddressSpace(void* addr,
+                                size_t reserved_size,
+                                size_t load_size) {
+  LOG_INFO("Called for %p, reserved %d, loaded %d", addr, reserved_size,
+           load_size);
+
+  if (reserved_size < load_size) {
+    LOG_ERROR("WARNING: library reservation was too small");
+    return true;
+  }
+
+  // Unmap the part of the reserved address space that is beyond the end of
+  // the loaded library data.
+  void* unmap =
+      reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) + load_size);
+  size_t length = reserved_size - load_size;
+  if (munmap(unmap, length) == -1) {
+    LOG_ERROR("Failed to unmap %d at %p", static_cast<int>(length), unmap);
+    return false;
+  }
+
+  return true;
+}
+
+// Load a library with the chromium linker, using android_dlopen_ext().
+//
+// android_dlopen_ext() understands how to directly load from a zipfile,
+// based on the format of |dlopen_ext_path|. If it contains a "!/" separator
+// then the string indicates <zip_path>!/<file_path> and indicates the
+// file_path element within the zip file at zip_path. A library in a
+// zipfile must be uncompressed and page aligned. The library is expected
+// to be lib/<abi_tag>/crazy.<basename>. The <abi_tag> used will be the
+// same as the abi for this linker. The "crazy." prefix is included
+// so that the Android Package Manager doesn't extract the library into
+// /data/app-lib.
+//
+// If |dlopen_ext_path| contains no "!/" separator then android_dlopen_ext()
+// assumes that it is a normal path to a standalone library file.
+//
+// Loading the library will also call its JNI_OnLoad() method, which
+// shall register its methods. Note that lazy native method resolution
+// will _not_ work after this, because Dalvik uses the system's dlsym()
+// which won't see the new library, so explicit registration is mandatory.
+// Load a library with the chromium linker. This will also call its
+// JNI_OnLoad() method, which shall register its methods. Note that
+// lazy native method resolution will _not_ work after this, because
+// Dalvik uses the system's dlsym() which won't see the new library,
+// so explicit registration is mandatory.
+//
+// |env| is the current JNI environment handle.
+// |clazz| is the static class handle for org.chromium.base.Linker,
+// and is ignored here.
+// |dlopen_ext_path| is the library identifier (e.g. libfoo.so).
+// |load_address| is an explicit load address.
+// |relro_path| is the path to the file into which RELRO data is held.
+// |lib_info_obj| is a LibInfo handle used to communicate information
+// with the Java side.
+// Return true on success.
+jboolean LoadLibrary(JNIEnv* env,
+                     jclass clazz,
+                     jstring dlopen_ext_path,
+                     jlong load_address,
+                     jobject lib_info_obj) {
+  String dlopen_library_path(env, dlopen_ext_path);
+  LOG_INFO("Called for %s, at address 0x%llx", dlopen_library_path.c_str(),
+           load_address);
+
+  if (!IsValidAddress(load_address)) {
+    LOG_ERROR("Invalid address 0x%llx", load_address);
+    return false;
+  }
+
+  const size_t size = kAddressSpaceReservationSize;
+  void* wanted_addr = reinterpret_cast<void*>(load_address);
+
+  // Reserve the address space into which we load the library.
+  ScopedAnonymousMmap mapping(wanted_addr, size);
+  void* addr = mapping.GetAddr();
+  if (addr == MAP_FAILED) {
+    LOG_ERROR("Failed to reserve space for load");
+    return false;
+  }
+  if (wanted_addr && addr != wanted_addr) {
+    LOG_ERROR("Failed to obtain fixed address for load");
+    return false;
+  }
+
+  // Build dlextinfo to load the library into the reserved space, using
+  // the shared RELRO if supplied and if its start address matches addr.
+  int relro_fd = -1;
+  int flags = ANDROID_DLEXT_RESERVED_ADDRESS;
+  if (wanted_addr && lib_info_obj) {
+    void* relro_start;
+    s_lib_info_fields.GetRelroInfo(env, lib_info_obj,
+                                   reinterpret_cast<size_t*>(&relro_start),
+                                   nullptr, &relro_fd);
+    if (relro_fd != -1 && relro_start == addr) {
+      flags |= ANDROID_DLEXT_USE_RELRO;
+    }
+  }
+  AndroidDlextinfo dlextinfo(flags, addr, size, relro_fd);
+
+  // Load the library into the reserved space.
+  const char* path = dlopen_library_path.c_str();
+  void* handle = nullptr;
+  if (!AndroidDlopenExt(path, RTLD_NOW, &dlextinfo, &handle)) {
+    LOG_ERROR("No android_dlopen_ext function found");
+    return false;
+  }
+  if (handle == nullptr) {
+    LOG_ERROR("android_dlopen_ext: %s", dlerror());
+    return false;
+  }
+
+  // After loading, trim the mapping to match the library's actual size.
+  size_t load_size = 0;
+  if (!GetLibraryLoadSize(addr, &load_size)) {
+    LOG_ERROR("Unable to find size for load at %p", addr);
+    return false;
+  }
+  if (!ResizeReservedAddressSpace(addr, size, load_size)) {
+    LOG_ERROR("Unable to resize reserved address mapping");
+    return false;
+  }
+
+  // Locate and then call the loaded library's JNI_OnLoad() function. Check
+  // that it returns a usable JNI version.
+  using JNI_OnLoadFunctionPtr = int (*)(void* vm, void* reserved);
+  auto jni_onload =
+      reinterpret_cast<JNI_OnLoadFunctionPtr>(dlsym(handle, "JNI_OnLoad"));
+  if (jni_onload == nullptr) {
+    LOG_ERROR("dlsym: JNI_OnLoad: %s", dlerror());
+    return false;
+  }
+
+  int jni_version = (*jni_onload)(s_java_vm, nullptr);
+  if (jni_version < JNI_VERSION_1_4) {
+    LOG_ERROR("JNI version is invalid: %d", jni_version);
+    return false;
+  }
+
+  // Release mapping before returning so that we do not unmap reserved space.
+  mapping.Release();
+
+  // Note the load address and load size in the supplied libinfo object.
+  const size_t cast_addr = reinterpret_cast<size_t>(addr);
+  s_lib_info_fields.SetLoadInfo(env, lib_info_obj, cast_addr, load_size);
+
+  LOG_INFO("Success loading library %s", dlopen_library_path.c_str());
+  return true;
+}
+
+// Create a shared RELRO file for a library, using android_dlopen_ext().
+//
+// Loads the library similarly to LoadLibrary() above, by reserving address
+// space and then using android_dlopen_ext() to load into the reserved
+// area. Adds flags to android_dlopen_ext() to saved the library's RELRO
+// memory into the given file path, then unload the library and returns.
+//
+// Does not call JNI_OnLoad() or otherwise execute any code from the library.
+//
+// |env| is the current JNI environment handle.
+// |clazz| is the static class handle for org.chromium.base.Linker,
+// and is ignored here.
+// |dlopen_ext_path| is the library identifier (e.g. libfoo.so).
+// |load_address| is an explicit load address.
+// |relro_path| is the path to the file into which RELRO data is written.
+// |lib_info_obj| is a LibInfo handle used to communicate information
+// with the Java side.
+// Return true on success.
+jboolean CreateSharedRelro(JNIEnv* env,
+                           jclass clazz,
+                           jstring dlopen_ext_path,
+                           jlong load_address,
+                           jstring relro_path,
+                           jobject lib_info_obj) {
+  String dlopen_library_path(env, dlopen_ext_path);
+  LOG_INFO("Called for %s, at address 0x%llx", dlopen_library_path.c_str(),
+           load_address);
+
+  if (!IsValidAddress(load_address) || load_address == 0) {
+    LOG_ERROR("Invalid address 0x%llx", load_address);
+    return false;
+  }
+
+  const size_t size = kAddressSpaceReservationSize;
+  void* wanted_addr = reinterpret_cast<void*>(load_address);
+
+  // Reserve the address space into which we load the library.
+  ScopedAnonymousMmap mapping(wanted_addr, size);
+  void* addr = mapping.GetAddr();
+  if (addr == MAP_FAILED) {
+    LOG_ERROR("Failed to reserve space for load");
+    return false;
+  }
+  if (addr != wanted_addr) {
+    LOG_ERROR("Failed to obtain fixed address for load");
+    return false;
+  }
+
+  // Open the shared RELRO file for write. Overwrites any prior content.
+  String shared_relro_path(env, relro_path);
+  const char* filepath = shared_relro_path.c_str();
+  unlink(filepath);
+  int relro_fd = open(filepath, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+  if (relro_fd == -1) {
+    LOG_ERROR("open: %s: %s", filepath, strerror(errno));
+    return false;
+  }
+
+  // Use android_dlopen_ext() to create the shared RELRO.
+  const int flags = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_WRITE_RELRO;
+  AndroidDlextinfo dlextinfo(flags, addr, size, relro_fd);
+
+  const char* path = dlopen_library_path.c_str();
+  void* handle = nullptr;
+  if (!AndroidDlopenExt(path, RTLD_NOW, &dlextinfo, &handle)) {
+    LOG_ERROR("No android_dlopen_ext function found");
+    close(relro_fd);
+    return false;
+  }
+  if (handle == nullptr) {
+    LOG_ERROR("android_dlopen_ext: %s", dlerror());
+    close(relro_fd);
+    return false;
+  }
+
+  // Unload the library from this address. The reserved space is
+  // automatically unmapped on exit from this function.
+  dlclose(handle);
+
+  // Reopen the shared RELFO fd in read-only mode. This ensures that nothing
+  // can write to it through the RELRO fd that we return in libinfo.
+  close(relro_fd);
+  relro_fd = open(filepath, O_RDONLY);
+  if (relro_fd == -1) {
+    LOG_ERROR("open: %s: %s", filepath, strerror(errno));
+    return false;
+  }
+
+  // Delete the directory entry for the RELRO file. The fd we hold ensures
+  // that its data remains intact.
+  if (unlink(filepath) == -1) {
+    LOG_ERROR("unlink: %s: %s", filepath, strerror(errno));
+    return false;
+  }
+
+  // Note the shared RELRO fd in the supplied libinfo object. In this
+  // implementation the RELRO start is set to the library's load address,
+  // and the RELRO size is unused.
+  const size_t cast_addr = reinterpret_cast<size_t>(addr);
+  s_lib_info_fields.SetRelroInfo(env, lib_info_obj, cast_addr, 0, relro_fd);
+
+  LOG_INFO("Success creating shared RELRO %s", shared_relro_path.c_str());
+  return true;
+}
+
+const JNINativeMethod kNativeMethods[] = {
+    {"nativeGetCpuAbi",
+     "("
+     ")"
+     "Ljava/lang/String;",
+     reinterpret_cast<void*>(&GetCpuAbi)},
+    {"nativeLoadLibrary",
+     "("
+     "Ljava/lang/String;"
+     "J"
+     "Lorg/chromium/base/library_loader/Linker$LibInfo;"
+     ")"
+     "Z",
+     reinterpret_cast<void*>(&LoadLibrary)},
+    {"nativeCreateSharedRelro",
+     "("
+     "Ljava/lang/String;"
+     "J"
+     "Ljava/lang/String;"
+     "Lorg/chromium/base/library_loader/Linker$LibInfo;"
+     ")"
+     "Z",
+     reinterpret_cast<void*>(&CreateSharedRelro)},
+};
+
+}  // namespace
+
+bool ModernLinkerJNIInit(JavaVM* vm, JNIEnv* env) {
+  LOG_INFO("Entering");
+
+  // Register native methods.
+  jclass linker_class;
+  if (!InitClassReference(env, "org/chromium/base/library_loader/ModernLinker",
+                          &linker_class))
+    return false;
+
+  LOG_INFO("Registering native methods");
+  env->RegisterNatives(linker_class, kNativeMethods,
+                       sizeof(kNativeMethods) / sizeof(kNativeMethods[0]));
+
+  // Record the Java VM handle.
+  s_java_vm = vm;
+
+  return true;
+}
+
+}  // namespace chromium_android_linker
diff --git a/android/linker/modern_linker_jni.h b/android/linker/modern_linker_jni.h
new file mode 100644
index 0000000..74eb586
--- /dev/null
+++ b/android/linker/modern_linker_jni.h
@@ -0,0 +1,21 @@
+// Copyright 2015 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 BASE_ANDROID_LINKER_MODERN_LINKER_JNI_H_
+#define BASE_ANDROID_LINKER_MODERN_LINKER_JNI_H_
+
+#include <jni.h>
+
+namespace chromium_android_linker {
+
+// JNI_OnLoad() initialization hook for the modern linker.
+// Sets up JNI and other initializations for native linker code.
+// |vm| is the Java VM handle passed to JNI_OnLoad().
+// |env| is the current JNI environment handle.
+// On success, returns true.
+extern bool ModernLinkerJNIInit(JavaVM* vm, JNIEnv* env);
+
+}  // namespace chromium_android_linker
+
+#endif  // BASE_ANDROID_LINKER_MODERN_LINKER_JNI_H_
diff --git a/callback_internal.h b/callback_internal.h
index fefd7a2..b6353dc 100644
--- a/callback_internal.h
+++ b/callback_internal.h
@@ -17,9 +17,6 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/template_util.h"
 
-template <typename T>
-class ScopedVector;
-
 namespace base {
 namespace internal {
 class CallbackBase;
diff --git a/file_descriptor_posix.h b/file_descriptor_posix.h
index 376ad39..2a36611 100644
--- a/file_descriptor_posix.h
+++ b/file_descriptor_posix.h
@@ -14,9 +14,16 @@
 // We introduct a special structure for file descriptors in order that we are
 // able to use template specialisation to special-case their handling.
 //
-// WARNING: (Chromium only) There are subtleties to consider if serialising
-// these objects over IPC. See comments in ipc/ipc_message_utils.h
-// above the template specialisation for this structure.
+// IMPORTANT: This is primarily intended for use when sending file descriptors
+// over IPC. Even if |auto_close| is true, base::FileDescriptor does NOT close()
+// |fd| when going out of scope. Instead, a consumer of a base::FileDescriptor
+// must invoke close() on |fd| if |auto_close| is true.
+//
+// In the case of IPC, the the IPC subsystem knows to close() |fd| after sending
+// a message that contains a base::FileDescriptor if auto_close == true. On the
+// other end, the receiver must make sure to close() |fd| after it has finished
+// processing the IPC message. See the IPC::ParamTraits<> specialization in
+// ipc/ipc_message_utils.h for all the details.
 // -----------------------------------------------------------------------------
 struct FileDescriptor {
   FileDescriptor() : fd(-1), auto_close(false) {}
diff --git a/files/file_path_watcher_linux.cc b/files/file_path_watcher_linux.cc
index 50827ac..6dfc0a6 100644
--- a/files/file_path_watcher_linux.cc
+++ b/files/file_path_watcher_linux.cc
@@ -28,6 +28,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/posix/eintr_wrapper.h"
 #include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
 #include "base/synchronization/lock.h"
 #include "base/thread_task_runner_handle.h"
 #include "base/threading/thread.h"
diff --git a/i18n/case_conversion_unittest.cc b/i18n/case_conversion_unittest.cc
index dc5bc1f..ee795bc 100644
--- a/i18n/case_conversion_unittest.cc
+++ b/i18n/case_conversion_unittest.cc
@@ -5,6 +5,7 @@
 #include "base/i18n/case_conversion.h"
 #include "base/i18n/rtl.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/icu_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/icu/source/i18n/unicode/usearch.h"
 
@@ -58,7 +59,7 @@
   const string16 expected_lower(WideToUTF16(L"\x69\x131"));
   const string16 expected_upper(WideToUTF16(L"\x49\x49"));
 
-  std::string default_locale(uloc_getDefault());
+  test::ScopedRestoreICUDefaultLocale restore_locale;
   i18n::SetICUDefaultLocale("en_US");
 
   string16 result = ToLower(mixed);
@@ -77,8 +78,6 @@
 
   result = ToUpper(mixed);
   EXPECT_EQ(expected_upper_turkish, result);
-
-  SetICUDefaultLocale(default_locale.data());
 }
 
 TEST(CaseConversionTest, FoldCase) {
@@ -97,7 +96,7 @@
   const string16 turkish(WideToUTF16(L"\x49\x131"));
   const string16 turkish_expected(WideToUTF16(L"\x69\x131"));
 
-  std::string default_locale(uloc_getDefault());
+  test::ScopedRestoreICUDefaultLocale restore_locale;
   i18n::SetICUDefaultLocale("en_US");
   EXPECT_EQ(turkish_expected, FoldCase(turkish));
 
diff --git a/i18n/number_formatting_unittest.cc b/i18n/number_formatting_unittest.cc
index 3b0718d..dc6de2b 100644
--- a/i18n/number_formatting_unittest.cc
+++ b/i18n/number_formatting_unittest.cc
@@ -7,7 +7,9 @@
 #include "base/i18n/number_formatting.h"
 #include "base/i18n/rtl.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/icu_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/icu/source/i18n/unicode/usearch.h"
 
 namespace base {
 namespace {
@@ -27,6 +29,8 @@
     {-42, "-42", "-42"},
   };
 
+  test::ScopedRestoreICUDefaultLocale restore_locale;
+
   for (size_t i = 0; i < arraysize(cases); ++i) {
     i18n::SetICUDefaultLocale("en");
     testing::ResetFormatters();
@@ -72,6 +76,7 @@
     {-42.7, 3, "-42.700", "-42,700"},
   };
 
+  test::ScopedRestoreICUDefaultLocale restore_locale;
   for (size_t i = 0; i < arraysize(cases); ++i) {
     i18n::SetICUDefaultLocale("en");
     testing::ResetFormatters();
diff --git a/i18n/time_formatting_unittest.cc b/i18n/time_formatting_unittest.cc
index 9385628..7b7d4d5 100644
--- a/i18n/time_formatting_unittest.cc
+++ b/i18n/time_formatting_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/i18n/rtl.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/icu_test_util.h"
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/icu/source/common/unicode/uversion.h"
@@ -41,6 +42,7 @@
 TEST(TimeFormattingTest, TimeFormatTimeOfDayDefault12h) {
   // Test for a locale defaulted to 12h clock.
   // As an instance, we use third_party/icu/source/data/locales/en.txt.
+  test::ScopedRestoreICUDefaultLocale restore_locale;
   i18n::SetICUDefaultLocale("en_US");
 
   Time time(Time::FromLocalExploded(kTestDateTimeExploded));
@@ -76,6 +78,7 @@
 TEST(TimeFormattingTest, TimeFormatTimeOfDayDefault24h) {
   // Test for a locale defaulted to 24h clock.
   // As an instance, we use third_party/icu/source/data/locales/en_GB.txt.
+  test::ScopedRestoreICUDefaultLocale restore_locale;
   i18n::SetICUDefaultLocale("en_GB");
 
   Time time(Time::FromLocalExploded(kTestDateTimeExploded));
@@ -111,6 +114,7 @@
 TEST(TimeFormattingTest, TimeFormatTimeOfDayJP) {
   // Test for a locale that uses different mark than "AM" and "PM".
   // As an instance, we use third_party/icu/source/data/locales/ja.txt.
+  test::ScopedRestoreICUDefaultLocale restore_locale;
   i18n::SetICUDefaultLocale("ja_JP");
 
   Time time(Time::FromLocalExploded(kTestDateTimeExploded));
@@ -144,6 +148,7 @@
 TEST(TimeFormattingTest, TimeFormatDateUS) {
   // See third_party/icu/source/data/locales/en.txt.
   // The date patterns are "EEEE, MMMM d, y", "MMM d, y", and "M/d/yy".
+  test::ScopedRestoreICUDefaultLocale restore_locale;
   i18n::SetICUDefaultLocale("en_US");
 
   Time time(Time::FromLocalExploded(kTestDateTimeExploded));
@@ -166,6 +171,7 @@
 TEST(TimeFormattingTest, TimeFormatDateGB) {
   // See third_party/icu/source/data/locales/en_GB.txt.
   // The date patterns are "EEEE, d MMMM y", "d MMM y", and "dd/MM/yyyy".
+  test::ScopedRestoreICUDefaultLocale restore_locale;
   i18n::SetICUDefaultLocale("en_GB");
 
   Time time(Time::FromLocalExploded(kTestDateTimeExploded));
diff --git a/memory/BUILD.gn b/memory/BUILD.gn
index 1bbbc7a..4250631 100644
--- a/memory/BUILD.gn
+++ b/memory/BUILD.gn
@@ -49,6 +49,8 @@
   if (is_ios) {
     sources -= [
       "discardable_shared_memory.cc",
+      "discardable_shared_memory.h",
+      "shared_memory.h",
       "shared_memory_posix.cc",
     ]
   }
diff --git a/message_loop/message_loop.cc b/message_loop/message_loop.cc
index 51db741..f350566 100644
--- a/message_loop/message_loop.cc
+++ b/message_loop/message_loop.cc
@@ -475,7 +475,7 @@
 
   HistogramEvent(kTaskRunEvent);
 
-  TRACE_TASK_EXECUTION("toplevel", pending_task);
+  TRACE_TASK_EXECUTION("MessageLoop::RunTask", pending_task);
 
   FOR_EACH_OBSERVER(TaskObserver, task_observers_,
                     WillProcessTask(pending_task));
diff --git a/prefs/json_pref_store.h b/prefs/json_pref_store.h
index 0be7702..d9dcdbd 100644
--- a/prefs/json_pref_store.h
+++ b/prefs/json_pref_store.h
@@ -15,7 +15,6 @@
 #include "base/files/important_file_writer.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/prefs/base_prefs_export.h"
diff --git a/process/launch_win.cc b/process/launch_win.cc
index 9515041..09a4536 100644
--- a/process/launch_win.cc
+++ b/process/launch_win.cc
@@ -175,6 +175,8 @@
       // It should be ERROR_INVALID_HANDLE at this point, which means the
       // browser was likely not started from a console.
       AllocConsole();
+    } else {
+      return;
     }
   }
 
diff --git a/process/process_util_unittest.cc b/process/process_util_unittest.cc
index cf83aa7..08144f2 100644
--- a/process/process_util_unittest.cc
+++ b/process/process_util_unittest.cc
@@ -59,6 +59,9 @@
 #include <malloc/malloc.h>
 #include "base/mac/mac_util.h"
 #endif
+#if defined(OS_ANDROID)
+#include "third_party/lss/linux_syscall_support.h"
+#endif
 
 using base::FilePath;
 
@@ -226,7 +229,19 @@
 
 MULTIPROCESS_TEST_MAIN(CrashingChildProcess) {
   WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileCrash).c_str());
-#if defined(OS_POSIX)
+#if defined(OS_ANDROID)
+  // Android L+ expose signal and sigaction symbols that override the system
+  // ones. There is a bug in these functions where a request to set the handler
+  // to SIG_DFL is ignored. In that case, an infinite loop is entered as the
+  // signal is repeatedly sent to the crash dump signal handler.
+  // To work around this, directly call the system's sigaction.
+  struct kernel_sigaction sa;
+  memset(&sa, 0, sizeof(sa));
+  sys_sigemptyset(&sa.sa_mask);
+  sa.sa_handler_ = SIG_DFL;
+  sa.sa_flags = SA_RESTART;
+  sys_rt_sigaction(SIGSEGV, &sa, NULL, sizeof(kernel_sigset_t));
+#elif defined(OS_POSIX)
   // Have to disable to signal handler for segv so we can get a crash
   // instead of an abnormal termination through the crash dump handler.
   ::signal(SIGSEGV, SIG_DFL);
diff --git a/profiler/native_stack_sampler_win.cc b/profiler/native_stack_sampler_win.cc
index 0718904..6cc6515 100644
--- a/profiler/native_stack_sampler_win.cc
+++ b/profiler/native_stack_sampler_win.cc
@@ -10,6 +10,7 @@
 
 #include "base/logging.h"
 #include "base/profiler/native_stack_sampler.h"
+#include "base/profiler/win32_stack_frame_unwinder.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
@@ -19,6 +20,8 @@
 
 namespace base {
 
+// Stack recording functions --------------------------------------------------
+
 namespace {
 
 // Walks the stack represented by |context| from the current frame downwards,
@@ -26,41 +29,13 @@
 int RecordStack(CONTEXT* context,
                 int max_stack_size,
                 const void* instruction_pointers[],
-                bool* last_frame_is_unknown_function) {
+                Win32StackFrameUnwinder* frame_unwinder) {
 #ifdef _WIN64
-  *last_frame_is_unknown_function = false;
-
   int i = 0;
   for (; (i < max_stack_size) && context->Rip; ++i) {
-    // Try to look up unwind metadata for the current function.
-    ULONG64 image_base;
-    PRUNTIME_FUNCTION runtime_function =
-        RtlLookupFunctionEntry(context->Rip, &image_base, nullptr);
-
     instruction_pointers[i] = reinterpret_cast<const void*>(context->Rip);
-
-    if (runtime_function) {
-      KNONVOLATILE_CONTEXT_POINTERS nvcontext = {};
-      void* handler_data;
-      ULONG64 establisher_frame;
-      RtlVirtualUnwind(0, image_base, context->Rip, runtime_function, context,
-                       &handler_data, &establisher_frame, &nvcontext);
-    } else {
-      // If we don't have a RUNTIME_FUNCTION, then in theory this should be a
-      // leaf function whose frame contains only a return address, at
-      // RSP. However, crash data also indicates that some third party libraries
-      // do not provide RUNTIME_FUNCTION information for non-leaf functions. We
-      // could manually unwind the stack in the former case, but attempting to
-      // do so in the latter case would produce wrong results and likely crash,
-      // so just bail out.
-      //
-      // Ad hoc runs with instrumentation show that ~5% of stack traces end with
-      // a valid leaf function. To avoid selectively omitting these traces it
-      // makes sense to ultimately try to distinguish these two cases and
-      // selectively unwind the stack for legitimate leaf functions. For the
-      // purposes of avoiding crashes though, just ignore them all for now.
-      return i;
-    }
+    if (!frame_unwinder->TryUnwind(context))
+      return i + 1;
   }
   return i;
 #else
@@ -75,11 +50,8 @@
 // arrays.
 void FindModuleHandlesForAddresses(const void* const addresses[],
                                    HMODULE module_handles[],
-                                   int stack_depth,
-                                   bool last_frame_is_unknown_function) {
-  const int module_frames =
-      last_frame_is_unknown_function ? stack_depth - 1 : stack_depth;
-  for (int i = 0; i < module_frames; ++i) {
+                                   int stack_depth) {
+  for (int i = 0; i < stack_depth; ++i) {
     HMODULE module_handle = NULL;
     if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
                           reinterpret_cast<LPCTSTR>(addresses[i]),
@@ -129,6 +101,8 @@
   return WideToUTF8(build_id);
 }
 
+// ScopedDisablePriorityBoost -------------------------------------------------
+
 // Disables priority boost on a thread for the lifetime of the object.
 class ScopedDisablePriorityBoost {
  public:
@@ -171,8 +145,9 @@
 // vectors make it too easy to subtly allocate memory.
 int SuspendThreadAndRecordStack(HANDLE thread_handle,
                                 int max_stack_size,
-                                const void* instruction_pointers[],
-                                bool* last_frame_is_unknown_function) {
+                                const void* instruction_pointers[]) {
+  Win32StackFrameUnwinder frame_unwinder;
+
   if (::SuspendThread(thread_handle) == -1)
     return 0;
 
@@ -180,9 +155,8 @@
   CONTEXT thread_context = {0};
   thread_context.ContextFlags = CONTEXT_FULL;
   if (::GetThreadContext(thread_handle, &thread_context)) {
-    stack_depth =
-        RecordStack(&thread_context, max_stack_size, instruction_pointers,
-                    last_frame_is_unknown_function);
+    stack_depth = RecordStack(&thread_context, max_stack_size,
+                              instruction_pointers, &frame_unwinder);
   }
 
   // Disable the priority boost that the thread would otherwise receive on
@@ -202,6 +176,8 @@
   return stack_depth;
 }
 
+// NativeStackSamplerWin ------------------------------------------------------
+
 class NativeStackSamplerWin : public NativeStackSampler {
  public:
   explicit NativeStackSamplerWin(win::ScopedHandle thread_handle);
@@ -264,12 +240,10 @@
   const void* instruction_pointers[max_stack_size] = {0};
   HMODULE module_handles[max_stack_size] = {0};
 
-  bool last_frame_is_unknown_function = false;
   int stack_depth = SuspendThreadAndRecordStack(
-      thread_handle_.Get(), max_stack_size, instruction_pointers,
-      &last_frame_is_unknown_function);
+      thread_handle_.Get(), max_stack_size, instruction_pointers);
   FindModuleHandlesForAddresses(instruction_pointers, module_handles,
-                                stack_depth, last_frame_is_unknown_function);
+                                stack_depth);
   CopyToSample(instruction_pointers, module_handles, stack_depth, sample,
                current_modules_);
   FreeModuleHandles(stack_depth, module_handles);
diff --git a/profiler/win32_stack_frame_unwinder.cc b/profiler/win32_stack_frame_unwinder.cc
new file mode 100644
index 0000000..c3809a6
--- /dev/null
+++ b/profiler/win32_stack_frame_unwinder.cc
@@ -0,0 +1,210 @@
+// Copyright 2015 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 "base/profiler/win32_stack_frame_unwinder.h"
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/singleton.h"
+#include "base/stl_util.h"
+
+namespace base {
+
+// LeafUnwindBlacklist --------------------------------------------------------
+
+namespace {
+
+// Records modules that are known to have functions that violate the Microsoft
+// x64 calling convention and would be dangerous to manually unwind if
+// encountered as the last frame on the call stack. Functions like these have
+// been observed in injected third party modules that either do not provide
+// function unwind information, or do not provide the required function prologue
+// and epilogue. The former case was observed in several AV products and the
+// latter in a WndProc function associated with Actual Window
+// Manager/aimemb64.dll. See https://crbug.com/476422.
+class LeafUnwindBlacklist {
+ public:
+  static LeafUnwindBlacklist* GetInstance();
+
+  // This function does not allocate memory and is safe to call between
+  // SuspendThread and ResumeThread.
+  bool IsBlacklisted(const void* module) const;
+
+  // Allocates memory. Must be invoked only after ResumeThread, otherwise we
+  // risk deadlocking on a heap lock held by a suspended thread.
+  void AddModuleToBlacklist(const void* module);
+
+ private:
+  friend struct DefaultSingletonTraits<LeafUnwindBlacklist>;
+
+  LeafUnwindBlacklist();
+  ~LeafUnwindBlacklist();
+
+  // The set of modules known to have functions that violate the Microsoft x64
+  // calling convention.
+  base::hash_set<const void*> blacklisted_modules_;
+
+  DISALLOW_COPY_AND_ASSIGN(LeafUnwindBlacklist);
+};
+
+// static
+LeafUnwindBlacklist* LeafUnwindBlacklist::GetInstance() {
+  // Leaky for shutdown performance.
+  return Singleton<LeafUnwindBlacklist,
+                   LeakySingletonTraits<LeafUnwindBlacklist>>::get();
+}
+
+bool LeafUnwindBlacklist::IsBlacklisted(const void* module) const {
+  return ContainsKey(blacklisted_modules_, module);
+}
+
+void LeafUnwindBlacklist::AddModuleToBlacklist(const void* module) {
+  CHECK(module);
+  blacklisted_modules_.insert(module);
+}
+
+LeafUnwindBlacklist::LeafUnwindBlacklist() {}
+LeafUnwindBlacklist::~LeafUnwindBlacklist() {}
+
+}  // namespace
+
+// Win32StackFrameUnwinder ----------------------------------------------------
+
+Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {}
+Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {}
+
+Win32StackFrameUnwinder::Win32UnwindFunctions::Win32UnwindFunctions() {}
+
+PRUNTIME_FUNCTION
+Win32StackFrameUnwinder::Win32UnwindFunctions::LookupFunctionEntry(
+    DWORD64 program_counter,
+    PDWORD64 image_base) {
+#ifdef _WIN64
+  return RtlLookupFunctionEntry(program_counter, image_base, nullptr);
+#else
+  NOTREACHED();
+  return nullptr;
+#endif
+}
+
+void Win32StackFrameUnwinder::Win32UnwindFunctions::VirtualUnwind(
+    DWORD64 image_base,
+    DWORD64 program_counter,
+    PRUNTIME_FUNCTION runtime_function,
+    CONTEXT* context) {
+#ifdef _WIN64
+  void* handler_data;
+  ULONG64 establisher_frame;
+  KNONVOLATILE_CONTEXT_POINTERS nvcontext = {};
+  RtlVirtualUnwind(0, image_base, program_counter, runtime_function, context,
+                   &handler_data, &establisher_frame, &nvcontext);
+#else
+  NOTREACHED();
+#endif
+}
+
+Win32StackFrameUnwinder::Win32StackFrameUnwinder()
+    : Win32StackFrameUnwinder(&win32_unwind_functions_) {}
+
+Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {
+  if (pending_blacklisted_module_) {
+    LeafUnwindBlacklist::GetInstance()->AddModuleToBlacklist(
+        pending_blacklisted_module_);
+  }
+}
+
+bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context) {
+#ifdef _WIN64
+  CHECK(!at_top_frame_ || unwind_info_present_for_all_frames_);
+  CHECK(!pending_blacklisted_module_);
+
+  ULONG64 image_base;
+  // Try to look up unwind metadata for the current function.
+  PRUNTIME_FUNCTION runtime_function =
+      unwind_functions_->LookupFunctionEntry(context->Rip, &image_base);
+
+  if (runtime_function) {
+    unwind_functions_->VirtualUnwind(image_base, context->Rip, runtime_function,
+                                     context);
+    at_top_frame_ = false;
+  } else {
+    // RtlLookupFunctionEntry didn't find unwind information. This could mean
+    // the code at the instruction pointer is in:
+    //
+    // 1. a true leaf function (i.e. a function that neither calls a function,
+    //    nor allocates any stack space itself) in which case the return
+    //    address is at RSP, or
+    //
+    // 2. a function that doesn't adhere to the Microsoft x64 calling
+    //    convention, either by not providing the required unwind information,
+    //    or by not having the prologue or epilogue required for unwinding;
+    //    this case has been observed in crash data in injected third party
+    //    DLLs.
+    //
+    // In valid code, case 1 can only occur (by definition) as the last frame
+    // on the stack. This happens in about 5% of observed stacks and can
+    // easily be unwound by popping RSP and using it as the next frame's
+    // instruction pointer.
+    //
+    // Case 2 can occur anywhere on the stack, and attempting to unwind the
+    // stack will result in treating whatever value happens to be on the stack
+    // at RSP as the next frame's instruction pointer. This is certainly wrong
+    // and very likely to lead to crashing by deferencing invalid pointers in
+    // the next RtlVirtualUnwind call.
+    //
+    // If we see case 2 at a location not the last frame, and all the previous
+    // frame had valid unwind information, then this is definitely bad code.
+    // We blacklist the module as untrustable for unwinding if we encounter a
+    // function in it that doesn't have unwind information.
+
+    if (at_top_frame_) {
+      at_top_frame_ = false;
+
+      // We are at the end of the stack. It's very likely that we're in case 1
+      // since the vast majority of code adheres to the Microsoft x64 calling
+      // convention. But there's a small chance we might be unlucky and be in
+      // case 2. If this module is known to have bad code according to the
+      // leaf unwind blacklist, stop here, otherwise manually unwind.
+      if (LeafUnwindBlacklist::GetInstance()->IsBlacklisted(
+              reinterpret_cast<const void*>(image_base))) {
+        return false;
+      }
+
+      context->Rip = context->Rsp;
+      context->Rsp += 8;
+      unwind_info_present_for_all_frames_ = false;
+    } else {
+      // We're not at the end of the stack. This frame is untrustworthy and we
+      // can't safely unwind from here.
+      if (unwind_info_present_for_all_frames_) {
+        // Unwind information was present for all previous frames, so we can
+        // be confident this is case 2. Record the module to be blacklisted.
+        pending_blacklisted_module_ = reinterpret_cast<const void*>(image_base);
+      } else {
+        // We started off on a function without unwind information. It's very
+        // likely that all frames up to this point have been good, and this
+        // frame is case 2. But it's possible that the initial frame was case
+        // 2 but hadn't been blacklisted yet, and we've started to go off into
+        // the weeds. Since we can't be sure, just bail out without
+        // blacklisting the module; chances are we'll later encounter the same
+        // function on a stack with full unwind information.
+      }
+      return false;
+    }
+  }
+
+  return true;
+#else
+  NOTREACHED();
+  return false;
+#endif
+}
+
+Win32StackFrameUnwinder::Win32StackFrameUnwinder(
+    UnwindFunctions* unwind_functions)
+    : at_top_frame_(true),
+      unwind_info_present_for_all_frames_(true),
+      pending_blacklisted_module_(nullptr),
+      unwind_functions_(unwind_functions) {}
+
+}  // namespace base
diff --git a/profiler/win32_stack_frame_unwinder.h b/profiler/win32_stack_frame_unwinder.h
new file mode 100644
index 0000000..2d80d63
--- /dev/null
+++ b/profiler/win32_stack_frame_unwinder.h
@@ -0,0 +1,84 @@
+// Copyright 2015 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 BASE_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_
+#define BASE_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_
+
+#include <windows.h>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+
+namespace base {
+
+#if !defined(_WIN64)
+// Allows code to compile for x86. Actual support for x86 will require either
+// refactoring these interfaces or separate architecture-specific interfaces.
+using PRUNTIME_FUNCTION = void*;
+#endif  // !defined(_WIN64)
+
+// Instances of this class are expected to be created and destroyed for each
+// stack unwinding, outside of SuspendThread/ResumeThread.
+class BASE_EXPORT Win32StackFrameUnwinder {
+ public:
+  // Interface for Win32 unwind-related functionality this class depends
+  // on. Provides a seam for testing.
+  class BASE_EXPORT UnwindFunctions {
+   public:
+    virtual ~UnwindFunctions();
+
+    virtual PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
+                                                  PDWORD64 image_base) = 0;
+    virtual void VirtualUnwind(DWORD64 image_base,
+                               DWORD64 program_counter,
+                               PRUNTIME_FUNCTION runtime_function,
+                               CONTEXT* context) = 0;
+
+   protected:
+    UnwindFunctions();
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(UnwindFunctions);
+  };
+
+  class BASE_EXPORT Win32UnwindFunctions : public UnwindFunctions {
+   public:
+    Win32UnwindFunctions();
+
+    PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
+                                          PDWORD64 image_base) override;
+
+    void VirtualUnwind(DWORD64 image_base,
+                       DWORD64 program_counter,
+                       PRUNTIME_FUNCTION runtime_function,
+                       CONTEXT* context) override;
+
+   private:
+    DISALLOW_COPY_AND_ASSIGN(Win32UnwindFunctions);
+  };
+
+  Win32StackFrameUnwinder();
+  ~Win32StackFrameUnwinder();
+
+  bool TryUnwind(CONTEXT* context);
+
+ private:
+  // This function is for test purposes only.
+  Win32StackFrameUnwinder(UnwindFunctions* unwind_functions);
+  friend class Win32StackFrameUnwinderTest;
+
+  // State associated with each stack unwinding.
+  bool at_top_frame_;
+  bool unwind_info_present_for_all_frames_;
+  const void* pending_blacklisted_module_;
+
+  Win32UnwindFunctions win32_unwind_functions_;
+  UnwindFunctions* const unwind_functions_;
+
+  DISALLOW_COPY_AND_ASSIGN(Win32StackFrameUnwinder);
+};
+
+}  // namespace base
+
+#endif  // BASE_PROFILER_WIN32_STACK_FRAME_UNWINDER_H_
diff --git a/profiler/win32_stack_frame_unwinder_unittest.cc b/profiler/win32_stack_frame_unwinder_unittest.cc
new file mode 100644
index 0000000..0ea55c3
--- /dev/null
+++ b/profiler/win32_stack_frame_unwinder_unittest.cc
@@ -0,0 +1,230 @@
+// Copyright 2015 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 "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/profiler/win32_stack_frame_unwinder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class TestUnwindFunctions : public Win32StackFrameUnwinder::UnwindFunctions {
+ public:
+  TestUnwindFunctions();
+
+  PRUNTIME_FUNCTION LookupFunctionEntry(DWORD64 program_counter,
+                                        PDWORD64 image_base) override;
+  void VirtualUnwind(DWORD64 image_base,
+                     DWORD64 program_counter,
+                     PRUNTIME_FUNCTION runtime_function,
+                     CONTEXT* context) override;
+
+  void SetNoUnwindInfoForNextFrame();
+  void SetImageBaseForNextFrame(DWORD64 image_base);
+
+ private:
+  enum { kRuntimeFunctionPointerIncrement = 1, kImageBaseIncrement = 1 << 20 };
+
+  static const PRUNTIME_FUNCTION kNonNullRuntimeFunctionPointer;
+
+  DWORD64 supplied_program_counter_;
+  DWORD64 custom_image_base_;
+  DWORD64 next_image_base_;
+  bool next_lookup_returns_null_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestUnwindFunctions);
+};
+
+// This value is opaque to Win32StackFrameUnwinder.
+const PRUNTIME_FUNCTION TestUnwindFunctions::kNonNullRuntimeFunctionPointer =
+    reinterpret_cast<PRUNTIME_FUNCTION>(8);
+
+TestUnwindFunctions::TestUnwindFunctions()
+    : supplied_program_counter_(0),
+      custom_image_base_(0),
+      next_image_base_(kImageBaseIncrement),
+      next_lookup_returns_null_(false) {}
+
+PRUNTIME_FUNCTION TestUnwindFunctions::LookupFunctionEntry(
+    DWORD64 program_counter,
+    PDWORD64 image_base) {
+  supplied_program_counter_ = program_counter;
+  if (custom_image_base_) {
+    *image_base = custom_image_base_;
+    custom_image_base_ = 0;
+  } else {
+    *image_base = next_image_base_;
+    next_image_base_ += kImageBaseIncrement;
+  }
+  if (next_lookup_returns_null_) {
+    next_lookup_returns_null_ = false;
+    return nullptr;
+  }
+
+  return kNonNullRuntimeFunctionPointer;
+}
+
+void TestUnwindFunctions::VirtualUnwind(DWORD64 image_base,
+                                        DWORD64 program_counter,
+                                        PRUNTIME_FUNCTION runtime_function,
+                                        CONTEXT* context) {
+  EXPECT_EQ(next_image_base_ - kImageBaseIncrement, image_base);
+  EXPECT_EQ(supplied_program_counter_, program_counter);
+  // This function should only be called when LookupFunctionEntry returns a
+  // non-null value.
+  EXPECT_EQ(kNonNullRuntimeFunctionPointer, runtime_function);
+}
+
+void TestUnwindFunctions::SetNoUnwindInfoForNextFrame() {
+  next_lookup_returns_null_ = true;
+}
+
+void TestUnwindFunctions::SetImageBaseForNextFrame(DWORD64 image_base) {
+  next_image_base_ = image_base;
+}
+
+}  // namespace
+
+class Win32StackFrameUnwinderTest : public testing::Test {
+ protected:
+  Win32StackFrameUnwinderTest() {}
+
+  // This exists so that Win32StackFrameUnwinder's constructor can be private
+  // with a single friend declaration of this test fixture.
+  scoped_ptr<Win32StackFrameUnwinder> CreateUnwinder();
+
+  TestUnwindFunctions unwind_functions_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Win32StackFrameUnwinderTest);
+};
+
+scoped_ptr<Win32StackFrameUnwinder>
+Win32StackFrameUnwinderTest::CreateUnwinder() {
+  return make_scoped_ptr(new Win32StackFrameUnwinder(&unwind_functions_));
+}
+
+// Checks the case where all frames have unwind information.
+TEST_F(Win32StackFrameUnwinderTest, FramesWithUnwindInfo) {
+  scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+  CONTEXT context = {0};
+  EXPECT_TRUE(unwinder->TryUnwind(&context));
+  EXPECT_TRUE(unwinder->TryUnwind(&context));
+  EXPECT_TRUE(unwinder->TryUnwind(&context));
+}
+
+// Checks that the CONTEXT's stack pointer gets popped when the top frame has no
+// unwind information.
+TEST_F(Win32StackFrameUnwinderTest, FrameAtTopWithoutUnwindInfo) {
+  scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+  CONTEXT context = {0};
+  const DWORD64 original_rsp = 128;
+  context.Rsp = original_rsp;
+  unwind_functions_.SetNoUnwindInfoForNextFrame();
+  EXPECT_TRUE(unwinder->TryUnwind(&context));
+  EXPECT_EQ(original_rsp, context.Rip);
+  EXPECT_EQ(original_rsp + 8, context.Rsp);
+
+  EXPECT_TRUE(unwinder->TryUnwind(&context));
+  EXPECT_TRUE(unwinder->TryUnwind(&context));
+}
+
+// Checks that a frame below the top of the stack with missing unwind info
+// results in blacklisting the module.
+TEST_F(Win32StackFrameUnwinderTest, BlacklistedModule) {
+  const DWORD64 image_base_for_module_with_bad_function = 1024;
+  {
+    // First stack, with a bad function below the top of the stack.
+    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+    CONTEXT context = {0};
+    EXPECT_TRUE(unwinder->TryUnwind(&context));
+
+    unwind_functions_.SetNoUnwindInfoForNextFrame();
+    unwind_functions_.SetImageBaseForNextFrame(
+        image_base_for_module_with_bad_function);
+    EXPECT_FALSE(unwinder->TryUnwind(&context));
+  }
+
+  {
+    // Second stack; check that a function at the top of the stack without
+    // unwind info from the previously-seen module is blacklisted.
+    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+    CONTEXT context = {0};
+    unwind_functions_.SetNoUnwindInfoForNextFrame();
+    unwind_functions_.SetImageBaseForNextFrame(
+        image_base_for_module_with_bad_function);
+    EXPECT_FALSE(unwinder->TryUnwind(&context));
+  }
+
+  {
+    // Third stack; check that a function at the top of the stack *with* unwind
+    // info from the previously-seen module is not blacklisted. Then check that
+    // functions below the top of the stack with unwind info are not
+    // blacklisted, regardless of whether they are in the previously-seen
+    // module.
+    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+    CONTEXT context = {0};
+    unwind_functions_.SetImageBaseForNextFrame(
+        image_base_for_module_with_bad_function);
+    EXPECT_TRUE(unwinder->TryUnwind(&context));
+
+    EXPECT_TRUE(unwinder->TryUnwind(&context));
+
+    unwind_functions_.SetImageBaseForNextFrame(
+        image_base_for_module_with_bad_function);
+    EXPECT_TRUE(unwinder->TryUnwind(&context));
+  }
+
+  {
+    // Fourth stack; check that a function at the top of the stack without
+    // unwind info and not from the previously-seen module is not
+    // blacklisted. Then check that functions below the top of the stack with
+    // unwind info are not blacklisted, regardless of whether they are in the
+    // previously-seen module.
+    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+    CONTEXT context = {0};
+    unwind_functions_.SetNoUnwindInfoForNextFrame();
+    EXPECT_TRUE(unwinder->TryUnwind(&context));
+
+    EXPECT_TRUE(unwinder->TryUnwind(&context));
+
+    unwind_functions_.SetImageBaseForNextFrame(
+        image_base_for_module_with_bad_function);
+    EXPECT_TRUE(unwinder->TryUnwind(&context));
+  }
+}
+
+// Checks that a frame below the top of the stack with missing unwind info does
+// not result in blacklisting the module if the first frame also was missing
+// unwind info. This ensures we don't blacklist an innocent module because the
+// first frame was bad but we didn't know it at the time.
+TEST_F(Win32StackFrameUnwinderTest, ModuleFromQuestionableFrameNotBlacklisted) {
+  const DWORD64 image_base_for_questionable_module = 2048;
+  {
+    // First stack, with both the first and second frames missing unwind info.
+    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+    CONTEXT context = {0};
+    unwind_functions_.SetNoUnwindInfoForNextFrame();
+    EXPECT_TRUE(unwinder->TryUnwind(&context));
+
+    unwind_functions_.SetNoUnwindInfoForNextFrame();
+    unwind_functions_.SetImageBaseForNextFrame(
+        image_base_for_questionable_module);
+    EXPECT_FALSE(unwinder->TryUnwind(&context));
+  }
+
+  {
+    // Second stack; check that the questionable module was not blacklisted.
+    scoped_ptr<Win32StackFrameUnwinder> unwinder = CreateUnwinder();
+    CONTEXT context = {0};
+    unwind_functions_.SetNoUnwindInfoForNextFrame();
+    unwind_functions_.SetImageBaseForNextFrame(
+        image_base_for_questionable_module);
+    EXPECT_TRUE(unwinder->TryUnwind(&context));
+  }
+}
+
+}  // namespace base
diff --git a/strings/string_util.h b/strings/string_util.h
index 2cea8c4..fccf726 100644
--- a/strings/string_util.h
+++ b/strings/string_util.h
@@ -46,19 +46,6 @@
   return result;
 }
 
-// TODO(mark) http://crbug.com/472900 crashpad shouldn't use base while
-// being DEPSed in. This backwards-compat hack is provided until crashpad is
-// updated.
-#if defined(OS_WIN)
-inline int strcasecmp(const char* s1, const char* s2) {
-  return _stricmp(s1, s2);
-}
-#else  // Posix
-inline int strcasecmp(const char* string1, const char* string2) {
-  return ::strcasecmp(string1, string2);
-}
-#endif
-
 // BSD-style safe and consistent string copy functions.
 // Copies |src| to |dst|, where |dst_size| is the total allocated size of |dst|.
 // Copies at most |dst_size|-1 characters, and always NULL terminates |dst|, as
diff --git a/test/BUILD.gn b/test/BUILD.gn
index c2a1f33..bd7a21c 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -37,7 +37,6 @@
     "histogram_tester.h",
     "ios/wait_util.h",
     "ios/wait_util.mm",
-    "launcher/test_launcher.h",
     "launcher/test_result.cc",
     "launcher/test_result.h",
     "launcher/test_results_tracker.h",
@@ -125,6 +124,7 @@
   } else {
     sources += [
       "launcher/test_launcher.cc",
+      "launcher/test_launcher.h",
       "launcher/test_results_tracker.cc",
       "launcher/unit_test_launcher.cc",
       "multiprocess_test.cc",
diff --git a/test/android/javatests/src/org/chromium/base/test/BaseActivityInstrumentationTestCase.java b/test/android/javatests/src/org/chromium/base/test/BaseActivityInstrumentationTestCase.java
index 9ce432b..b0c0d77 100644
--- a/test/android/javatests/src/org/chromium/base/test/BaseActivityInstrumentationTestCase.java
+++ b/test/android/javatests/src/org/chromium/base/test/BaseActivityInstrumentationTestCase.java
@@ -18,13 +18,12 @@
 /**
  * Base class for all Activity-based Instrumentation tests.
  *
- * This class currently does nothing.
- *
  * @param <T> The Activity type.
  */
 public class BaseActivityInstrumentationTestCase<T extends Activity>
         extends ActivityInstrumentationTestCase2<T> implements Parameterizable {
     private Parameter.Reader mParameterReader;
+    private Map<String, BaseParameter> mAvailableParameters;
 
     /**
      * Creates a instance for running tests against an Activity of the given class.
@@ -36,14 +35,35 @@
     }
 
     /**
-     * Creates the list of available parameters that inherited classes can use.
+     * Creates the {@link Map} of available parameters for the test to use.
      *
-     * @return a list of {@link BaseParameter} objects to set as the available parameters.
+     * @return a {@link Map} of {@link BaseParameter} objects.
+     */
+    protected Map<String, BaseParameter> createAvailableParameters() {
+        Map<String, BaseParameter> availableParameters = new HashMap<>();
+        availableParameters.put(
+                MethodParameter.PARAMETER_TAG, new MethodParameter(getParameterReader()));
+        return availableParameters;
+    }
+
+    /**
+     * Gets the {@link Map} of available parameters that inherited classes can use.
+     *
+     * @return a {@link Map} of {@link BaseParameter} objects to set as the available parameters.
      */
     public Map<String, BaseParameter> getAvailableParameters() {
-        Map<String, BaseParameter> parameters = new HashMap<>();
-        parameters.put(MethodParameter.PARAMETER_TAG, new MethodParameter(getParameterReader()));
-        return parameters;
+        return mAvailableParameters;
+    }
+
+    /**
+     * Gets a specific parameter from the current test.
+     *
+     * @param parameterTag a string with the name of the {@link BaseParameter} we want.
+     * @return a parameter that extends {@link BaseParameter} that has the matching parameterTag.
+     */
+    @SuppressWarnings("unchecked")
+    public <T extends BaseParameter> T getAvailableParameter(String parameterTag) {
+        return (T) mAvailableParameters.get(parameterTag);
     }
 
     /**
@@ -53,6 +73,7 @@
      */
     public void setParameterReader(Parameter.Reader parameterReader) {
         mParameterReader = parameterReader;
+        mAvailableParameters = createAvailableParameters();
     }
 
     /**
diff --git a/test/android/javatests/src/org/chromium/base/test/util/TestThread.java b/test/android/javatests/src/org/chromium/base/test/util/TestThread.java
index 93c23f7..4f62969 100644
--- a/test/android/javatests/src/org/chromium/base/test/util/TestThread.java
+++ b/test/android/javatests/src/org/chromium/base/test/util/TestThread.java
@@ -43,7 +43,7 @@
  */
 
 public class TestThread extends Thread {
-    private Object mThreadReadyLock;
+    private final Object mThreadReadyLock;
     private AtomicBoolean mThreadReady;
     private Handler mMainThreadHandler;
     private Handler mTestThreadHandler;
diff --git a/test/android/javatests/src/org/chromium/base/test/util/parameter/Parameterizable.java b/test/android/javatests/src/org/chromium/base/test/util/parameter/Parameterizable.java
index b271430..f0eb97d 100644
--- a/test/android/javatests/src/org/chromium/base/test/util/parameter/Parameterizable.java
+++ b/test/android/javatests/src/org/chromium/base/test/util/parameter/Parameterizable.java
@@ -10,6 +10,25 @@
  * An interface to implement on test cases to run {@link ParameterizedTest}s.
  */
 public interface Parameterizable {
+    /**
+     * Gets the {@link Map} of available parameters for the test to use.
+     *
+     * @return a {@link Map} of {@link BaseParameter} objects.
+     */
     Map<String, BaseParameter> getAvailableParameters();
+
+    /**
+     * Setter method for {@link Parameter.Reader}.
+     *
+     * @param parameterReader the {@link Parameter.Reader} to set.
+     */
     void setParameterReader(Parameter.Reader parameterReader);
+
+    /**
+     * Gets a specific parameter from the current test.
+     *
+     * @param parameterTag a string with the name of the {@link BaseParameter} we want.
+     * @return a parameter that extends {@link BaseParameter} that has the matching parameterTag.
+     */
+    <T extends BaseParameter> T getAvailableParameter(String parameterTag);
 }
diff --git a/threading/sequenced_worker_pool.cc b/threading/sequenced_worker_pool.cc
index 7bbca92..772cc01 100644
--- a/threading/sequenced_worker_pool.cc
+++ b/threading/sequenced_worker_pool.cc
@@ -619,9 +619,11 @@
     // The trace_id is used for identifying the task in about:tracing.
     sequenced.trace_id = trace_id_++;
 
-    TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
-        "SequencedWorkerPool::PostTask",
-        TRACE_ID_MANGLE(GetTaskTraceID(sequenced, static_cast<void*>(this))));
+    TRACE_EVENT_WITH_FLOW0(
+        TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
+        "SequencedWorkerPool::Inner::PostTask",
+        TRACE_ID_MANGLE(GetTaskTraceID(sequenced, static_cast<void*>(this))),
+        TRACE_EVENT_FLAG_FLOW_OUT);
 
     sequenced.sequence_task_number = LockedGetNextSequenceTaskNumber();
 
@@ -754,12 +756,12 @@
       GetWorkStatus status =
           GetWork(&task, &wait_time, &delete_these_outside_lock);
       if (status == GET_WORK_FOUND) {
-        TRACE_EVENT_FLOW_END0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
-            "SequencedWorkerPool::PostTask",
-            TRACE_ID_MANGLE(GetTaskTraceID(task, static_cast<void*>(this))));
-        TRACE_EVENT2("toplevel", "SequencedWorkerPool::ThreadLoop",
-                     "src_file", task.posted_from.file_name(),
-                     "src_func", task.posted_from.function_name());
+        TRACE_EVENT_WITH_FLOW2(
+            TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
+            "SequencedWorkerPool::Inner::ThreadLoop",
+            TRACE_ID_MANGLE(GetTaskTraceID(task, static_cast<void*>(this))),
+            TRACE_EVENT_FLAG_FLOW_IN, "src_file", task.posted_from.file_name(),
+            "src_func", task.posted_from.function_name());
         int new_thread_id = WillRunWorkerTask(task);
         {
           AutoUnlock unlock(lock_);
diff --git a/trace_event/memory_dump_manager.cc b/trace_event/memory_dump_manager.cc
index 6c13179..6d46584 100644
--- a/trace_event/memory_dump_manager.cc
+++ b/trace_event/memory_dump_manager.cc
@@ -93,12 +93,12 @@
 }
 
 MemoryDumpManager::MemoryDumpManager()
-    : did_unregister_dump_provider_(false),
-      delegate_(nullptr),
+    : delegate_(nullptr),
       memory_tracing_enabled_(0),
       tracing_process_id_(kInvalidTracingProcessId),
       system_allocator_pool_name_(nullptr),
-      skip_core_dumpers_auto_registration_for_testing_(false) {
+      skip_core_dumpers_auto_registration_for_testing_(false),
+      disable_periodic_dumps_for_testing_(false) {
   g_next_guid.GetNext();  // Make sure that first guid is not zero.
 }
 
@@ -152,7 +152,16 @@
     const scoped_refptr<SingleThreadTaskRunner>& task_runner) {
   MemoryDumpProviderInfo mdp_info(mdp, task_runner);
   AutoLock lock(lock_);
-  dump_providers_.insert(mdp_info);
+  auto iter_new = dump_providers_.insert(mdp_info);
+
+  // If there was a previous entry, replace it with the new one. This is to deal
+  // with the case where a dump provider unregisters itself and then re-
+  // registers before a memory dump happens, so its entry was still in the
+  // collection but flagged |unregistered|.
+  if (!iter_new.second) {
+    dump_providers_.erase(iter_new.first);
+    dump_providers_.insert(mdp_info);
+  }
 }
 
 void MemoryDumpManager::RegisterDumpProvider(MemoryDumpProvider* mdp) {
@@ -183,8 +192,7 @@
       << "The MemoryDumpProvider attempted to unregister itself in a racy way. "
       << "Please file a crbug.";
 
-  dump_providers_.erase(mdp_iter);
-  did_unregister_dump_provider_ = true;
+  mdp_iter->unregistered = true;
 }
 
 void MemoryDumpManager::RequestGlobalDump(MemoryDumpType dump_type,
@@ -228,7 +236,6 @@
   scoped_ptr<ProcessMemoryDumpAsyncState> pmd_async_state;
   {
     AutoLock lock(lock_);
-    did_unregister_dump_provider_ = false;
     pmd_async_state.reset(new ProcessMemoryDumpAsyncState(
         args, dump_providers_.begin(), session_state_, callback));
   }
@@ -268,18 +275,10 @@
   bool skip_dump = false;
   {
     AutoLock lock(lock_);
-    // In the unlikely event that a dump provider was unregistered while
-    // dumping, abort the dump, as that would make |next_dump_provider| invalid.
-    // Registration, on the other hand, is safe as per std::set<> contract.
-    if (did_unregister_dump_provider_) {
-      return AbortDumpLocked(pmd_async_state->callback,
-                             pmd_async_state->task_runner,
-                             pmd_async_state->req_args.dump_guid);
-    }
 
-    auto* mdp_info = &*pmd_async_state->next_dump_provider;
+    auto mdp_info = pmd_async_state->next_dump_provider;
     mdp = mdp_info->dump_provider;
-    if (mdp_info->disabled) {
+    if (mdp_info->disabled || mdp_info->unregistered) {
       skip_dump = true;
     } else if (mdp_info->task_runner &&
                !mdp_info->task_runner->BelongsToCurrentThread()) {
@@ -318,12 +317,7 @@
 
   {
     AutoLock lock(lock_);
-    if (did_unregister_dump_provider_) {
-      return AbortDumpLocked(pmd_async_state->callback,
-                             pmd_async_state->task_runner,
-                             pmd_async_state->req_args.dump_guid);
-    }
-    auto* mdp_info = &*pmd_async_state->next_dump_provider;
+    auto mdp_info = pmd_async_state->next_dump_provider;
     if (dump_successful) {
       mdp_info->consecutive_failures = 0;
     } else if (!skip_dump) {
@@ -334,6 +328,9 @@
     }
     ++pmd_async_state->next_dump_provider;
     finalize = pmd_async_state->next_dump_provider == dump_providers_.end();
+
+    if (mdp_info->unregistered)
+      dump_providers_.erase(mdp_info);
   }
 
   if (!skip_dump && !dump_successful) {
@@ -427,9 +424,11 @@
   // TODO(primiano): This is a temporary hack to disable periodic memory dumps
   // when running memory benchmarks until they can be enabled/disabled in
   // base::trace_event::TraceConfig. See https://goo.gl/5Hj3o0.
+  // The same mechanism should be used to disable periodic dumps in tests.
   if (delegate_->IsCoordinatorProcess() &&
       !CommandLine::ForCurrentProcess()->HasSwitch(
-          "enable-memory-benchmarking")) {
+          "enable-memory-benchmarking") &&
+      !disable_periodic_dumps_for_testing_) {
     g_periodic_dumps_count = 0;
     periodic_dump_timer_.Start(FROM_HERE,
                                TimeDelta::FromMilliseconds(kDumpIntervalMs),
@@ -454,7 +453,8 @@
     : dump_provider(dump_provider),
       task_runner(task_runner),
       consecutive_failures(0),
-      disabled(false) {}
+      disabled(false),
+      unregistered(false) {}
 
 MemoryDumpManager::MemoryDumpProviderInfo::~MemoryDumpProviderInfo() {
 }
diff --git a/trace_event/memory_dump_manager.h b/trace_event/memory_dump_manager.h
index 5c75e4e..dfaf3a1 100644
--- a/trace_event/memory_dump_manager.h
+++ b/trace_event/memory_dump_manager.h
@@ -97,12 +97,19 @@
     return system_allocator_pool_name_;
   };
 
+  // Tells the initialization phase to skip scheduling periodic memory dumps.
+  void DisablePeriodicDumpsForTesting() {
+    disable_periodic_dumps_for_testing_ = true;
+  }
+
  private:
   friend struct DefaultDeleter<MemoryDumpManager>;  // For the testing instance.
   friend struct DefaultSingletonTraits<MemoryDumpManager>;
   friend class MemoryDumpManagerDelegate;
   friend class MemoryDumpManagerTest;
   FRIEND_TEST_ALL_PREFIXES(MemoryDumpManagerTest, DisableFailingDumpers);
+  FRIEND_TEST_ALL_PREFIXES(MemoryDumpManagerTest,
+                           UnregisterDumperFromThreadWhileDumping);
 
   // Descriptor struct used to hold information about registered MDPs. It is
   // deliberately copyable, in order to allow it to be used as std::set value.
@@ -123,6 +130,11 @@
     // as can be safely changed without impacting the order within the set.
     mutable int consecutive_failures;
     mutable bool disabled;
+
+    // When a dump provider unregisters, it is flagged as |unregistered| and it
+    // is removed only upon the next memory dump. This is to avoid altering the
+    // |dump_providers_| collection while a dump is in progress.
+    mutable bool unregistered;
   };
 
   using MemoryDumpProviderInfoSet = std::set<MemoryDumpProviderInfo>;
@@ -189,10 +201,6 @@
   // affinity (MDPs belonging to the same thread are adjacent).
   MemoryDumpProviderInfoSet dump_providers_;
 
-  // Flag used to signal that some provider was removed from |dump_providers_|
-  // and therefore the current memory dump (if any) should be aborted.
-  bool did_unregister_dump_provider_;
-
   // Shared among all the PMDs to keep state scoped to the tracing session.
   scoped_refptr<MemoryDumpSessionState> session_state_;
 
@@ -219,6 +227,11 @@
   // Skips the auto-registration of the core dumpers during Initialize().
   bool skip_core_dumpers_auto_registration_for_testing_;
 
+  // When true, the initialization phase does not start the periodic memory
+  // dumps.
+  // TODO(primiano): This should go into TraceConfig. https://goo.gl/5Hj3o0.
+  bool disable_periodic_dumps_for_testing_;
+
   DISALLOW_COPY_AND_ASSIGN(MemoryDumpManager);
 };
 
diff --git a/trace_event/memory_dump_manager_unittest.cc b/trace_event/memory_dump_manager_unittest.cc
index 9835415..6fb8c32 100644
--- a/trace_event/memory_dump_manager_unittest.cc
+++ b/trace_event/memory_dump_manager_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/memory/scoped_vector.h"
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/test/test_io_thread.h"
 #include "base/thread_task_runner_handle.h"
 #include "base/threading/thread.h"
 #include "base/trace_event/memory_dump_provider.h"
@@ -16,6 +17,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using testing::_;
+using testing::AtMost;
 using testing::Between;
 using testing::Invoke;
 using testing::Return;
@@ -282,6 +284,55 @@
   DisableTracing();
 }
 
+// Verify that whether OnMemoryDump is called depends only on the current
+// registration state and not on previous registrations and dumps.
+TEST_F(MemoryDumpManagerTest, RegistrationConsistency) {
+  MockDumpProvider mdp;
+
+  mdm_->RegisterDumpProvider(&mdp);
+
+  {
+    EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(1);
+    EnableTracing(kTraceCategory);
+    mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                            high_detail_args);
+    DisableTracing();
+  }
+
+  mdm_->UnregisterDumpProvider(&mdp);
+
+  {
+    EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
+    EnableTracing(kTraceCategory);
+    mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                            high_detail_args);
+    DisableTracing();
+  }
+
+  mdm_->RegisterDumpProvider(&mdp);
+  mdm_->UnregisterDumpProvider(&mdp);
+
+  {
+    EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
+    EnableTracing(kTraceCategory);
+    mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                            high_detail_args);
+    DisableTracing();
+  }
+
+  mdm_->RegisterDumpProvider(&mdp);
+  mdm_->UnregisterDumpProvider(&mdp);
+  mdm_->RegisterDumpProvider(&mdp);
+
+  {
+    EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(1);
+    EnableTracing(kTraceCategory);
+    mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED,
+                            high_detail_args);
+    DisableTracing();
+  }
+}
+
 // Checks that the MemoryDumpManager respects the thread affinity when a
 // MemoryDumpProvider specifies a task_runner(). The test starts creating 8
 // threads and registering a MemoryDumpProvider on each of them. At each
@@ -433,6 +484,61 @@
   DisableTracing();
 }
 
+// Verify that the dump does not abort when unregistering a provider while
+// dumping from a different thread than the dumping thread.
+TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) {
+  ScopedVector<TestIOThread> threads;
+  ScopedVector<MockDumpProvider> mdps;
+
+  for (int i = 0; i < 2; i++) {
+    threads.push_back(new TestIOThread(TestIOThread::kAutoStart));
+    mdps.push_back(new MockDumpProvider(threads.back()->task_runner()));
+    mdm_->RegisterDumpProvider(mdps.back(), threads.back()->task_runner());
+  }
+
+  int on_memory_dump_call_count = 0;
+  RunLoop run_loop;
+
+  // When OnMemoryDump is called on either of the dump providers, it will
+  // unregister the other one.
+  for (MockDumpProvider* mdp : mdps) {
+    int other_idx = (mdps.front() == mdp);
+    TestIOThread* other_thread = threads[other_idx];
+    MockDumpProvider* other_mdp = mdps[other_idx];
+    auto on_dump = [this, other_thread, other_mdp, &on_memory_dump_call_count](
+        const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
+      other_thread->PostTaskAndWait(
+          FROM_HERE, base::Bind(&MemoryDumpManager::UnregisterDumpProvider,
+                                base::Unretained(&*mdm_), other_mdp));
+      on_memory_dump_call_count++;
+      return true;
+    };
+
+    // OnMemoryDump is called once for the provider that dumps first, and zero
+    // times for the other provider.
+    EXPECT_CALL(*mdp, OnMemoryDump(_, _))
+        .Times(AtMost(1))
+        .WillOnce(Invoke(on_dump));
+  }
+
+  last_callback_success_ = false;
+  MemoryDumpCallback callback =
+      Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this),
+           MessageLoop::current()->task_runner(), run_loop.QuitClosure());
+
+  EnableTracing(kTraceCategory);
+  MemoryDumpRequestArgs request_args = {0, MemoryDumpType::EXPLICITLY_TRIGGERED,
+                                        high_detail_args};
+  mdm_->CreateProcessDump(request_args, callback);
+
+  run_loop.Run();
+
+  ASSERT_EQ(1, on_memory_dump_call_count);
+  ASSERT_EQ(true, last_callback_success_);
+
+  DisableTracing();
+}
+
 // Ensures that a NACK callback is invoked if RequestGlobalDump is called when
 // tracing is not enabled.
 TEST_F(MemoryDumpManagerTest, CallbackCalledOnFailure) {
diff --git a/trace_event/trace_buffer.cc b/trace_event/trace_buffer.cc
index 33cb60f..a2e4f14 100644
--- a/trace_event/trace_buffer.cc
+++ b/trace_event/trace_buffer.cc
@@ -4,6 +4,7 @@
 
 #include "base/trace_event/trace_buffer.h"
 
+#include "base/memory/scoped_vector.h"
 #include "base/trace_event/trace_event_impl.h"
 
 namespace base {
diff --git a/trace_event/trace_event_impl.h b/trace_event/trace_event_impl.h
index 936ca4e..1d5b06d 100644
--- a/trace_event/trace_event_impl.h
+++ b/trace_event/trace_event_impl.h
@@ -16,7 +16,6 @@
 #include "base/containers/hash_tables.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/ref_counted_memory.h"
-#include "base/memory/scoped_vector.h"
 #include "base/observer_list.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_util.h"