Skip to content

Commit

Permalink
Recover from unpacked DSOs not found on disk
Browse files Browse the repository at this point in the history
Reviewed By: simpleton

Differential Revision: D61873053

fbshipit-source-id: e337d15104ec6cd77f3dfdbe5ae45c266bac23de
  • Loading branch information
adicatana authored and facebook-github-bot committed Aug 29, 2024
1 parent 217b0f5 commit 83be14a
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 15 deletions.
7 changes: 7 additions & 0 deletions java/com/facebook/soloader/BackupSoSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ public boolean peekAndPrepareSoSource(String soName, int prepareFlags) throws IO
return true;
}

@Override
public Dso[] getDsosBaseApk() throws IOException {
try (Unpacker u = mZipSources.get(0).makeUnpacker()) {
return u.getDsos();
}
}

protected class ApkUnpacker extends Unpacker {

@Override
Expand Down
2 changes: 1 addition & 1 deletion java/com/facebook/soloader/DirectorySoSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ protected int loadLibraryFrom(

@Override
@Nullable
protected File getSoFileByName(String soName) throws IOException {
public File getSoFileByName(String soName) throws IOException {
File soFile = new File(soDirectory, soName);
if (soFile.exists()) {
return soFile;
Expand Down
2 changes: 1 addition & 1 deletion java/com/facebook/soloader/SoLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ private static boolean loadLibraryBySoName(
@SuppressLint("CatchGeneralException")
private static RecoveryStrategy recover(
String soName, UnsatisfiedLinkError e, @Nullable RecoveryStrategy recovery) {
LogUtil.w(TAG, "Starting recovery for " + soName, e);
LogUtil.w(TAG, "Running a recovery step for " + soName + " due to " + e.toString());
sSoSourcesLock.writeLock().lock();
try {
if (recovery == null) {
Expand Down
6 changes: 6 additions & 0 deletions java/com/facebook/soloader/UnpackingSoSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ public String[] getSoSourceAbis() {
return mAbis;
}

public Dso[] getDsosBaseApk() throws IOException {
try (UnpackingSoSource.Unpacker unpacker = makeUnpacker()) {
return unpacker.getDsos();
}
}

public void setSoSourceAbis(final String[] abis) {
mAbis = abis;
}
Expand Down
110 changes: 110 additions & 0 deletions java/com/facebook/soloader/recovery/CheckOnDiskStateDataApp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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.recovery;

import android.content.Context;
import com.facebook.soloader.BackupSoSource;
import com.facebook.soloader.DirectorySoSource;
import com.facebook.soloader.LogUtil;
import com.facebook.soloader.SoLoader;
import com.facebook.soloader.SoLoaderULError;
import com.facebook.soloader.SoSource;
import com.facebook.soloader.UnpackingSoSource.Dso;
import java.io.File;
import java.util.ArrayList;

public class CheckOnDiskStateDataApp implements RecoveryStrategy {

private final Context mContext;

public CheckOnDiskStateDataApp(Context context) {
mContext = context;
}

@Override
public boolean recover(UnsatisfiedLinkError error, SoSource[] soSources) {
if (!(error instanceof SoLoaderULError)) {
// Only recover from SoLoaderULE errors
return false;
}

LogUtil.e(SoLoader.TAG, "Checking /data/app missing libraries.");

File nativeLibStandardDir = new File(mContext.getApplicationInfo().nativeLibraryDir);
if (!nativeLibStandardDir.exists()) {
LogUtil.e(
SoLoader.TAG,
"Native library directory "
+ nativeLibStandardDir
+ " does not exist, exiting /data/app recovery.");
return false;
}

ArrayList<String> missingLibs = new ArrayList<>();
for (SoSource soSource : soSources) {
if (!(soSource instanceof BackupSoSource)) {
continue;
}
BackupSoSource uss = (BackupSoSource) soSource;
try {
Dso[] dsosFromArchive = uss.getDsosBaseApk();
for (Dso dso : dsosFromArchive) {
File soFile = new File(nativeLibStandardDir, dso.name);
if (soFile.exists()) {
continue;
}
missingLibs.add(dso.name);
}

if (missingLibs.isEmpty()) {
LogUtil.e(SoLoader.TAG, "No libraries missing from " + nativeLibStandardDir);
return false;
}

LogUtil.e(
SoLoader.TAG,
"Missing libraries from "
+ nativeLibStandardDir
+ ": "
+ missingLibs.toString()
+ ", will run prepare on tbe backup so source");
uss.prepare(0);
break;
} catch (Exception e) {
LogUtil.e(
SoLoader.TAG, "Encountered an exception while recovering from /data/app failure ", e);
return false;
}
}

for (SoSource soSource : soSources) {
if (!(soSource instanceof DirectorySoSource)) {
continue;
}
if (soSource instanceof BackupSoSource) {
continue;
}
DirectorySoSource directorySoSource = (DirectorySoSource) soSource;
// We need to explicitly resolve dependencies, as dlopen() cannot do
// so for dependencies at non-standard locations.
directorySoSource.setExplicitDependencyResolution();
}

LogUtil.e(SoLoader.TAG, "Successfully recovered from /data/app disk failure.");
return true;
}
}
75 changes: 75 additions & 0 deletions java/com/facebook/soloader/recovery/CheckOnDiskStateDataData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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.recovery;

import com.facebook.soloader.BackupSoSource;
import com.facebook.soloader.LogUtil;
import com.facebook.soloader.SoLoader;
import com.facebook.soloader.SoLoaderULError;
import com.facebook.soloader.SoSource;
import com.facebook.soloader.UnpackingSoSource;
import com.facebook.soloader.UnpackingSoSource.Dso;

public class CheckOnDiskStateDataData implements RecoveryStrategy {
@Override
public boolean recover(UnsatisfiedLinkError error, SoSource[] soSources) {
if (!(error instanceof SoLoaderULError)) {
// Only recover from SoLoaderULE errors
return false;
}

LogUtil.e(SoLoader.TAG, "Checking /data/data missing libraries.");

boolean recovered = false;
for (SoSource soSource : soSources) {
if (!(soSource instanceof UnpackingSoSource)) {
continue;
}
if (soSource instanceof BackupSoSource) {
continue;
}
UnpackingSoSource uss = (UnpackingSoSource) soSource;
try {
Dso[] dsosFromArchive = uss.getDsosBaseApk();
for (Dso dso : dsosFromArchive) {
if (uss.getSoFileByName(dso.name) == null) {
LogUtil.e(
SoLoader.TAG,
"Missing " + dso.name + " from " + uss.getName() + ", will force prepare.");
uss.prepare(SoSource.PREPARE_FLAG_FORCE_REFRESH);
recovered = true;
break;
}
}
} catch (Exception e) {
LogUtil.e(
SoLoader.TAG, "Encountered an exception while recovering from /data/data failure ", e);
return false;
}
}

if (recovered) {
LogUtil.e(SoLoader.TAG, "Successfully recovered from /data/data disk failure.");
return true;
}

LogUtil.e(
SoLoader.TAG,
"No libraries missing from unpacking so paths while recovering /data/data failure");
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ public RecoveryStrategy get() {
new DetectDataAppMove(mContext, mBaseApkPathHistory),
new CheckBaseApkExists(mContext, mBaseApkPathHistory),
new WaitForAsyncInit(),
new CheckOnDiskStateDataApp(mContext),
new ReunpackBackupSoSources(mRecoveryFlags),
new CheckOnDiskStateDataData(),
new ReunpackNonBackupSoSources(),
new WaitForAsyncInit());
}
Expand Down
13 changes: 0 additions & 13 deletions java/com/facebook/soloader/recovery/WaitForAsyncInit.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,11 @@
import com.facebook.soloader.AsyncInitSoSource;
import com.facebook.soloader.LogUtil;
import com.facebook.soloader.SoLoader;
import com.facebook.soloader.SoLoaderULError;
import com.facebook.soloader.SoSource;

public class WaitForAsyncInit implements RecoveryStrategy {
@Override
public boolean recover(UnsatisfiedLinkError e, SoSource[] soSources) {
String soName = null;
if (e instanceof SoLoaderULError) {
SoLoaderULError err = (SoLoaderULError) e;
soName = err.getSoName();
}

LogUtil.e(
SoLoader.TAG,
"Waiting on SoSources due to "
+ e
+ ((soName == null) ? "" : (", retrying for specific library " + soName)));

for (SoSource soSource : soSources) {
if (soSource instanceof AsyncInitSoSource) {
AsyncInitSoSource source = (AsyncInitSoSource) soSource;
Expand Down

0 comments on commit 83be14a

Please sign in to comment.