/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.facebook.soloader; import android.os.StrictMode; import android.util.Log; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import javax.annotation.Nullable; /** {@link SoSource} that finds shared libraries in a given directory. */ public class DirectorySoSource extends SoSource { public static final int RESOLVE_DEPENDENCIES = 1; public static final int ON_LD_LIBRARY_PATH = 2; protected final File soDirectory; protected final int flags; /** * Make a new DirectorySoSource. If {@code flags} contains {@code RESOLVE_DEPENDENCIES}, * recursively load dependencies for shared objects loaded from this directory. (We shouldn't need * to resolve dependencies for libraries loaded from system directories: the dynamic linker is * smart enough to do it on its own there.) */ public DirectorySoSource(File soDirectory, int flags) { this.soDirectory = soDirectory; this.flags = flags; } @Override public int loadLibrary(String soName, int loadFlags, StrictMode.ThreadPolicy threadPolicy) throws IOException { return loadLibraryFrom(soName, loadFlags, soDirectory, threadPolicy); } // Abstracted this logic in another method so subclasses can take advantage of it. protected int loadLibraryFrom( String soName, int loadFlags, File libDir, StrictMode.ThreadPolicy threadPolicy) throws IOException { File soFile = new File(libDir, soName); if (!soFile.exists()) { Log.d(SoLoader.TAG, soName + " not found on " + libDir.getCanonicalPath()); return LOAD_RESULT_NOT_FOUND; } else { Log.d(SoLoader.TAG, soName + " found on " + libDir.getCanonicalPath()); } if ((loadFlags & LOAD_FLAG_ALLOW_IMPLICIT_PROVISION) != 0 && (flags & ON_LD_LIBRARY_PATH) != 0) { Log.d(SoLoader.TAG, soName + " loaded implicitly"); return LOAD_RESULT_IMPLICITLY_PROVIDED; } if ((flags & RESOLVE_DEPENDENCIES) != 0) { loadDependencies(soFile, loadFlags, threadPolicy); } else { Log.d(SoLoader.TAG, "Not resolving dependencies for " + soName); } try { SoLoader.sSoFileLoader.load(soFile.getAbsolutePath(), loadFlags); } catch (UnsatisfiedLinkError e) { if (e.getMessage().contains("bad ELF magic")) { Log.d(SoLoader.TAG, "Corrupted lib file detected"); // Swallow exception. Higher layers will try again from a backup source return LOAD_RESULT_CORRUPTED_LIB_FILE; } else { throw e; } } return LOAD_RESULT_LOADED; } @Override @Nullable public String getLibraryPath(String soName) throws IOException { File soFile = new File(soDirectory, soName); if (soFile.exists()) { return soFile.getCanonicalPath(); } return null; } @Nullable @Override public String[] getLibraryDependencies(String soName) throws IOException { File soFile = new File(soDirectory, soName); if (soFile.exists()) { return getDependencies(soFile); } else { return null; } } private static void loadDependencies( File soFile, int loadFlags, StrictMode.ThreadPolicy threadPolicy) throws IOException { String[] dependencies = getDependencies(soFile); Log.d(SoLoader.TAG, "Loading lib dependencies: " + Arrays.toString(dependencies)); for (String dependency : dependencies) { if (dependency.startsWith("/")) { continue; } SoLoader.loadLibraryBySoName( dependency, loadFlags | LOAD_FLAG_ALLOW_IMPLICIT_PROVISION, threadPolicy); } } private static String[] getDependencies(File soFile) throws IOException { if (SoLoader.SYSTRACE_LIBRARY_LOADING) { Api18TraceUtils.beginTraceSection("SoLoader.getElfDependencies[", soFile.getName(), "]"); } try { return MinElf.extract_DT_NEEDED(soFile); } finally { if (SoLoader.SYSTRACE_LIBRARY_LOADING) { Api18TraceUtils.endSection(); } } } @Override @Nullable public File unpackLibrary(String soName) throws IOException { File soFile = new File(soDirectory, soName); if (soFile.exists()) { return soFile; } return null; } @Override public void addToLdLibraryPath(Collection<String> paths) { paths.add(soDirectory.getAbsolutePath()); } @Override public String toString() { String path; try { path = String.valueOf(soDirectory.getCanonicalPath()); } catch (IOException e) { path = soDirectory.getName(); } return new StringBuilder() .append(getClass().getName()) .append("[root = ") .append(path) .append(" flags = ") .append(flags) .append(']') .toString(); } }