Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incompatible argument to function with ASM bytecode instrumentation

I am having some troubles running a simple main program with Guava libraries.

I have instrumented the classes to get the methods parameters using my code from here : Java method parameters values in ASM

The issue is, that while the code works for small projects (aka Tower of Hanoi), with Guava I have errors and exceptions.

In particular, when testing the Joiner.join method, I have this error:

Exception in thread "Jalen Agent" java.lang.VerifyError: (class: com/google/common/base/Joiner, method: withKeyValueSeparator signature: (Ljava/lang/String;)Lcom/google/common/base/Joiner$MapJoiner;) Incompatible argument to function
at Main.joinBench(Main.java:42)
at Main.main(Main.java:20)

And when running the example using -noverify, I have an exception:

Exception in thread "Jalen Agent" java.lang.ArrayIndexOutOfBoundsException: 1
at com.google.common.base.Joiner.<init>(Joiner.java)
at com.google.common.base.Joiner.on(Joiner.java:71)
at Main.joinBench(Main.java:42)
at Main.main(Main.java:20)

The bytecode of the method is consistent:

  public static com.google.common.base.Joiner on(java.lang.String);
      Code:
         0: bipush        1
         2: anewarray     #4                  // class java/lang/Object
         5: astore_1      
         6: aload_1       
         7: bipush        0
         9: aload_0       
        10: aastore       
        11: ldc           #20                 // int 369
        13: ldc           #21                 // String com/google/common/base/Joiner
        15: ldc           #22                 // String on
        17: aload_1       
        18: invokestatic  #28                 // Method jalen/MethodStats.onMethodEntry:(ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
        21: new           #2                  // class com/google/common/base/Joiner
        24: dup           
        25: aload_0       
        26: invokespecial #32                 // Method "<init>":(Ljava/lang/String;)V
        29: ldc           #20                 // int 369
        31: invokestatic  #36                 // Method jalen/MethodStats.onMethodExit:(I)V
        34: areturn

I understand that the error may be related to libraries version, but the main java program was compiled against the instrumented library and run using the same jar of the library.

Any ideas on why this is happening? And how it can be solved?

Thanks :)

EDIT

Here are the bytecode of the method withKeyValueSeparator before and after instrumentation

Original bytecode:

public com.google.common.base.Joiner$MapJoiner withKeyValueSeparator(java.lang.String);
Code:
   0: new           #33                 // class com/google/common/base/Joiner$MapJoiner
   3: dup           
   4: aload_0       
   5: aload_1       
   6: aconst_null   
   7: invokespecial #34                 // Method com/google/common/base/Joiner$MapJoiner."<init>":(Lcom/google/common/base/Joiner;Ljava/lang/String;Lcom/google/common/base/Joiner$1;)V
  10: areturn

Instrumented bytecode:

public com.google.common.base.Joiner$MapJoiner withKeyValueSeparator(java.lang.String);
Code:
   0: bipush        1
   2: anewarray     #4                  // class java/lang/Object
   5: astore_1      
   6: aload_1       
   7: bipush        1
   9: aload_1       
  10: aastore       
  11: ldc           #199                // int 390
  13: ldc           #21                 // String com/google/common/base/Joiner
  15: ldc           #200                // String withKeyValueSeparator
  17: aload_1       
  18: invokestatic  #28                 // Method jalen/MethodStats.onMethodEntry:(ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
  21: new           #8                  // class com/google/common/base/Joiner$MapJoiner
  24: dup           
  25: aload_0       
  26: aload_1       
  27: aconst_null   
  28: invokespecial #203                // Method com/google/common/base/Joiner$MapJoiner."<init>":(Lcom/google/common/base/Joiner;Ljava/lang/String;Lcom/google/common/base/Joiner$1;)V
  31: ldc           #199                // int 390
  33: invokestatic  #36                 // Method jalen/MethodStats.onMethodExit:(I)V
  36: areturn 

Here are the full bytecode of the joiner class :

Original : http://pastebin.com/VsccVX18

Instrumented : http://pastebin.com/xtke1a8y

like image 604
Adel Avatar asked Nov 12 '22 14:11

Adel


1 Answers

The original code of withKeyValueSeparator passes a bunch of its arguments to the MapJoiner constructor. You're adding instrumentation code that stores an array in the second slot of the local variable table (using astore_1). This overwrites the first argument to withKeyValueSeparator, which is a String. (The first slot of the local variable table is a MapJoiner instance itself, a.k.a this.) So when the original function's code tries to pass the object in the second slot of the local var table to the constructor, there is that "Incompatible argument" error.

To fix this, you should allocate a new slot in the local variable table for your array; this answer outlines how.

like image 183
int3 Avatar answered Nov 15 '22 06:11

int3