Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(230)

Unified Diff: base/android/java/src/org/chromium/base/library_loader/Linker.java

Issue 673093005: Fallback for loading library from a zipfile. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 41104f0453e7acc0631a3d535ed3bbe3ae30c725..20d2cb084d12c865de361c49a7f18f2abaccc983 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
@@ -15,9 +15,20 @@ import org.chromium.base.CalledByNative;
import org.chromium.base.SysUtils;
import org.chromium.base.ThreadUtils;
+import java.io.BufferedOutputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
import javax.annotation.Nullable;
@@ -651,7 +662,7 @@ public class Linker {
final long address = nativeGetRandomBaseLoadAddress(maxExpectedBytes);
if (DEBUG) {
Log.i(TAG,
- String.format(Locale.US, "Random native base load address: 0x%x", address));
+ String.format(Locale.US, "Random native base load address: 0x%x", address));
}
return address;
}
@@ -717,9 +728,11 @@ public class Linker {
*
* @param zipfile The filename of the zipfile contain the library.
* @param library The library's base name.
+ * @param fallbackDir Name of directory to use if falling back on copying.
*/
- public static void loadLibraryInZipFile(String zipfile, String library) {
- loadLibraryMaybeInZipFile(zipfile, library);
+ public static void loadLibraryInZipFile(
+ String zipfile, String library, String fallbackDir) {
+ loadLibraryMaybeInZipFile(zipfile, library, fallbackDir);
}
/**
@@ -728,11 +741,13 @@ public class Linker {
* @param library The library's base name.
*/
public static void loadLibrary(String library) {
- loadLibraryMaybeInZipFile(null, library);
+ loadLibraryMaybeInZipFile(null, library, null);
}
private static void loadLibraryMaybeInZipFile(
- @Nullable String zipFile, String library) {
+ @Nullable String zipFile,
+ String library, @Nullable String fallbackDir) {
+
if (DEBUG) Log.i(TAG, "loadLibrary: " + library);
// Don't self-load the linker. This is because the build system is
@@ -771,13 +786,19 @@ public class Linker {
String sharedRelRoName = libName;
if (zipFile != null) {
- if (!nativeLoadLibraryInZipFile(zipFile, libName, loadAddress, libInfo)) {
- String errorMessage = "Unable to load library: " + libName +
- ", in: " + zipFile;
+ if (!checkLibraryLoadFromApkSupport(zipFile)) {
+ sharedRelRoName = loadLibraryInZipFileFallback(
+ zipFile, libName, fallbackDir, loadAddress, sInBrowserProcess, libInfo);
+ } else if (nativeLoadLibraryInZipFile(
+ zipFile, libName, loadAddress, libInfo)) {
+ sharedRelRoName = zipFile;
+ } else {
+ String errorMessage =
+ "Unable to load library: " + libName + " in: " +
+ zipFile;
Log.e(TAG, errorMessage);
throw new UnsatisfiedLinkError(errorMessage);
}
- sharedRelRoName = zipFile;
} else {
if (!nativeLoadLibrary(libName, loadAddress, libInfo)) {
String errorMessage = "Unable to load library: " + libName;
@@ -849,6 +870,235 @@ public class Linker {
}
}
+ // Log an error and throw UnsatisfiedLinkError.
+ private static void throwUnsatisfiedLinkError(
+ String fallbackLibName, String zipfileName, String reason, @Nullable Throwable e) {
+ String errorMessage =
+ "Unable to load library: " + fallbackLibName +
+ " when falling back from loading directly from: " +
+ zipfileName + ", " + reason;
+ if (e != null) {
+ Log.e(TAG, errorMessage, e);
+ } else {
+ Log.e(TAG, errorMessage);
+ }
+ throw new UnsatisfiedLinkError(errorMessage);
+ }
+
+ // Delete the file and log it.
+ private static void deleteFile(File file) {
+ if (file.delete()) {
+ Log.i(TAG, "deleted file: " + file);
+ } else {
+ Log.w(TAG, "failed to delete file: " + file);
+ }
+ }
+
+ // Delete the temporary file and log a warning if it fails.
+ private static void deleteTempFile(File tmpFile) {
+ if (!tmpFile.delete()) {
+ Log.w(TAG, "Failed to delete the temporary fallback file: " + tmpFile.getPath());
+ }
+ }
+
+ // Close the closable and log a warning if it fails.
+ private static void close(Closeable closable, String name) {
+ try {
+ closable.close();
+ } catch (IOException e) {
+ // warn and ignore
+ Log.w(TAG, "IO exception when closing " + name, e);
+ }
+ }
+
+ private static final String sFallbackPrefix = "fallback-";
+ private static final String sTempPrefix = "tmp-";
+
+ /**
+ * Used as a fallback when we are unable to load the library directly
+ * from the zipfile. Instead we copy the library into the application
+ * private directory and load it from there.
+ *
+ * @param zipfileName Filename of the zip file containing the library.
+ * @param library Platform specific library name (e.g. libfoo.so)
+ * @param fallbackDir Name of directory to put a copy of the library.
+ * @param createFallbackIfNeeded If necessary make the fallback file.
+ * @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 name of the fallback copy of the library.
+ */
+ private static String loadLibraryInZipFileFallback(
+ String zipfileName,
+ String libraryName,
+ String fallbackDir,
+ long loadAddress,
+ boolean createFallbackIfNeeded,
+ LibInfo libInfo) {
+
+ File fallbackDirFile = new File(fallbackDir);
+ // Name used for the fallback library in the filesystem.
+ File fallbackLib = new File(fallbackDirFile, sFallbackPrefix + libraryName);
+ String fallbackLibName = fallbackLib.getPath();
+
+ // We either want to create a new fallback file or we want to check
+ // the one we have is the same size as the one in the archive. So
+ // we always open the zip file and locate the library entry. Sadly
+ // this does take tens of milliseconds.
+ long before = System.currentTimeMillis();
+ ZipFile zipfile = null;
+ try {
+ zipfile = new ZipFile(zipfileName);
+ } catch (ZipException e) {
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName, "unable to open the zip file", e);
+ } catch (IOException e) {
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName, "unable to open the zip file", e);
+ }
+
+ // Open the library inside the zip file.
+ String nameInZip = nativeLibraryFilenameInZipFile(libraryName);
+ ZipEntry zipEntry = null;
+ try {
+ zipEntry = zipfile.getEntry(nameInZip);
+ } catch (IllegalStateException e) {
+ close(zipfile, "zipfile");
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName, "zipfile has been closed", e);
+ }
+
+ boolean useExistingFallbackFile = fallbackLib.exists();
+ if (useExistingFallbackFile) {
+ // Check that it is reasonable to use the existing file.
+ File zipfileFile = new File(zipfileName);
+ long zipfileTime = zipfileFile.lastModified();
+ long fallbackLibTime = fallbackLib.lastModified();
+ if (zipfileTime > fallbackLibTime) {
+ Log.i(TAG, "Not using existing fallback file because the"
+ + " APK file: " + zipfileName
+ + " is newer then fallback library " + libraryName
+ + " APK timestamp: " + zipfileTime
+ + " fallback library timestamp " + fallbackLibTime);
+ deleteFile(fallbackLib);
+ useExistingFallbackFile = false;
+ } else {
+ long fallbackLibSize = fallbackLib.length();
+ long zipfileLibSize = zipEntry.getSize();
+ if (fallbackLibSize != zipfileLibSize) {
+ Log.i(TAG, "Not using existing fallback file because the"
+ + " library in the APK file is a different size: "
+ + " fallback library size: " + fallbackLibSize
+ + " library in APK size: " + zipfileLibSize);
+ deleteFile(fallbackLib);
+ useExistingFallbackFile = false;
+ }
+ }
+ }
picksi1 2014/10/28 12:25:26 Should the above block of code be re-jigged slight
Anton 2014/10/28 12:42:14 Agreed to the decomposition. Though fallbackLib.ex
+
+ if (createFallbackIfNeeded && !useExistingFallbackFile) {
+ // Remove any "tmp-" and "fallback-" libraries which currently exist.
+ for (File f : fallbackDirFile.listFiles()) {
+ String name = f.getName();
+ if (name.endsWith(".so") &&
+ (name.startsWith(sFallbackPrefix + "lib")
+ || name.startsWith(sTempPrefix + "lib"))) {
+ deleteFile(f);
+ }
+ }
+ // Locate the library inside the zip file.
+ InputStream in = null;
+ try {
+ in = zipfile.getInputStream(zipEntry);
+ } catch (IOException e) {
+ close(zipfile, "zipfile");
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName,
+ "io error locating library in the zipfile", e);
+ }
+ // Temporary name used while the fallback library file is incomplete.
+ File tmpFile = new File(fallbackDirFile, sTempPrefix + libraryName);
+
+ // Open the output file.
+ OutputStream out = null;
+ try {
+ out = new BufferedOutputStream(new FileOutputStream(tmpFile));
+ } catch (FileNotFoundException e) {
+ close(in, "input stream");
+ close(zipfile, "zipfile");
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName, "failed to create fallback file", e);
+ } catch (SecurityException e) {
+ close(in, "input stream");
+ close(zipfile, "zipfile");
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName,
+ "security prevented creation of fallback file", e);
+ }
+ try {
+ // Copy the library from the zipfile into the file system.
+ byte[] buffer = new byte[4096];
picksi1 2014/10/28 12:25:26 Will this leak? Or does 'byte' contain a destructo
Anton 2014/10/28 12:42:14 Nope. This is Java it is garbage collected.
+ int len;
+ while ((len = in.read(buffer)) != -1) {
+ out.write(buffer, 0, len);
+ }
+ } catch (IOException e) {
+ // Attempt to delete the fallback file.
+ deleteTempFile(tmpFile);
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName, "failed to make copy of library file", e);
+ } finally {
+ close(in, "input stream");
+ close(out, "output stream");
+ close(zipfile, "zipfile");
+ }
+ if (!tmpFile.setReadable(/* readable */ true, /* ownerOnly */ false)) {
+ deleteTempFile(tmpFile);
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName, "failed to chmod +r the file", null);
+ }
+ if (!tmpFile.setExecutable(/* executable */ true, /* ownerOnly */ false)) {
+ deleteTempFile(tmpFile);
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName, "failed to chmod +x the file", null);
+ }
+ if (!tmpFile.renameTo(fallbackLib)) {
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName, "failed to rename the temporary file", null);
+ }
+ long after = System.currentTimeMillis();
+ Log.i(TAG, "Fallback copying library took: " + (after - before) + " millis");
+ Log.i(TAG, "Fallback library is: " + fallbackLibName + " exists: " +
+ fallbackLib.exists());
+ useExistingFallbackFile = true;
+ } else {
+ close(zipfile, "zipfile");
+ long after = System.currentTimeMillis();
+ Log.i(TAG, "Checking the fallback library took: " + (after - before) + " millis");
+ }
+
+ if (!useExistingFallbackFile) {
+ if (fallbackLib.exists()) {
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName, "the fallback file was invalid", null);
+ } else {
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName,
+ "the browser process did not create the fallback file", null);
+ }
+ }
+
+ if (!nativeLoadLibrary(fallbackLibName, loadAddress, libInfo)) {
+ // Delete the fallback library. In case the failure was because the
+ // file was corrupt, this is our best bet at avoiding becoming
+ // permenantly broken.
+ deleteFile(fallbackLib);
+ throwUnsatisfiedLinkError(
+ fallbackLibName, zipfileName, "fallback copy did not load", null);
+ }
+ return fallbackLibName;
+ }
+
/**
* Move activity from the native thread to the main UI thread.
* Called from native code on its own thread. Posts a callback from
@@ -886,6 +1136,14 @@ public class Linker {
LibInfo libInfo);
/**
+ * Native method used to get the library filename in zip file:
+ * "lib/<abi>/crazy.<lib_name>".
+ *
+ * @return the library filename (or empty string on failure).
+ */
+ private static native String nativeLibraryFilenameInZipFile(String libName);
+
+ /**
* 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)
@@ -895,10 +1153,10 @@ public class Linker {
* @return true for success, false otherwise.
*/
private static native boolean nativeLoadLibraryInZipFile(
- String zipfileName,
- String libraryName,
- long loadAddress,
- LibInfo libInfo);
+ String zipfileName,
+ String libraryName,
+ long loadAddress,
+ LibInfo libInfo);
/**
* Native method used to create a shared RELRO section.
@@ -1089,5 +1347,5 @@ public class Linker {
// 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";
}

Powered by Google App Engine
This is Rietveld 408576698