Resolve dex2oat parameters for Android 10+ (#1803)

This commit is contained in:
Nullptr 2022-04-02 13:13:22 +08:00 committed by GitHub
parent 6afa054b1c
commit 2f9e0e07b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 443 additions and 3 deletions

View File

@ -0,0 +1,152 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2022 LSPosed Contributors
*/
package org.lsposed.lspd.service;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.SELinux;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Locale;
public class Dex2OatService {
private static final String TAG = "Dex2OatService";
private static final String DEX2OAT_32 = "/apex/com.android.art/bin/dex2oat32";
private static final String DEX2OAT_64 = "/apex/com.android.art/bin/dex2oat64";
private Thread thread = null;
public void start() {
try {
var devPath = Files.readAllLines(Paths.get("/data/adb/lspd/dev_path")).get(0);
Log.d(TAG, "dev path: " + devPath);
daemon(devPath);
} catch (IOException e) {
Log.e(TAG, "dex2oat daemon failed to start", e);
}
}
public boolean isAlive() {
return thread.isAlive();
}
private void daemon(String devPath) {
thread = new Thread(() -> {
try {
Log.i(TAG, "dex2oat daemon start");
if (setSocketCreateContext("u:r:dex2oat:s0")) {
Log.d(TAG, "set socket context to u:r:dex2oat:s0");
} else {
Log.e(TAG, "failed to set socket context");
}
var sockPath = devPath + "/dex2oat.sock";
var serverSocket = new LocalSocket(LocalSocket.SOCKET_STREAM);
serverSocket.bind(new LocalSocketAddress(sockPath, LocalSocketAddress.Namespace.FILESYSTEM));
var server = new LocalServerSocket(serverSocket.getFileDescriptor());
SELinux.setFileContext(sockPath, "u:object_r:magisk_file:s0");
FileDescriptor stockFd32 = null, stockFd64 = null;
if (new File(DEX2OAT_32).exists()) stockFd32 = Os.open(DEX2OAT_32, OsConstants.O_RDONLY, 0);
if (new File(DEX2OAT_64).exists()) stockFd64 = Os.open(DEX2OAT_64, OsConstants.O_RDONLY, 0);
while (true) {
var client = server.accept();
try (var is = client.getInputStream();
var os = client.getOutputStream()) {
var lp = is.read();
if (lp == 32) client.setFileDescriptorsForSend(new FileDescriptor[]{stockFd32});
else client.setFileDescriptorsForSend(new FileDescriptor[]{stockFd64});
os.write(1);
Log.d(TAG, "sent fd" + lp);
}
}
} catch (Exception e) {
Log.e(TAG, "dex2oat daemon crashed", e);
}
});
thread.start();
}
private boolean setSocketCreateContext(String context) {
FileDescriptor fd = null;
try {
fd = Os.open("/proc/thread-self/attr/sockcreate", OsConstants.O_RDWR, 0);
} catch (ErrnoException e) {
if (e.errno == OsConstants.ENOENT) {
int tid = Os.gettid();
try {
fd = Os.open(String.format(Locale.ENGLISH, "/proc/self/task/%d/attr/sockcreate", tid), OsConstants.O_RDWR, 0);
} catch (ErrnoException ignored) {
}
}
}
if (fd == null) {
return false;
}
byte[] bytes;
int length;
int remaining;
if (!TextUtils.isEmpty(context)) {
byte[] stringBytes = context.getBytes();
bytes = new byte[stringBytes.length + 1];
System.arraycopy(stringBytes, 0, bytes, 0, stringBytes.length);
bytes[stringBytes.length] = '\0';
length = bytes.length;
remaining = bytes.length;
} else {
bytes = null;
length = 0;
remaining = 0;
}
do {
try {
remaining -= Os.write(fd, bytes, length - remaining, remaining);
if (remaining <= 0) {
break;
}
} catch (ErrnoException e) {
break;
} catch (InterruptedIOException e) {
remaining -= e.bytesTransferred;
}
} while (true);
try {
Os.close(fd);
} catch (ErrnoException e) {
Log.w(TAG, Log.getStackTraceString(e));
}
return true;
}
}

View File

@ -688,8 +688,12 @@ public class LSPManagerService extends ILSPManagerService.Stub {
@Override
public boolean dex2oatFlagsLoaded() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return ServiceManager.getDex2OatService().isAlive();
} else {
return SystemProperties.get(PROP_NAME).contains(PROP_VALUE);
}
}
@Override
public void setHiddenIcon(boolean hide) {

View File

@ -22,6 +22,7 @@ package org.lsposed.lspd.service;
import android.app.ActivityThread;
import android.content.Context;
import android.ddm.DdmHandleAppName;
import android.os.Build;
import android.os.IBinder;
import android.os.IServiceManager;
import android.os.Looper;
@ -49,9 +50,14 @@ public class ServiceManager {
private static LSPManagerService managerService = null;
private static LSPSystemServerService systemServerService = null;
private static LogcatService logcatService = null;
private static Dex2OatService dex2OatService = null;
private static final ExecutorService executorService = Executors.newCachedThreadPool();
public static Dex2OatService getDex2OatService() {
return dex2OatService;
}
public static ExecutorService getExecutorService() {
return executorService;
}
@ -98,6 +104,11 @@ public class ServiceManager {
logcatService = new LogcatService();
logcatService.start();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
dex2OatService = new Dex2OatService();
dex2OatService.start();
}
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
Looper.prepareMainLooper();

2
dex2oat/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/build
/.cxx

42
dex2oat/build.gradle.kts Normal file
View File

@ -0,0 +1,42 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2022 LSPosed Contributors
*/
plugins {
id("com.android.library")
}
android {
namespace = "org.lsposed.dex2oat"
buildFeatures {
androidResources = false
buildConfig = false
prefab = true
}
defaultConfig {
minSdk = 29
}
externalNativeBuild {
cmake {
path("src/main/cpp/CMakeLists.txt")
}
}
}

View File

@ -0,0 +1,19 @@
project(dex2oat)
cmake_minimum_required(VERSION 3.4.1)
add_subdirectory(${EXTERNAL_ROOT} external)
add_executable(dex2oat dex2oat.c)
target_link_libraries(dex2oat cxx log)
if (DEFINED DEBUG_SYMBOLS_PATH)
message(STATUS "Debug symbols will be placed at ${DEBUG_SYMBOLS_PATH}")
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}
COMMAND ${CMAKE_OBJCOPY} --only-keep-debug $<TARGET_FILE:${PROJECT_NAME}>
${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug
COMMAND ${CMAKE_STRIP} --strip-all $<TARGET_FILE:${PROJECT_NAME}>
COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug
$<TARGET_FILE:${PROJECT_NAME}>)
endif()

View File

@ -0,0 +1,114 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2022 LSPosed Contributors
*/
//
// Created by Nullptr on 2022/4/1.
//
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "logging.h"
#if defined(__LP64__)
# define LP_SELECT(lp32, lp64) lp64
#else
# define LP_SELECT(lp32, lp64) lp32
#endif
char kTmpDir[] = "placeholder_/dev/0123456789abcdef";
static ssize_t xrecvmsg(int sockfd, struct msghdr *msg, int flags) {
int rec = recvmsg(sockfd, msg, flags);
if (rec < 0) {
PLOGE("recvmsg");
}
return rec;
}
static void *recv_fds(int sockfd, char *cmsgbuf, size_t bufsz, int cnt) {
struct iovec iov = {
.iov_base = &cnt,
.iov_len = sizeof(cnt),
};
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = cmsgbuf,
.msg_controllen = bufsz
};
xrecvmsg(sockfd, &msg, MSG_WAITALL);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
if (msg.msg_controllen != bufsz ||
cmsg == NULL ||
cmsg->cmsg_len != CMSG_LEN(sizeof(int) * cnt) ||
cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS) {
return NULL;
}
return CMSG_DATA(cmsg);
}
static void write_int(int fd, int val) {
if (fd < 0) return;
write(fd, &val, sizeof(val));
}
static int recv_fd(int sockfd) {
char cmsgbuf[CMSG_SPACE(sizeof(int))];
void *data = recv_fds(sockfd, cmsgbuf, sizeof(cmsgbuf), 1);
if (data == NULL)
return -1;
int result;
memcpy(&result, data, sizeof(int));
return result;
}
int main(int argc, char **argv) {
LOGI("dex2oat wrapper");
struct sockaddr_un sock;
sock.sun_family = AF_UNIX;
snprintf(sock.sun_path, sizeof(sock.sun_path), "%s/dex2oat.sock", kTmpDir + 12);
int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (-1 == connect(sock_fd, (struct sockaddr *) &sock, sizeof(sock))) {
PLOGE("failed to connect to %s", sock.sun_path);
return 1;
}
write_int(sock_fd, LP_SELECT(32, 64));
int stock_fd = recv_fd(sock_fd);
close(sock_fd);
LOGD("sock: %s %d", sock.sun_path, stock_fd);
const char *new_argv[argc + 2];
for (int i = 0; i < argc; i++) new_argv[i] = argv[i];
new_argv[argc] = "--inline-max-code-units=0";
new_argv[argc + 1] = NULL;
fexecve(stock_fd, (char **) new_argv, environ);
PLOGE("fexecve failed");
return 2;
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <android/log.h>
#ifndef LOG_TAG
#define LOG_TAG "dex2oat-wrapper"
#endif
#ifdef LOG_DISABLED
#define LOGD(...)
#define LOGV(...)
#define LOGI(...)
#define LOGW(...)
#define LOGE(...)
#else
#ifndef NDEBUG
#define LOGD(fmt, ...) \
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, \
"%s:%d#%s" \
": " fmt, \
__FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(, ) __VA_ARGS__)
#define LOGV(fmt, ...) \
__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, \
"%s:%d#%s" \
": " fmt, \
__FILE_NAME__, __LINE__, __PRETTY_FUNCTION__ __VA_OPT__(, ) __VA_ARGS__)
#else
#define LOGD(...)
#define LOGV(...)
#endif
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, __VA_ARGS__)
#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno))
#endif

View File

@ -143,7 +143,8 @@ fun afterEval() = android.applicationVariants.forEach { variant ->
dependsOn(
"assemble$variantCapped",
":app:package$buildTypeCapped",
":daemon:package$buildTypeCapped"
":daemon:package$buildTypeCapped",
":dex2oat:merge${buildTypeCapped}NativeLibs"
)
into(magiskDir)
from("${rootProject.projectDir}/README.md")
@ -205,6 +206,11 @@ fun afterEval() = android.applicationVariants.forEach { variant ->
include("**/libdaemon.so")
}
}
into("bin") {
from("${project(":dex2oat").buildDir}/intermediates/cmake/$buildTypeLowered/obj") {
include("**/dex2oat")
}
}
val dexOutPath = if (buildTypeLowered == "release")
"$buildDir/intermediates/dex/$variantCapped/minify${variantCapped}WithR8" else
"$buildDir/intermediates/dex/$variantCapped/mergeDex$variantCapped"

View File

@ -79,7 +79,6 @@ fi
ui_print "- Extracting module files"
extract "$ZIPFILE" 'module.prop' "$MODPATH"
extract "$ZIPFILE" 'system.prop' "$MODPATH"
extract "$ZIPFILE" 'post-fs-data.sh' "$MODPATH"
extract "$ZIPFILE" 'service.sh' "$MODPATH"
extract "$ZIPFILE" 'uninstall.sh' "$MODPATH"
@ -89,6 +88,31 @@ extract "$ZIPFILE" 'daemon' "$MODPATH"
rm -f /data/adb/lspd/manager.apk
extract "$ZIPFILE" 'manager.apk' '/data/adb/lspd'
if [ "$API" -ge 29 ]; then
ui_print "- Extracting dex2oat binaries"
mkdir "$MODPATH/bin"
if [ "$ARCH" = "arm" ] || [ "$ARCH" = "arm64" ]; then
extract "$ZIPFILE" "bin/armeabi-v7a/dex2oat" "$MODPATH/bin" true
mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat32"
if [ "$IS64BIT" = true ]; then
extract "$ZIPFILE" "bin/arm64-v8a/dex2oat" "$MODPATH/bin" true
mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat64"
fi
elif [ "$ARCH" == "x86" ] || [ "$ARCH" == "x64" ]; then
extract "$ZIPFILE" "bin/x86/dex2oat" "$MODPATH/bin" true
mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat32"
if [ "$IS64BIT" = true ]; then
extract "$ZIPFILE" "bin/x86_64/dex2oat" "$MODPATH/bin" true
mv "$MODPATH/bin/dex2oat" "$MODPATH/bin/dex2oat64"
fi
fi
else
extract "$ZIPFILE" 'system.prop' "$MODPATH"
fi
ui_print "- Extracting daemon libraries"
if [ "$ARCH" = "arm" ] ; then
extract "$ZIPFILE" 'lib/armeabi-v7a/libdaemon.so' "$MODPATH" true
@ -161,6 +185,7 @@ elif [ "$FLAVOR" == "riru" ]; then
fi
set_perm_recursive "$MODPATH" 0 0 0755 0644
set_perm_recursive "$MODPATH/bin" 0 0 0755 0755 u:object_r:dex2oat_exec:s0
chmod 0744 "$MODPATH/daemon"
if [ "$(grep_prop ro.maple.enable)" == "1" ] && [ "$FLAVOR" == "zygisk" ]; then

View File

@ -1,6 +1,7 @@
#!/system/bin/sh
dir=${0%/*}
magiskPath=$(magisk --path)
tmpLspdApk="/data/local/tmp/daemon.apk"
debug=@DEBUG@
flavor=@FLAVOR@
@ -38,5 +39,16 @@ if [ ! -S "/dev/socket/zygote" ]; then
done
fi
$debug && log -p d -t "LSPosed" "start $flavor daemon $*"
if [ "$(getprop ro.build.version.sdk)" -ge 29 ]; then
umount /apex/com.android.art/bin/dex2oat*
mkdir "$magiskPath/dex2oat"
cp -p /apex/com.android.art/bin/dex2oat32 "$magiskPath/dex2oat/dex2oat32"
cp -p /apex/com.android.art/bin/dex2oat64 "$magiskPath/dex2oat/dex2oat64"
chcon -R u:object_r:magisk_file:s0 "$magiskPath/dex2oat"
mount --bind "$magiskPath/dex2oat/dex2oat32" /apex/com.android.art/bin/dex2oat32
mount --bind "$magiskPath/dex2oat/dex2oat64" /apex/com.android.art/bin/dex2oat64
fi
# shellcheck disable=SC2086
exec /system/bin/app_process $java_options /system/bin --nice-name=lspd org.lsposed.lspd.Main "$@" >/dev/null 2>&1

View File

@ -18,7 +18,23 @@
#
MODDIR=${0%/*}
MODNAME=${MODDIR##*/}
MAGISK_PATH=$(magisk --path)
rm -f "/data/local/tmp/daemon.apk"
cd "$MODDIR"
if [ "$(getprop ro.build.version.sdk)" -ge 29 ]; then
TMP=$($RANDOM | md5sum | head -c 16)
while [ -d "/dev/$TMP" ]; do
TMP=$($RANDOM | md5sum | head -c 16)
done
mkdir "/dev/$TMP"
echo "/dev/$TMP" > "/data/adb/lspd/dev_path"
sed -i "s/placeholder_\/dev\/................/placeholder_\/dev\/$TMP/" "$MODDIR/bin/dex2oat32"
sed -i "s/placeholder_\/dev\/................/placeholder_\/dev\/$TMP/" "$MODDIR/bin/dex2oat64"
mount --bind "$MAGISK_PATH/.magisk/modules/$MODNAME/bin/dex2oat32" "/apex/com.android.art/bin/dex2oat32"
mount --bind "$MAGISK_PATH/.magisk/modules/$MODNAME/bin/dex2oat64" "/apex/com.android.art/bin/dex2oat64"
fi
unshare -m sh -c "$MODDIR/daemon &"

View File

@ -29,6 +29,7 @@ include(
":app",
":core",
":daemon",
":dex2oat",
":hiddenapi:stubs",
":hiddenapi:bridge",
":magisk-loader",