Index: base/android/java/src/org/chromium/base/library_loader/Linker.java |
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java |
index 7e5099875a6d9be9b438c2e117e3ec7bdc2073b3..49acead1e0aca1676070eb598d6e78538075d4e9 100644 |
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java |
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java |
@@ -106,16 +106,14 @@ import javax.annotation.Nullable; |
* running any native code from any of the libraries (except their static |
* constructors, which can't be avoided). |
* |
- * - A service process shall call either initServiceProcess() or |
- * disableSharedRelros() early (i.e. before any loadLibrary() call). |
- * Otherwise, the linker considers that it is running inside the browser |
- * process. This is because various Chromium projects have vastly |
- * different initialization paths. |
+ * - A service process shall call initServiceProcess() early (i.e. before any |
+ * loadLibrary() call). Otherwise, the linker considers that it is running |
+ * inside the browser process. This is because various Chromium projects have |
+ * vastly different initialization paths. |
* |
- * disableSharedRelros() completely disables shared RELROs, and loadLibrary() |
- * will behave exactly like System.loadLibrary(). |
+ * disableSharedRelros() completely disables shared RELROs. |
* |
- * initServiceProcess(baseLoadAddress) indicates that shared RELROs are to be |
+ * enableSharedRelros(baseLoadAddress) indicates that shared RELROs are to be |
* used in this process. |
* |
* - The browser is in charge of deciding where in memory each library should |
@@ -140,18 +138,22 @@ import javax.annotation.Nullable; |
* fixed addresses too. |
* |
* - Once all libraries are loaded in the browser process, one can call |
- * getSharedRelros() which returns a Bundle instance containing a map that |
- * links each loaded library to its shared RELRO region. |
+ * produceSharedStateBundle() which returns a Bundle instance of |
+ * resources that a browser process can pass on to child processes. These resources |
+ * include a map that links each loaded library to its shared RELRO region. |
+ * TODO(oth): include fallback library descriptors. |
* |
* This Bundle must be passed to each service process, for example through |
* 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() will block until the Bundle is received. |
+ * When the Bundle is received, the service process must call |
+ * consumeSharedStateBundle() from the receiving thread. Typically this will |
+ * be a different thread to the thread calling finishLibraryLoad() as it will block |
+ * until consumeSharedStateBundle() is called. |
* |
- * This method also ensures the process uses the shared RELROs. |
+ * consumeSharedStateBundle() also ensures the process uses the shared RELROs. |
*/ |
public class Linker { |
@@ -197,14 +199,8 @@ public class Linker { |
private static boolean sRelroSharingSupported = 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 static boolean sInBrowserProcess = true; |
- // Becomes true to indicate this process needs to wait for a shared RELRO in |
- // finishLibraryLoad(). |
- private static boolean sWaitForSharedRelros = false; |
- |
// Becomes true when initialization determines that the browser process can use the |
// shared RELRO. |
private static boolean sBrowserUsesSharedRelro = false; |
@@ -274,7 +270,6 @@ public class Linker { |
if (!sRelroSharingSupported) { |
// Sanity. |
sBrowserUsesSharedRelro = false; |
- sWaitForSharedRelros = false; |
} |
sInitialized = true; |
@@ -413,18 +408,18 @@ public class Linker { |
/** |
* 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(). |
+ * received, i.e. when another thread calls consumeSharedStateBundle(). |
*/ |
public static void finishLibraryLoad() { |
if (DEBUG) Log.i(TAG, "finishLibraryLoad() called"); |
synchronized (Linker.class) { |
- if (DEBUG) Log.i(TAG, String.format( |
- Locale.US, |
- "sInBrowserProcess=%s sBrowserUsesSharedRelro=%s sWaitForSharedRelros=%s", |
- sInBrowserProcess ? "true" : "false", |
- sBrowserUsesSharedRelro ? "true" : "false", |
- sWaitForSharedRelros ? "true" : "false")); |
- |
+ if (DEBUG) { |
+ Log.i(TAG, |
+ String.format(Locale.US, |
+ "sInBrowserProcess=%s sBrowserUsesSharedRelro=%s", |
+ sInBrowserProcess ? "true" : "false", |
+ sBrowserUsesSharedRelro ? "true" : "false")); |
+ } |
if (sLoadedLibraries == null) { |
if (DEBUG) Log.i(TAG, "No libraries loaded"); |
} else { |
@@ -440,23 +435,13 @@ public class Linker { |
if (sBrowserUsesSharedRelro) { |
useSharedRelrosLocked(sSharedRelros); |
} |
- } |
- |
- if (sWaitForSharedRelros) { |
- assert !sInBrowserProcess; |
- |
- // Wait until the shared relro bundle is received from useSharedRelros(). |
- while (sSharedRelros == null) { |
- try { |
- Linker.class.wait(); |
- } catch (InterruptedException ie) { |
- // no-op |
- } |
+ } else { |
+ waitForSharedStateBundleLocked(); |
+ if (!sSharedRelros.isEmpty()) { |
+ useSharedRelrosLocked(sSharedRelros); |
+ // Clear the Bundle to ensure it can't be reused. |
+ sSharedRelros.clear(); |
} |
- useSharedRelrosLocked(sSharedRelros); |
- // Clear the Bundle to ensure its file descriptor references can't be reused. |
- sSharedRelros.clear(); |
- sSharedRelros = null; |
} |
} |
@@ -487,14 +472,15 @@ public class Linker { |
} |
/** |
- * Call this to send a Bundle containing the shared RELRO sections to be |
- * used in this process. If initServiceProcess() was previously called, |
- * finishLibraryLoad() will not exit until this method is called in another |
+ * Call this in service processes to consume shared linker resources in a Bundle from a browser |
+ * (these will have been produced with produceSharedStateBundle). If enableSharedRelros() 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. |
*/ |
- public static void useSharedRelros(Bundle bundle) { |
+ public static void consumeSharedStateBundle(Bundle bundle) { |
+ assert !sInBrowserProcess : "Browser does not consume shared state bundle."; |
// Ensure the bundle uses the application's class loader, not the framework |
// one which doesn't know anything about LibInfo. |
// Also, hold a fresh copy of it so the caller can't recycle it. |
@@ -509,31 +495,43 @@ public class Linker { |
parcel.recycle(); |
} |
if (DEBUG) { |
- Log.i(TAG, "useSharedRelros() called with " + bundle |
- + ", cloned " + clonedBundle); |
+ Log.i(TAG, "consumeSharedStateBundle() called with " + bundle + ", cloned " |
+ + clonedBundle); |
} |
synchronized (Linker.class) { |
// Note that in certain cases, this can be called before |
- // initServiceProcess() in service processes. |
+ // enableSharedRelros() in service processes. |
sSharedRelros = clonedBundle; |
// Tell any listener blocked in finishLibraryLoad() about it. |
Linker.class.notifyAll(); |
} |
} |
+ private static void waitForSharedStateBundleLocked() { |
+ assert Thread.holdsLock(Linker.class); |
+ |
+ while (sSharedRelros == null) { |
+ try { |
+ Linker.class.wait(); |
+ } catch (InterruptedException ie) { |
+ // no-op |
+ } |
+ } |
+ } |
+ |
/** |
- * 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. |
+ * Call this to retrieve shared state between the browser process and it's |
+ * children, e.g. the RELRO sections created in this process. |
+ * |
+ * Only valid for browser processes and after loading all libraries. |
+ * |
+ * @return Bundle instance for sharable linker state. |
*/ |
- public static Bundle getSharedRelros() { |
- if (DEBUG) Log.i(TAG, "getSharedRelros() called"); |
+ public static Bundle produceSharedStateBundle() { |
+ if (DEBUG) Log.i(TAG, "getSharedStateBundle() called"); |
synchronized (Linker.class) { |
- if (!sInBrowserProcess) { |
- if (DEBUG) Log.i(TAG, "... returning null Bundle"); |
- return null; |
- } |
+ assert sInBrowserProcess : "Non-browser does not share state bundle."; |
+ assert sSharedRelros != null : "Bundle not ready."; |
// Return the Bundle created in finishLibraryLoad(). |
if (DEBUG) Log.i(TAG, "... returning " + sSharedRelros); |
@@ -541,16 +539,27 @@ public class Linker { |
} |
} |
+ /** |
+ * Call this method before enabling/disabling shared RELRO sections to |
+ * indicate that this is a service process (i.e. not a browser process). |
+ */ |
+ public static void initServiceProcess() { |
+ if (DEBUG) Log.i(TAG, "initServiceProcess() called"); |
+ synchronized (Linker.class) { |
+ assert sSharedRelros == null : "Bundle is initially empty for non-browser."; |
+ sInBrowserProcess = false; |
+ sBrowserUsesSharedRelro = false; |
+ } |
+ } |
/** |
* Call this method before loading any libraries to indicate that this |
- * process shall neither create or reuse shared RELRO sections. |
+ * process shall neither create nor reuse shared RELRO sections. |
*/ |
public static void disableSharedRelros() { |
if (DEBUG) Log.i(TAG, "disableSharedRelros() called"); |
synchronized (Linker.class) { |
- sInBrowserProcess = false; |
- sWaitForSharedRelros = false; |
+ sBaseLoadAddress = 0; |
sBrowserUsesSharedRelro = false; |
} |
} |
@@ -558,20 +567,20 @@ public class Linker { |
/** |
* 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. |
+ * Typically used when starting service processes. Any calls to this method |
+ * must be preceded by initServiceProcess(). |
* @param baseLoadAddress the base library load address to use. |
*/ |
- public static void initServiceProcess(long baseLoadAddress) { |
+ public static void enableSharedRelros(long baseLoadAddress) { |
if (DEBUG) { |
Log.i(TAG, String.format( |
- Locale.US, "initServiceProcess(0x%x) called", baseLoadAddress)); |
+ Locale.US, "enableSharedRelros(0x%x) called", baseLoadAddress)); |
} |
synchronized (Linker.class) { |
+ assert !sInBrowserProcess; |
+ assert !sBrowserUsesSharedRelro; |
ensureInitializedLocked(); |
- sInBrowserProcess = false; |
- sBrowserUsesSharedRelro = false; |
if (sRelroSharingSupported) { |
- sWaitForSharedRelros = true; |
sBaseLoadAddress = baseLoadAddress; |
sCurrentLoadAddress = baseLoadAddress; |
} |
@@ -612,7 +621,6 @@ public class Linker { |
// free address space, so disable RELRO shared / fixed load addresses. |
Log.w(TAG, "Disabling shared RELROs due address space pressure"); |
sBrowserUsesSharedRelro = false; |
- sWaitForSharedRelros = false; |
} |
} |
} |
@@ -730,7 +738,7 @@ public class Linker { |
LibInfo libInfo = new LibInfo(); |
long loadAddress = 0; |
- if ((sInBrowserProcess && sBrowserUsesSharedRelro) || sWaitForSharedRelros) { |
+ if ((sInBrowserProcess && sBrowserUsesSharedRelro) || sBaseLoadAddress != 0) { |
// Load the library at a fixed address. |
loadAddress = sCurrentLoadAddress; |
} |
@@ -1137,7 +1145,7 @@ public class Linker { |
// The map of libraries that are currently loaded in this process. |
private static HashMap<String, LibInfo> sLoadedLibraries = null; |
- // Used to pass the shared RELRO Bundle through Binder. |
- public static final String EXTRA_LINKER_SHARED_RELROS = |
- "org.chromium.base.android.linker.shared_relros"; |
+ // Used to pass the shared linker state Bundle through the Binder. |
+ public static final String EXTRA_LINKER_SHARED_STATE = |
+ "org.chromium.base.android.linker.shared_state"; |
} |