import org.chromium.base.SysUtils;
import org.chromium.base.ThreadUtils;
+import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
*
* This behaviour is altered by the BROWSER_SHARED_RELRO_CONFIG configuration
* variable below, which may force the browser to load the libraries at
- * fixed addresses to.
+ * 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
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
// Current fixed-location load address for the next library called by loadLibrary().
private static long sCurrentLoadAddress = 0;
- // Becomes true if any library fails to load at a given, non-0, fixed address.
- private static boolean sLoadAtFixedAddressFailed = false;
-
// Becomes true once prepareLibraryLoad() has been called.
private static boolean sPrepareLibraryLoadCalled = false;
if (!sInitialized) {
sRelroSharingSupported = false;
- if (NativeLibraries.USE_LINKER) {
+ if (NativeLibraries.sUseLinker) {
if (DEBUG) Log.i(TAG, "Loading lib" + TAG + ".so");
try {
System.loadLibrary(TAG);
} catch (UnsatisfiedLinkError e) {
// In a component build, the ".cr" suffix is added to each library name.
+ Log.w(TAG, "Couldn't load lib" + TAG + ".so, trying lib" + TAG + ".cr.so");
System.loadLibrary(TAG + ".cr");
}
sRelroSharingSupported = nativeCanUseSharedRelro();
}
if (sMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT) {
- sMemoryDeviceConfig = SysUtils.isLowEndDevice() ?
- MEMORY_DEVICE_CONFIG_LOW : MEMORY_DEVICE_CONFIG_NORMAL;
+ sMemoryDeviceConfig = SysUtils.isLowEndDevice()
+ ? MEMORY_DEVICE_CONFIG_LOW : MEMORY_DEVICE_CONFIG_NORMAL;
}
switch (BROWSER_SHARED_RELRO_CONFIG) {
/**
* 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.ENABLE_LINKER_TESTS
+ * which is controlled by the value of NativeLibraries.sEnableLinkerTests
* configured at build time.
*/
public interface TestRunner {
public static void setTestRunnerClassName(String testRunnerClassName) {
if (DEBUG) Log.i(TAG, "setTestRunnerByClassName(" + testRunnerClassName + ") called");
- if (!NativeLibraries.ENABLE_LINKER_TESTS) {
+ if (!NativeLibraries.sEnableLinkerTests) {
// Ignore this in production code to prevent malvolent runtime injection.
return;
}
public static void setMemoryDeviceConfig(int memoryDeviceConfig) {
if (DEBUG) Log.i(TAG, "setMemoryDeviceConfig(" + memoryDeviceConfig + ") called");
// Sanity check. This method should only be called during tests.
- assert NativeLibraries.ENABLE_LINKER_TESTS;
+ assert NativeLibraries.sEnableLinkerTests;
synchronized (Linker.class) {
assert sMemoryDeviceConfig == MEMORY_DEVICE_CONFIG_INIT;
- assert memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW ||
- memoryDeviceConfig == MEMORY_DEVICE_CONFIG_NORMAL;
+ assert memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW
+ || memoryDeviceConfig == MEMORY_DEVICE_CONFIG_NORMAL;
if (DEBUG) {
if (memoryDeviceConfig == MEMORY_DEVICE_CONFIG_LOW)
Log.i(TAG, "Simulating a low-memory device");
public static 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.USE_LINKER variable will be false.
- if (!NativeLibraries.USE_LINKER)
+ // auto-generated NativeLibraries.sUseLinker variable will be false.
+ if (!NativeLibraries.sUseLinker)
return false;
synchronized (Linker.class) {
* the library directly from the zip file.
*/
public static boolean isInZipFile() {
- return NativeLibraries.USE_LIBRARY_IN_ZIP_FILE;
+ return NativeLibraries.sUseLibraryInZipFile;
}
/**
}
}
- if (NativeLibraries.ENABLE_LINKER_TESTS && sTestRunnerClassName != null) {
+ if (NativeLibraries.sEnableLinkerTests && sTestRunnerClassName != null) {
// The TestRunner implementation must be instantiated _after_
// all libraries are loaded to ensure that its native methods
// are properly registered.
parcel.recycle();
}
if (DEBUG) {
- Log.i(TAG, "useSharedRelros() called with " + bundle +
- ", cloned " + clonedBundle);
+ Log.i(TAG, "useSharedRelros() called with " + bundle
+ + ", cloned " + clonedBundle);
}
synchronized (Linker.class) {
// Note that in certain cases, this can be called before
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));
+ Log.i(TAG, String.format(Locale.US, "Random native base load address: 0x%x", address));
}
return address;
}
}
/**
- * Returns whether the linker was unable to load one library at a given fixed address.
- *
- * @return true if at least one library was not loaded at the expected fixed address.
- */
- public static boolean loadAtFixedAddressFailed() {
- return sLoadAtFixedAddressFailed;
- }
-
- /**
- * Load a native shared library with the Chromium linker.
- * The shared library is 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.
+ * 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 zipfile The filename of the zipfile contain the library.
- * @param library The library's base name.
+ * @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 static void loadLibraryInZipFile(String zipfile, String library) {
- loadLibraryMaybeInZipFile(zipfile, library);
- }
-
- /**
- * Load a native shared library with the Chromium linker.
- *
- * @param library The library's base name.
- */
- public static void loadLibrary(String library) {
- loadLibraryMaybeInZipFile(null, library);
- }
-
- private static void loadLibraryMaybeInZipFile(
- @Nullable String zipFile, String library) {
- if (DEBUG) Log.i(TAG, "loadLibrary: " + library);
-
- // Don't self-load the linker. This is because the build system is
- // not clever enough to understand that all the libraries packaged
- // in the final .apk don't need to be explicitly loaded.
- // Also deal with the component build that adds a .cr suffix to the name.
- if (library.equals(TAG) || library.equals(TAG + ".cr")) {
- if (DEBUG) Log.i(TAG, "ignoring self-linker load");
- return;
- }
+ public static void loadLibrary(@Nullable String zipFilePath, String libFilePath) {
+ if (DEBUG) Log.i(TAG, "loadLibrary: " + zipFilePath + ", " + libFilePath);
synchronized (Linker.class) {
ensureInitializedLocked();
// that wrap all calls to loadLibrary() in the library loader.
assert sPrepareLibraryLoadCalled;
- String libName = System.mapLibraryName(library);
-
- if (sLoadedLibraries == null)
- sLoadedLibraries = new HashMap<String, LibInfo>();
+ if (sLoadedLibraries == null) sLoadedLibraries = new HashMap<String, LibInfo>();
- if (sLoadedLibraries.containsKey(libName)) {
- if (DEBUG) Log.i(TAG, "Not loading " + libName + " twice");
+ if (sLoadedLibraries.containsKey(libFilePath)) {
+ if (DEBUG) Log.i(TAG, "Not loading " + libFilePath + " twice");
return;
}
loadAddress = sCurrentLoadAddress;
}
- String sharedRelRoName = libName;
- if (zipFile != null) {
- if (!nativeLoadLibraryInZipFile(
- zipFile, libName, loadAddress, libInfo)) {
- String errorMessage =
- "Unable to load library: " + libName + " in: " +
- zipFile;
+ String sharedRelRoName = libFilePath;
+ if (zipFilePath != null) {
+ if (!nativeLoadLibraryInZipFile(zipFilePath, libFilePath, loadAddress, libInfo)) {
+ String errorMessage = "Unable to load library: " + libFilePath
+ + ", in: " + zipFilePath;
Log.e(TAG, errorMessage);
throw new UnsatisfiedLinkError(errorMessage);
}
- sharedRelRoName = zipFile;
+ sharedRelRoName = zipFilePath;
} else {
- if (!nativeLoadLibrary(libName, loadAddress, libInfo)) {
- String errorMessage = "Unable to load library: " + libName;
+ if (!nativeLoadLibrary(libFilePath, loadAddress, libInfo)) {
+ String errorMessage = "Unable to load library: " + libFilePath;
Log.e(TAG, errorMessage);
throw new UnsatisfiedLinkError(errorMessage);
}
}
- // Keep track whether the library has been loaded at the expected load address.
- if (loadAddress != 0 && loadAddress != libInfo.mLoadAddress)
- sLoadAtFixedAddressFailed = true;
// 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:
// RENDERER_LIBRARY_ADDRESS: <library-name> <address>
// Where <library-name> is the library name, and <address> is the hexadecimal load
// address.
- if (NativeLibraries.ENABLE_LINKER_TESTS) {
+ if (NativeLibraries.sEnableLinkerTests) {
Log.i(TAG, String.format(
Locale.US,
"%s_LIBRARY_ADDRESS: %s %x",
sInBrowserProcess ? "BROWSER" : "RENDERER",
- libName,
+ libFilePath,
libInfo.mLoadAddress));
}
// Create a new shared RELRO section at the 'current' fixed load address.
if (!nativeCreateSharedRelro(sharedRelRoName, sCurrentLoadAddress, libInfo)) {
Log.w(TAG, String.format(Locale.US,
- "Could not create shared RELRO for %s at %x", libName,
+ "Could not create shared RELRO for %s at %x", libFilePath,
sCurrentLoadAddress));
} else {
if (DEBUG) Log.i(TAG,
sCurrentLoadAddress = libInfo.mLoadAddress + libInfo.mLoadSize;
}
- sLoadedLibraries.put(libName, libInfo);
+ sLoadedLibraries.put(sharedRelRoName, libInfo);
if (DEBUG) Log.i(TAG, "Library details " + libInfo.toString());
}
}
/**
+ * Enable the fallback due to lack of support for mapping the APK file with
+ * executable permission (see crbug.com/398425).
+ */
+ public static void enableNoMapExecSupportFallback() {
+ synchronized (Linker.class) {
+ ensureInitializedLocked();
+
+ if (DEBUG) Log.i(TAG, "Enabling no map executable support fallback");
+ nativeEnableNoMapExecSupportFallback();
+ }
+ }
+
+ /**
+ * Determine whether a library is the linker library. Also deal with the
+ * component build that adds a .cr suffix to the name.
+ */
+ public static boolean isChromiumLinkerLibrary(String library) {
+ return library.equals(TAG) || library.equals(TAG + ".cr");
+ }
+
+ /**
+ * Get the full library path in zip file (lib/<abi>/crazy.<lib_name>).
+ *
+ * @param library The library's base name.
+ * @return the library path.
+ */
+ public static String getLibraryFilePathInZipFile(String library) throws FileNotFoundException {
+ synchronized (Linker.class) {
+ ensureInitializedLocked();
+
+ String path = nativeGetLibraryFilePathInZipFile(library);
+ if (path.equals("")) {
+ throw new FileNotFoundException(
+ "Failed to retrieve path in zip file for library " + library);
+ }
+ return path;
+ }
+ }
+
+ /**
+ * Check whether the device supports mapping the APK file with executable permission.
+ *
+ * @param apkFile Filename of the APK.
+ * @return true if supported.
+ */
+ public static boolean checkMapExecSupport(String apkFile) {
+ assert apkFile != null;
+ synchronized (Linker.class) {
+ ensureInitializedLocked();
+
+ if (DEBUG) Log.i(TAG, "checkMapExecSupport: " + apkFile);
+ boolean supported = nativeCheckMapExecSupport(apkFile);
+ if (DEBUG) Log.i(TAG, "Mapping the APK file with executable permission "
+ + (supported ? "" : "NOT ") + "supported");
+ return supported;
+ }
+ }
+
+ /**
+ * Check whether a library is page aligned and uncompressed in the APK file.
+ *
+ * @param apkFile Filename of the APK.
+ * @param library The library's base name.
+ * @return true if page aligned and uncompressed.
+ */
+ public static boolean checkLibraryIsMappableInApk(String apkFile, String library) {
+ synchronized (Linker.class) {
+ ensureInitializedLocked();
+
+ if (DEBUG) Log.i(TAG, "checkLibraryIsMappableInApk: " + apkFile + ", " + library);
+ boolean aligned = nativeCheckLibraryIsMappableInApk(apkFile, library);
+ if (DEBUG) Log.i(TAG, library + " is " + (aligned ? "" : "NOT ")
+ + "page aligned in " + apkFile);
+ return aligned;
+ }
+ }
+
+ /**
* Move activity from the native thread to the main UI thread.
* Called from native code on its own thread. Posts a callback from
* the UI thread back to native code.
* of this LibInfo instance will set on success.
* @return true for success, false otherwise.
*/
- private static native boolean nativeLoadLibraryInZipFile(
- String zipfileName,
- String libraryName,
- long loadAddress,
- LibInfo libInfo);
+ private static native boolean nativeLoadLibraryInZipFile(String zipfileName,
+ String libraryName,
+ long loadAddress,
+ LibInfo libInfo);
+
+ /**
+ * Native method used to enable the fallback due to lack of support for
+ * mapping the APK file with executable permission.
+ */
+ private static native void nativeEnableNoMapExecSupportFallback();
/**
* Native method used to create a shared RELRO section.
private static native long nativeGetRandomBaseLoadAddress(long sizeBytes);
/**
+ * Native method used to get the full library path in zip file
+ * (lib/<abi>/crazy.<lib_name>).
+ *
+ * @param library The library's base name.
+ * @return the library path (or empty string on failure).
+ */
+ private static native String nativeGetLibraryFilePathInZipFile(String library);
+
+ /**
+ * Native method which checks whether the device supports mapping the APK file
+ * with executable permission.
+ *
+ * @param apkFile Filename of the APK.
+ * @return true if supported.
+ */
+ private static native boolean nativeCheckMapExecSupport(String apkFile);
+
+ /**
+ * Native method which checks whether a library is page aligned and
+ * uncompressed in the APK file.
+ *
+ * @param apkFile Filename of the APK.
+ * @param library The library's base name.
+ * @return true if page aligned and uncompressed.
+ */
+ private static native boolean nativeCheckLibraryIsMappableInApk(String apkFile, String library);
+
+ /**
* 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.
// Used to pass the shared RELRO Bundle through Binder.
public static final String EXTRA_LINKER_SHARED_RELROS =
- "org.chromium.base.android.linker.shared_relros";
+ "org.chromium.base.android.linker.shared_relros";
}