Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8af77416 authored by Makoto Onuki's avatar Makoto Onuki Committed by Android (Google) Code Review
Browse files

Merge "[HostStubGen] Add --package-redirect" into main

parents ccc2dd02 8bb2b4a9
Loading
Loading
Loading
Loading
+31 −22
Original line number Original line Diff line number Diff line
@@ -17,9 +17,9 @@ package com.android.hoststubgen


import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.filters.AnnotationBasedFilter
import com.android.hoststubgen.filters.AnnotationBasedFilter
import com.android.hoststubgen.filters.DefaultHookInjectingFilter
import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter
import com.android.hoststubgen.filters.ClassWidePolicyPropagatingFilter
import com.android.hoststubgen.filters.ConstantFilter
import com.android.hoststubgen.filters.ConstantFilter
import com.android.hoststubgen.filters.DefaultHookInjectingFilter
import com.android.hoststubgen.filters.FilterPolicy
import com.android.hoststubgen.filters.FilterPolicy
import com.android.hoststubgen.filters.ImplicitOutputFilter
import com.android.hoststubgen.filters.ImplicitOutputFilter
import com.android.hoststubgen.filters.KeepAllClassesFilter
import com.android.hoststubgen.filters.KeepAllClassesFilter
@@ -28,6 +28,7 @@ import com.android.hoststubgen.filters.StubIntersectingFilter
import com.android.hoststubgen.filters.createFilterFromTextPolicyFile
import com.android.hoststubgen.filters.createFilterFromTextPolicyFile
import com.android.hoststubgen.filters.printAsTextPolicy
import com.android.hoststubgen.filters.printAsTextPolicy
import com.android.hoststubgen.visitors.BaseAdapter
import com.android.hoststubgen.visitors.BaseAdapter
import com.android.hoststubgen.visitors.PackageRedirectRemapper
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.ClassWriter
@@ -238,6 +239,8 @@ class HostStubGen(val options: HostStubGenOptions) {


        val start = System.currentTimeMillis()
        val start = System.currentTimeMillis()


        val packageRedirector = PackageRedirectRemapper(options.packageRedirects)

        log.withIndent {
        log.withIndent {
            // Open the input jar file and process each entry.
            // Open the input jar file and process each entry.
            ZipFile(inJar).use { inZip ->
            ZipFile(inJar).use { inZip ->
@@ -247,7 +250,7 @@ class HostStubGen(val options: HostStubGenOptions) {
                        while (inEntries.hasMoreElements()) {
                        while (inEntries.hasMoreElements()) {
                            val entry = inEntries.nextElement()
                            val entry = inEntries.nextElement()
                            convertSingleEntry(inZip, entry, stubOutStream, implOutStream,
                            convertSingleEntry(inZip, entry, stubOutStream, implOutStream,
                                    filter, enableChecker, classes, errors)
                                    filter, packageRedirector, enableChecker, classes, errors)
                        }
                        }
                        log.i("Converted all entries.")
                        log.i("Converted all entries.")
                    }
                    }
@@ -269,6 +272,7 @@ class HostStubGen(val options: HostStubGenOptions) {
            stubOutStream: ZipOutputStream,
            stubOutStream: ZipOutputStream,
            implOutStream: ZipOutputStream,
            implOutStream: ZipOutputStream,
            filter: OutputFilter,
            filter: OutputFilter,
            packageRedirector: PackageRedirectRemapper,
            enableChecker: Boolean,
            enableChecker: Boolean,
            classes: ClassNodes,
            classes: ClassNodes,
            errors: HostStubGenErrors,
            errors: HostStubGenErrors,
@@ -285,7 +289,7 @@ class HostStubGen(val options: HostStubGenOptions) {
            // If it's a class, convert it.
            // If it's a class, convert it.
            if (name.endsWith(".class")) {
            if (name.endsWith(".class")) {
                processSingleClass(inZip, entry, stubOutStream, implOutStream, filter,
                processSingleClass(inZip, entry, stubOutStream, implOutStream, filter,
                        enableChecker, classes, errors)
                        packageRedirector, enableChecker, classes, errors)
                return
                return
            }
            }


@@ -335,37 +339,40 @@ class HostStubGen(val options: HostStubGenOptions) {
            stubOutStream: ZipOutputStream,
            stubOutStream: ZipOutputStream,
            implOutStream: ZipOutputStream,
            implOutStream: ZipOutputStream,
            filter: OutputFilter,
            filter: OutputFilter,
            packageRedirector: PackageRedirectRemapper,
            enableChecker: Boolean,
            enableChecker: Boolean,
            classes: ClassNodes,
            classes: ClassNodes,
            errors: HostStubGenErrors,
            errors: HostStubGenErrors,
            ) {
            ) {
        val className = entry.name.replaceFirst("\\.class$".toRegex(), "")
        val classInternalName = entry.name.replaceFirst("\\.class$".toRegex(), "")
        val classPolicy = filter.getPolicyForClass(className)
        val classPolicy = filter.getPolicyForClass(classInternalName)
        if (classPolicy.policy == FilterPolicy.Remove) {
        if (classPolicy.policy == FilterPolicy.Remove) {
            log.d("Removing class: %s %s", className, classPolicy)
            log.d("Removing class: %s %s", classInternalName, classPolicy)
            return
            return
        }
        }
        // Generate stub first.
        // Generate stub first.
        if (classPolicy.policy.needsInStub) {
        if (classPolicy.policy.needsInStub) {
            log.v("Creating stub class: %s Policy: %s", className, classPolicy)
            log.v("Creating stub class: %s Policy: %s", classInternalName, classPolicy)
            log.withIndent {
            log.withIndent {
                BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
                BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
                    val newEntry = ZipEntry(entry.name)
                    val newEntry = ZipEntry(entry.name)
                    stubOutStream.putNextEntry(newEntry)
                    stubOutStream.putNextEntry(newEntry)
                    convertClass(/*forImpl=*/false, bis, stubOutStream, filter, enableChecker,
                    convertClass(classInternalName, /*forImpl=*/false, bis,
                            classes, errors)
                            stubOutStream, filter, packageRedirector, enableChecker, classes,
                            errors)
                    stubOutStream.closeEntry()
                    stubOutStream.closeEntry()
                }
                }
            }
            }
        }
        }
        log.v("Creating impl class: %s Policy: %s", className, classPolicy)
        log.v("Creating impl class: %s Policy: %s", classInternalName, classPolicy)
        if (classPolicy.policy.needsInImpl) {
        if (classPolicy.policy.needsInImpl) {
            log.withIndent {
            log.withIndent {
                BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
                BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
                    val newEntry = ZipEntry(entry.name)
                    val newEntry = ZipEntry(entry.name)
                    implOutStream.putNextEntry(newEntry)
                    implOutStream.putNextEntry(newEntry)
                    convertClass(/*forImpl=*/true, bis, implOutStream, filter, enableChecker,
                    convertClass(classInternalName, /*forImpl=*/true, bis,
                            classes, errors)
                            implOutStream, filter, packageRedirector, enableChecker, classes,
                            errors)
                    implOutStream.closeEntry()
                    implOutStream.closeEntry()
                }
                }
            }
            }
@@ -376,10 +383,12 @@ class HostStubGen(val options: HostStubGenOptions) {
     * Convert a single class to either "stub" or "impl".
     * Convert a single class to either "stub" or "impl".
     */
     */
    private fun convertClass(
    private fun convertClass(
            classInternalName: String,
            forImpl: Boolean,
            forImpl: Boolean,
            input: InputStream,
            input: InputStream,
            out: OutputStream,
            out: OutputStream,
            filter: OutputFilter,
            filter: OutputFilter,
            packageRedirector: PackageRedirectRemapper,
            enableChecker: Boolean,
            enableChecker: Boolean,
            classes: ClassNodes,
            classes: ClassNodes,
            errors: HostStubGenErrors,
            errors: HostStubGenErrors,
@@ -398,11 +407,11 @@ class HostStubGen(val options: HostStubGenOptions) {
        val visitorOptions = BaseAdapter.Options(
        val visitorOptions = BaseAdapter.Options(
                enablePreTrace = options.enablePreTrace,
                enablePreTrace = options.enablePreTrace,
                enablePostTrace = options.enablePostTrace,
                enablePostTrace = options.enablePostTrace,
                enableMethodLogging = options.enablePreTrace,
                enableNonStubMethodCallDetection = options.enableNonStubMethodCallDetection,
                enableNonStubMethodCallDetection = options.enableNonStubMethodCallDetection,
                errors = errors,
                errors = errors,
        )
        )
        outVisitor = BaseAdapter.getVisitor(classes, outVisitor, filter, forImpl, visitorOptions)
        outVisitor = BaseAdapter.getVisitor(classInternalName, classes, outVisitor, filter,
                packageRedirector, forImpl, visitorOptions)


        cr.accept(outVisitor, ClassReader.EXPAND_FRAMES)
        cr.accept(outVisitor, ClassReader.EXPAND_FRAMES)
        val data = cw.toByteArray()
        val data = cw.toByteArray()
+15 −0
Original line number Original line Diff line number Diff line
@@ -49,6 +49,8 @@ class HostStubGenOptions(
        var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(),
        var classLoadHookAnnotations: MutableSet<String> = mutableSetOf(),
        var stubStaticInitializerAnnotations: MutableSet<String> = mutableSetOf(),
        var stubStaticInitializerAnnotations: MutableSet<String> = mutableSetOf(),


        var packageRedirects: MutableList<Pair<String, String>> = mutableListOf(),

        var defaultClassLoadHook: String? = null,
        var defaultClassLoadHook: String? = null,
        var defaultMethodCallHook: String? = null,
        var defaultMethodCallHook: String? = null,


@@ -78,6 +80,15 @@ class HostStubGenOptions(
            return this
            return this
        }
        }


        private fun parsePackageRedirect(fromColonTo: String): Pair<String, String> {
            val colon = fromColonTo.indexOf(':')
            if ((colon < 1) || (colon + 1 >= fromColonTo.length)) {
                throw ArgumentsException("--package-redirect must be a colon-separated string")
            }
            // TODO check for duplicates
            return Pair(fromColonTo.substring(0, colon), fromColonTo.substring(colon + 1))
        }

        fun parseArgs(args: Array<String>): HostStubGenOptions {
        fun parseArgs(args: Array<String>): HostStubGenOptions {
            val ret = HostStubGenOptions()
            val ret = HostStubGenOptions()


@@ -157,6 +168,9 @@ class HostStubGenOptions(
                        ret.stubStaticInitializerAnnotations +=
                        ret.stubStaticInitializerAnnotations +=
                                ensureUniqueAnnotation(ai.nextArgRequired(arg))
                                ensureUniqueAnnotation(ai.nextArgRequired(arg))


                    "--package-redirect" ->
                        ret.packageRedirects += parsePackageRedirect(ai.nextArgRequired(arg))

                    "--default-class-load-hook" ->
                    "--default-class-load-hook" ->
                        ret.defaultClassLoadHook = ai.nextArgRequired(arg)
                        ret.defaultClassLoadHook = ai.nextArgRequired(arg)


@@ -299,6 +313,7 @@ class HostStubGenOptions(
              substituteAnnotations=$substituteAnnotations,
              substituteAnnotations=$substituteAnnotations,
              nativeSubstituteAnnotations=$nativeSubstituteAnnotations,
              nativeSubstituteAnnotations=$nativeSubstituteAnnotations,
              classLoadHookAnnotations=$classLoadHookAnnotations,
              classLoadHookAnnotations=$classLoadHookAnnotations,
              packageRedirects=$packageRedirects,
              defaultClassLoadHook=$defaultClassLoadHook,
              defaultClassLoadHook=$defaultClassLoadHook,
              defaultMethodCallHook=$defaultMethodCallHook,
              defaultMethodCallHook=$defaultMethodCallHook,
              intersectStubJars=$intersectStubJars,
              intersectStubJars=$intersectStubJars,
+21 −4
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.FieldVisitor
import org.objectweb.asm.FieldVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Opcodes
import org.objectweb.asm.commons.ClassRemapper
import org.objectweb.asm.util.TraceClassVisitor
import org.objectweb.asm.util.TraceClassVisitor
import java.io.PrintWriter
import java.io.PrintWriter


@@ -49,7 +50,6 @@ abstract class BaseAdapter (
            val errors: HostStubGenErrors,
            val errors: HostStubGenErrors,
            val enablePreTrace: Boolean,
            val enablePreTrace: Boolean,
            val enablePostTrace: Boolean,
            val enablePostTrace: Boolean,
            val enableMethodLogging: Boolean,
            val enableNonStubMethodCallDetection: Boolean,
            val enableNonStubMethodCallDetection: Boolean,
            )
            )


@@ -219,9 +219,11 @@ abstract class BaseAdapter (


    companion object {
    companion object {
        fun getVisitor(
        fun getVisitor(
                classInternalName: String,
                classes: ClassNodes,
                classes: ClassNodes,
                nextVisitor: ClassVisitor,
                nextVisitor: ClassVisitor,
                filter: OutputFilter,
                filter: OutputFilter,
                packageRedirector: PackageRedirectRemapper,
                forImpl: Boolean,
                forImpl: Boolean,
                options: Options,
                options: Options,
        ): ClassVisitor {
        ): ClassVisitor {
@@ -229,12 +231,27 @@ abstract class BaseAdapter (


            val verbosePrinter = PrintWriter(log.getVerbosePrintStream())
            val verbosePrinter = PrintWriter(log.getVerbosePrintStream())


            // TODO: This doesn't work yet.

            // Inject TraceClassVisitor for debugging.
            // Inject TraceClassVisitor for debugging.
            if (options.enablePostTrace) {
            if (options.enablePostTrace) {
                next = TraceClassVisitor(next, verbosePrinter)
                next = TraceClassVisitor(next, verbosePrinter)
            }
            }

            // Handle --package-redirect
            if (!packageRedirector.isEmpty) {
                // Don't apply the remapper on redirect-from classes.
                // Otherwise, if the target jar actually contains the "from" classes (which
                // may or may not be the case) they'd be renamed.
                // But we update all references in other places, so, a method call to a "from" class
                // would be replaced with the "to" class. All type references (e.g. variable types)
                // will be updated too.
                if (!packageRedirector.isTarget(classInternalName)) {
                    next = ClassRemapper(next, packageRedirector)
                } else {
                    log.v("Class $classInternalName is a redirect-from class, not applying" +
                            " --package-redirect")
                }
            }

            var ret: ClassVisitor
            var ret: ClassVisitor
            if (forImpl) {
            if (forImpl) {
                ret = ImplGeneratingAdapter(classes, next, filter, options)
                ret = ImplGeneratingAdapter(classes, next, filter, options)
+2 −2
Original line number Original line Diff line number Diff line
@@ -200,7 +200,7 @@ class ImplGeneratingAdapter(
                                access, name, descriptor, signature, exceptions, innerVisitor)
                                access, name, descriptor, signature, exceptions, innerVisitor)
                    }
                    }
                    else -> {
                    else -> {
                        throw RuntimeException("Ignored policy only allowed for void methods");
                        throw RuntimeException("Ignored policy only allowed for void methods")
                    }
                    }
                }
                }
            }
            }
+82 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * 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.android.hoststubgen.visitors

import com.android.hoststubgen.asm.toJvmClassName
import org.objectweb.asm.commons.Remapper

/**
 * A [Remapper] for `--package-redirect`
 */
class PackageRedirectRemapper(
        packageRedirects: List<Pair<String, String>>,
        ) : Remapper() {

    /**
     * Example: `dalvik/` -> `com/android/hostsubgen/substitution/dalvik/`
     */
    private val packageRedirectsWithSlash: List<Pair<String, String>> = packageRedirects.map {
        p -> Pair(p.first.toJvmClassName() + "/", p.second.toJvmClassName() + "/")
    }

    /**
     * Cache.
     * If a class is a redirect-from class, then the "to" class name will be stored as the value.
     * Otherwise, "" will be stored.
     */
    private val cache = mutableMapOf<String, String>()

    /**
     * Return whether any redirect is defined.
     */
    val isEmpty get() = packageRedirectsWithSlash.isEmpty()

    override fun map(internalName: String?): String? {
        if (internalName == null) {
            return null
        }
        val to = mapInner(internalName)
        return to ?: internalName
    }

    /**
     * Internal "map" function. Unlike [map(String)], this method will return null
     * if a class is not a redirect-from class.
     */
    private fun mapInner(internalName: String): String? {
        cache[internalName]?.let {
            return if (it.isEmpty()) null else it
        }

        var ret = ""
        packageRedirectsWithSlash.forEach { fromTo ->
            if (internalName.startsWith(fromTo.first)) {
                ret = fromTo.second + internalName.substring(fromTo.first.length)
            }
        }
        cache.set(internalName, ret)

        return if (ret.isEmpty()) null else ret
    }

    /**
     * Return true if a class is a redirect-from class.
     */
    fun isTarget(internalName: String): Boolean {
        return mapInner(internalName) != null
    }
}
Loading