soot-oss / soot

Soot - A Java optimization framework
GNU Lesser General Public License v2.1
2.86k stars 705 forks source link

A miscalculation when soot transforms .jimple into .class #742

Closed myspringchen closed 4 years ago

myspringchen commented 7 years ago

Hi, I recently used soot to perform some jimple-class transformations. It seems that the next method is not correctly transformed. The Jimple code is correct, while JVM throws a verify error when it loads the transformed code. I thought the exception handling triggers the mis-transformation.

I used the asm-backend.

Thank you.

Yuting

public static int foo2() { java.lang.String r0; int $i0; java.lang.Exception $r1; boolean $z0; java.lang.StringBuffer $r2;

    r0 = new java.lang.String;
    specialinvoke r0.<java.lang.String: void <init>()>();
    if r0==null goto label4;

 label1:
    $i0 = staticinvoke <java.lang.Integer: int parseInt(java.lang.String)>("5");

 label2:
    return $i0;

 label3:
    $r1 := @caughtexception;
    $r2 = new java.lang.StringBuffer;
    specialinvoke $r2.<java.lang.StringBuffer: void <init>(java.lang.String)>("");
    virtualinvoke $r2.<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>(r0);

 label4:
    return 6;

    catch java.lang.Exception from label1 to label2 with label3;
}

public static int foo2(); descriptor: ()I flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=2, args_size=0 0: new #19 // class java/lang/String 3: astore_1 4: aload_1 5: invokespecial #20 // Method java/lang/String."":()V 8: aload_1 9: ifnull 35 12: ldc #22 // String 5 14: invokestatic #28 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I 17: istore_1 18: iload_1 19: ireturn 20: astore_0 21: new #30 // class java/lang/StringBuffer 24: dup 25: ldc #32 // String 27: invokespecial #35 // Method java/lang/StringBuffer."":(Ljava/lang/String;)V 30: aload_1 31: invokevirtual #39 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 34: pop 35: bipush 6 37: ireturn Exception table: from to target type 12 18 20 Class java/lang/Exception StackMapTable: number_of_entries = 2 frame_type = 84 / same_locals_1_stack_item / stack = [ class java/lang/Exception ] frame_type = 14 / same /

Exception Details: Location: Search.foo2()I @30: aload_1 Reason: Type top (current frame, locals[1]) is not assignable to reference type Current Frame: bci: @30 flags: { } locals: { 'java/lang/Exception' } stack: { 'java/lang/StringBuffer' }

ericbodden commented 7 years ago

Thanks a lot for the detailed report. I am not 100% sure but the problem might be that the exception handler in the bytecode should also include bytecode 19, not just 12 to 18.

Could you maybe do us a favour and disassemble also the respective method of the original Java .class file (first producing such a .class file with javac in case you are using Soot on source code)?

myspringchen commented 7 years ago

Hi, Eric,

The original code is given as follows. Soot transforms the bytecode into the jimple code, while fails in transforming it back correctly.

Regards, Yuting

The original bytecode is: private static int getStartLevel(); descriptor: ()I flags: ACC_PRIVATE, ACC_STATIC Code: stack=4, locals=1, args_size=0 0: ldc #47 // String osgi.startLevel 2: invokestatic #1465 // Method org/eclipse/osgi/framework/internal/core/FrameworkProperties.getProperty:(Ljava/lang/String;)Ljava/lang/String; 5: astore_0 6: aload_0 7: ifnull 51 10: aload_0 11: invokestatic #1324 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I 14: ireturn 15: pop 16: getstatic #1254 // Field debug:Z 19: ifeq 51 22: getstatic #1253 // Field java/lang/System.out:Ljava/io/PrintStream; 25: new #765 // class java/lang/StringBuffer 28: dup 29: ldc_w #704 // String Start level = 32: invokespecial #1346 // Method java/lang/StringBuffer."":(Ljava/lang/String;)V 35: aload_0 36: invokevirtual #1349 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 39: ldc_w #685 // String parsed. Using hardcoded default: 6 42: invokevirtual #1349 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 45: invokevirtual #1345 // Method java/lang/StringBuffer.toString:()Ljava/lang/String; 48: invokevirtual #1310 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 51: bipush 6 53: ireturn Exception table: from to target type 10 14 15 Class java/lang/NumberFormatException LineNumberTable: line 329: 0 line 330: 6 line 332: 10 line 333: 15 line 334: 16 line 335: 22 line 337: 51 LocalVariableTable: Start Length Slot Name Signature 6 48 0 level Ljava/lang/String;

The corresponding Jimple code: private static int getStartLevel() { java.lang.String r0, $r6; int $i0; java.lang.NumberFormatException $r1; boolean $z0; java.lang.StringBuffer $r2, $r4, $r5; java.io.PrintStream $r3;

    r0 = staticinvoke <org.eclipse.osgi.framework.internal.core.FrameworkProperties: java.lang.String getProperty(java.lang.String)>("osgi.startLevel");
    if r0 == null goto label4;
 label1:
    $i0 = staticinvoke <java.lang.Integer: int parseInt(java.lang.String)>(r0);
 label2:
    return $i0;
 label3:
    $r1 := @caughtexception;
    $z0 = <org.eclipse.core.runtime.adaptor.EclipseStarter: boolean debug>;
    if $z0 == 0 goto label4;
    $r3 = <java.lang.System: java.io.PrintStream out>;
    $r2 = new java.lang.StringBuffer;
    specialinvoke $r2.<java.lang.StringBuffer: void <init>(java.lang.String)>("Start level = ");
    $r4 = virtualinvoke $r2.<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>(r0);
    $r5 = virtualinvoke $r4.<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>("  parsed. Using hardcoded default: 6");
    $r6 = virtualinvoke $r5.<java.lang.StringBuffer: java.lang.String toString()>();
    virtualinvoke $r3.<java.io.PrintStream: void println(java.lang.String)>($r6);

 label4:
    return 6;

    catch java.lang.NumberFormatException from label1 to label2 with label3;
}

The bytecode transformed from the jimple file: private static int getStartLevel(); descriptor: ()I flags: ACC_PRIVATE, ACC_STATIC Code: stack=4, locals=2, args_size=0 0: ldc #79 // String osgi.startLevel 2: invokestatic #395 // Method org/eclipse/osgi/framework/internal/core/FrameworkProperties.getProperty:(Ljava/lang/String;)Ljava/lang/String; 5: astore_1 6: aload_1 7: ifnull 53 10: aload_1 11: invokestatic #594 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I 14: istore_1 15: iload_1 16: ireturn 17: astore_0 18: getstatic #308 // Field debug:Z 21: ifeq 53 24: getstatic #647 // Field java/lang/System.out:Ljava/io/PrintStream; 27: new #422 // class java/lang/StringBuffer 30: dup 31: ldc_w #649 // String Start level = 34: invokespecial #439 // Method java/lang/StringBuffer."":(Ljava/lang/String;)V 37: aload_1 38: invokevirtual #445 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 41: ldc_w #651 // String parsed. Using hardcoded default: 6 44: invokevirtual #445 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer; 47: invokevirtual #447 // Method java/lang/StringBuffer.toString:()Ljava/lang/String; 50: invokevirtual #656 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 53: bipush 6 55: ireturn Exception table: from to target type 10 15 17 Class java/lang/NumberFormatException StackMapTable: number_of_entries = 2 frame_type = 81 / same_locals_1_stack_item / stack = [ class java/lang/NumberFormatException ] frame_type = 35 / same /

ericbodden commented 7 years ago

Thanks, that confirms my suspicion: in the original bytecode the trap covers the ireturn (bytecode 14), but in the bytecode that Soot generates it is not covered (bytecode 16, but the trap ends at 15). In fact it appears that the Jimple code is wrong already. I believe that it should rather read:

private static int getStartLevel()
{
java.lang.String r0, $r6;
int $i0;
java.lang.NumberFormatException $r1;
boolean $z0;
java.lang.StringBuffer $r2, $r4, $r5;
java.io.PrintStream $r3;
    r0 = staticinvoke <org.eclipse.osgi.framework.internal.core.FrameworkProperties: java.lang.String getProperty(java.lang.String)>("osgi.startLevel");
    if r0 == null goto label4;
 label1:
    $i0 = staticinvoke <java.lang.Integer: int parseInt(java.lang.String)>(r0);
    return $i0;
 label3:
    $r1 := @caughtexception;
    $z0 = <org.eclipse.core.runtime.adaptor.EclipseStarter: boolean debug>;
    if $z0 == 0 goto label4;
    $r3 = <java.lang.System: java.io.PrintStream out>;
    $r2 = new java.lang.StringBuffer;
    specialinvoke $r2.<java.lang.StringBuffer: void <init>(java.lang.String)>("Start level = ");
    $r4 = virtualinvoke $r2.<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>(r0);
    $r5 = virtualinvoke $r4.<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>("  parsed. Using hardcoded default: 6");
    $r6 = virtualinvoke $r5.<java.lang.StringBuffer: java.lang.String toString()>();
    virtualinvoke $r3.<java.io.PrintStream: void println(java.lang.String)>($r6);

 label4:
    return 6;

    catch java.lang.NumberFormatException from label1 to label3 with label3;
}

The mistake is probably somewhere in ExceptionalUnitGraph. @myspringchen could you devote some time yourself to debug this?

myspringchen commented 7 years ago

Thank you, Eric. It's a method from the Dacapo benchmark.

Actually I have partially solved my problem (let the bytecode pass verification) by rewriting the Jimple code: before the use of r0 in the exception handling part, I redefine r0 (see next). My work can be continued now.

Regards,

Yuting

r0 = staticinvoke <org.eclipse.osgi.framework.internal.core.FrameworkProperties: java.lang.String getProperty(java.lang.String)>("osgi.startLevel");
if r0 == null goto label4;

label1: $i0 = staticinvoke <java.lang.Integer: int parseInt(java.lang.String)>(r0); label2: return $i0; label3: $r1 := @caughtexception; $z0 = ; if $z0 == 0 goto label4; $r3 = ; $r2 = new java.lang.StringBuffer; specialinvoke $r2.<java.lang.StringBuffer: void (java.lang.String)>("Start level = "); +r0 = staticinvoke <org.eclipse.osgi.framework.internal.core.FrameworkProperties: java.lang.String getProperty(java.lang.String)>("osgi.startLevel"); $r4 = virtualinvoke $r2.<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>(r0); $r5 = virtualinvoke $r4.<java.lang.StringBuffer: java.lang.StringBuffer append(java.lang.String)>(" parsed. Using hardcoded default: 6"); $r6 = virtualinvoke $r5.<java.lang.StringBuffer: java.lang.String toString()>(); virtualinvoke $r3.<java.io.PrintStream: void println(java.lang.String)>($r6);

label4: return 6;

catch java.lang.NumberFormatException from label1 to label2 with label3;

}

pavanupb commented 4 years ago

@myspringchen Closed due to inactivity. Please re-open if still relevant.