Index: android/java/src/org/chromium/base/library_loader/Linker.java |
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 c769339620df9bae97d72735f05b030d064bb57a..ee62570fed75a0fa23a1717cd6f75e7fea2225cf 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 @@ import javax.annotation.Nullable; |
* |
* - 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 @@ import javax.annotation.Nullable; |
* 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 @@ import javax.annotation.Nullable; |
* 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 @@ import javax.annotation.Nullable; |
* 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,18 +147,8 @@ public abstract class Linker { |
// Log tag for this class. |
private static final String TAG = "cr.library_loader"; |
- // Set to true to enable debug logs. |
- protected static final boolean DEBUG = false; |
- |
- // Used to pass the shared RELRO Bundle through Binder. |
- public static final String EXTRA_LINKER_SHARED_RELROS = |
- "org.chromium.base.android.linker.shared_relros"; |
- |
- // 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. |
+ // 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. |
@@ -175,11 +159,13 @@ public abstract class Linker { |
// 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 value of sMemoryDeviceConfig. |
+ // 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. |
@@ -189,9 +175,44 @@ public abstract class Linker { |
// 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(). |
+ // 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; |
+ |
+ // Used to pass the shared RELRO Bundle through Binder. |
+ public static final String EXTRA_LINKER_SHARED_RELROS = |
+ "org.chromium.base.android.linker.shared_relros"; |
+ |
+ // Guards all access to the linker. |
+ protected final Object mLock = new Object(); |
+ |
+ // 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 LINKER_IMPLEMENTATION_LEGACY = 1; |
+ public static final int LINKER_IMPLEMENTATION_MODERN = 2; |
+ |
// Singleton. |
private static Linker sSingleton = null; |
private static Object sSingletonLock = new Object(); |
@@ -199,19 +220,111 @@ public abstract class Linker { |
// 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 abstract class Linker { |
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"); |
- } |
- if (!NativeLibraries.sEnableLinkerTests) { |
- // Ignore this in production code to prevent malevolent runtime injection. |
- return; |
+ Log.i(TAG, "setTestRunnerByClassNameForTesting(" + testRunnerClassName + ") called"); |
} |
+ // 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 @@ public abstract class Linker { |
* 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 class Linker { |
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 @@ public abstract class Linker { |
* 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 @@ public abstract class Linker { |
/** |
* 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 @@ public abstract 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. |
+ * |
* @param baseLoadAddress the base library load address to use. |
*/ |
public abstract void initServiceProcess(long baseLoadAddress); |
@@ -356,34 +613,28 @@ public abstract class Linker { |
* 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 @@ public abstract class Linker { |
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 @@ public abstract class Linker { |
@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 @@ public abstract class Linker { |
for (Map.Entry<String, LibInfo> entry : map.entrySet()) { |
bundle.putParcelable(entry.getKey(), entry.getValue()); |
} |
- |
return bundle; |
} |
@@ -508,4 +758,15 @@ public abstract class Linker { |
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(); |
} |