Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dalvik transformation using wrong invoke- opcode

I'm running into a problem with the dalvik dex converter and the opcode it is using to invoke methods. Basically I have a private final method defined in my class, and when calling it, instead of generating the invoke-direct opcode, dx is generating invoke-super. Because it's a private method, the method doesn't exist on the super class, so I get a VFY violation on the device. I was able to track down the exact scenario that triggers this, and it appears to happen when:

  1. instrumenting the classes with JaCoCo, and
  2. classes compiled with --target 1.6

If those two conditions are met, the resulting dex class has invoke-super instead of invoke-direct. If I disable JaCoCo OR if I compile with --target 1.5, it uses the correct invoke-direct opcode.

In looking at the javap disassembled class code, I can see what causes dx to assume super instead of direct:

Not instrumented, compiled for 1.6:

$ javap -d com.example.ClassName | grep waitForConnectivity
159:    invokespecial   #115; //Method waitForConnectivity:()V
$ dexdump -d classes.dex | grep waitForConnectivity
147ad8: 7010 6042 0200           |001e: invoke-direct {v2}, Lcom/example/ClassName;.waitForConnectivity:()V // method@4260

Instrumented, compiled for 1.5 (--target 1.5):

$ javap -d com.example.ClassName | grep waitForConnectivity
235:    invokespecial   #115; //Method waitForConnectivity:()V
$ dexdump -d classes.dex | grep waitForConnectivity
149d4c: 7010 9242 0400           |0018: invoke-direct {v4}, Lcom/example/ClassName;.waitForConnectivity:()V // method@4292

Instrumented, compiled for 1.6:

$ javap -d com.example.ClassName | grep waitForConnectivity
235:    invokespecial   #115; //Method com/example/ClassName.waitForConnectivity:()V
$ dexdump -d classes.dex | grep waitForConnectivity
149d4c: 6f10 9242 0400           |0018: invoke-super {v4}, Lcom/example/ClassName;.waitForConnectivity:()V // method@4292

So the difference is that the compiled .class file has compiled java bytecode that references the fully qualified class name of the this class (notice "//Method waitForConnectivity:()V" vs "//Method com/example/ClassName.waitForConnectivity:()V"). It appears that dx automatically assumes that if the method name is fully qualified, it must use invoke-super, but if it's not qualified, it uses invoke-direct.

My questions are:

  1. Is this a bug in Android's dx, or a bug in JaCoCo?
  2. How can I avoid this, so that the JaCoCo-instrumented classes can work properly in my automated test builds?

My current workaround is to have a Maven "jacoco" profile, and in there I override the ${java.version} property to change it from the default "1.6" to "1.5". Is there any better solution?

like image 962
Joe Avatar asked Nov 02 '22 18:11

Joe


1 Answers

One of the rules that dx uses to determine whether to emit invoke-super or invoke-direct is whether it believes the method call is being made on the same class as the one doing the calling. See RopperMachine.java in the source, around line 912, included here for reference:

        case ByteOps.INVOKESPECIAL: {
            /*
             * Determine whether the opcode should be
             * INVOKE_DIRECT or INVOKE_SUPER. See vmspec-2 section 6
             * on "invokespecial" as well as section 4.8.2 (7th
             * bullet point) for the gory details.
             */
            CstMethodRef ref = (CstMethodRef) cst;
            if (ref.isInstanceInit() ||
                (ref.getDefiningClass() == method.getDefiningClass()) ||
                !method.getAccSuper()) {
                return RegOps.INVOKE_DIRECT;
            }
            return RegOps.INVOKE_SUPER;

It would be interesting to see a more complete dump of the class that's getting misconverted. I think it's probably the case that what you're seeing from javap isn't quite a complete picture of reality. Note that dx itself has a .class file dumper built into it which provides a lot more detail than javap. Invoke it as dx --dump --bytes path/to/Name.class.

like image 150
danfuzz Avatar answered Nov 09 '22 07:11

danfuzz