jasmin hacking and verify error - java

I'm playing with jasmin and I try to launch my .class file, which is supposed to perform simple string concatenation. My jasmin source looks like this:
.class public default_class
.super java/lang/Object
.method public static main([Ljava/lang/String;)V
.limit locals 1
.limit stack 1
invokestatic main_65428301()I
return
.end method
.method public static main_65428301()I
.limit locals 1
.limit stack 100
new java/lang/String
dup
ldc "foo"
invokestatic java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
invokespecial java/lang/StringBuilder(Ljava/lang/String;)V
ldc "bar"
invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
invokevirtual java/lang/String.toString()V
astore_0
iconst_0
ireturn
.end method
Now I'm able to run java -jar jasmin.jar and I get default_class.class. However, when I try to launch it like java default_class I get an error:
Exception in thread "main" java.lang.VerifyError: (class: default_class, method: main_65428301 signature: ()I) Illegal use of nonvirtual function call
What should I change in my assembly to get this to work?

In JVM, to create the object you have to first use new instruction and then call <init> method (constructor). You do not create new StringBuilder and call the wrong constructor name (should be java/lang/StringBuilder/<init>(Ljava/lang/String;)V).
I also see no reason to do:
new java/lang/String
dup
or
invokestatic java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;

"The new instruction does not completely create a new instance; instance initialization is not completed until an instance initialization method has been invoked on the uninitialized instance."

Related

Assembly language in Jasmin - getting syntax error

In putty I am attempting to create a Jasmin program that, when assembled and ran as a Java program, will output the integer "431". When I attempt to assemble the program the console says there is a syntax error on line 11. I am having trouble figuring out what it is. Here is my code:
.class public Lab3_JasminExample
.super java/lang/Object
.method public <init>()V
aload_0
invokespecial java/lang/Object/<init>()V
return
.end method
.method public static main ([Ljava.lang.String;)V
.limit stack 10
.limit locals 10
getstatic java/lang/System/out Ljava/io/PrintStream;
sipush 431
invokevirtual java/io/PrintStream/println(I)V
return
.end method
Line 11 would be ".limit stack 10" and I can't see what is wrong with how I wrote that. What am I doing incorrectly?
Errors may be reported on a line but be triggered by previous (or following!) lines, so always look around the offending line.
My Jasmin (version 2.4) correctly reports the error on line 10
a.j:10: Warning - Syntax error.
.method public static main ([Ljava.lang.String;)V
^
This is a silly mistake really: there is space between the method name (main) and its descriptor (([Ljava.lang.String;)V)
Line 10 should be .method public static main([Ljava.lang.String;)V

Generating java class file headers from jvm bytecode

Currently I am tinkering with the jvm bytecode instructions. I made a simple compiler that given source code (C like style) generates valid jvm bytecode representation. For example, the following code:
float x = 3;
float y = 4.5;
float z = x + y;
print z;
Compiles to:
ldc 3
i2f
fstore 1
ldc 4.5
fstore 2
fload 1
fload 2
fadd
fstore 3
getstatic java/lang/System/out Ljava/io/PrintStream;
fload 3
invokevirtual java/io/PrintStream/println(F)V
return
(I know the generated java code is not the most efficient as of now, but that is not the point).
Using a Java Bytecode Editor, I loaded a compiled main class and replaced the main method code with my code. After that, I was able to run the class file with my code perfectly fine. My question is, is there a tool/script without UI that can take java bytecode and generate the appropriate headers for the class file (in other words, take the bytecode and make a valid class file out of it). I guess I can write a script myself, but that would take some time that I might not have now.
The Krakatau assembler allows you to write bytecode in a textual format and assembles it into a classfile, handling all the binary encoding details for you.
It's similar to the older Jasmin assembler, but with minor syntax changes in order to remove ambiguity and to support classfile features that Jasmin can't handle. Unlike Jasmin, it fully supports the entire Java 8 classfile format and optionally allows full control over the binary representation of the classfile.
For example, here's a class using lambdas in Krakatau assembly format.
.version 52 0
.class public super LambdaTest1
.super java/lang/Object
.method public <init> : ()V
.code stack 1 locals 1
aload_0
invokespecial Method java/lang/Object <init> ()V
return
.end code
.end method
.method public static varargs main : ([Ljava/lang/String;)V
.code stack 4 locals 2
invokedynamic InvokeDynamic invokeStatic Method java/lang/invoke/LambdaMetafactory metafactory (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; MethodType (J)J MethodHandle invokeStatic Method LambdaTest1 lambda$main$0 (J)J MethodType (J)J : applyAsLong ()Ljava/util/function/LongUnaryOperator;
astore_1
getstatic Field java/lang/System out Ljava/io/PrintStream;
aload_1
ldc2_w 42L
invokeinterface InterfaceMethod java/util/function/LongUnaryOperator applyAsLong (J)J 3
invokevirtual Method java/io/PrintStream println (J)V
return
.end code
.end method
.method private static synthetic lambda$main$0 : (J)J
.code stack 4 locals 2
lload_0
lload_0
l2i
lshl
lreturn
.end code
.end method
.innerclasses
java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup public static final
.end innerclasses
.end class

Eclipse EcLemma number of covered instructions more than lines of code

In the coverage result, it shows that I've covered 9 instructions while there are only 5 lines highlighted green. Which are the other 4 instructions?
Click the dropdown arrow at the top right of the Coverage box. It'll give you a couple different ways to measure your coverage. The default seems to be instructions (bytecode instructions), but you can manually select lines.
The reason you are seeing 9 instructions is because there are 9 bytecode instructions in Foo:
$ javap -c Foo.class
Compiled from "Foo.java"
public class Foo {
public Foo();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #22 // String Test
5: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: new #1 // class Foo
11: invokespecial #30 // Method "<init>":()V
14: return
}
As #schmosel says, it is counting bytecode instructions.
You can verify this by reading the EMMA reference documentation (EclEMMA is an Eclipse GUI wrapped around EMMA), in which the phrase "bytecode instructions" is used throughout.

Why doesn't the Java 7 byteode verifier choke on this?

I'm working on code that calculates entries in the StackFrameMap (SFM). The goal is to be able to generate (SFM) entries that make the Java 7 bytecode verifier happy. Following a TDD methodology, I started by creating bogus SMF entries for the verifier to complain about; I would the replace these with my properly-calculated entries to see that I was doing it correctly.
The problem is: I can't get the bytecode verifier to complain. Here is an example, starting with the original Java code (this code is not supposed to do anything useful):
public int stackFrameTest(int x) {
if (x > 0) {
System.out.println("positive x");
}
return -x;
}
This generates the following bytecode (with SFM):
public int stackFrameTest(int);
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: ifle 12
4: getstatic #47 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #85 // String positive x
9: invokevirtual #55 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: iload_1
13: ineg
14: ireturn
StackMapTable: number_of_entries = 1
frame_type = 12 /* same */
Now, I change the SFM to contain this:
StackMapTable: number_of_entries = 1
frame_type = 255 /* full_frame */
offset_delta = 12
locals = [ double, float ]
stack = [ double ]
As you can see, that is completely bogus, but it loads without error. I read the JVM spec, and I couldn't see any reason why this would work. I'm not using the SplitBytecodeVerifier option.
EDIT: Per the accepted answer below, Eclipse had been set to emit Java 6 class files (version 50.0). Classfiles of this this version will quietly ignore issues with the StackFrameMap. After changing the setting to use the default Java 7 classfile format (51.0), it worked as expected.
I am unable to reproduce your results. I tried modifying the stack frame and it failed to load as expected. If you want, I can post my modified classfile.
It's not clear what happened, but you've almost certainly made a mistake somewhere. The most likely explanation is that your classfile has version 50.0, in which case the JVM will fall back to normal verification when the stackmap is invalid. You need to set the version to 51.0 to force stackmap validation. Another possibility is that you simply messed up editing the file and didn't actually save the changes or didn't make the changes you thought you did.
Here's the assembly for my modified classfile.
.version 51 0
.class super public StackFrameTest4
.super java/lang/Object
.method public <init> : ()V
.limit stack 1
.limit locals 1
aload_0
invokespecial java/lang/Object <init> ()V
return
.end method
.method static public main : ([Ljava/lang/String;)V
.limit stack 2
.limit locals 1
new StackFrameTest
dup
invokespecial StackFrameTest <init> ()V
bipush 42
invokevirtual StackFrameTest stackFrameTest (I)I
pop
return
.end method
.method public stackFrameTest : (I)I
.limit stack 2
.limit locals 2
iload_1
ifle L12
getstatic java/lang/System out Ljava/io/PrintStream;
ldc 'positive x'
invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
L12:
.stack full
locals Double Float
stack Double
.end stack
iload_1
ineg
ireturn
.end method

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
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.
First, I do not see why this should be related to libraries versions. It seems that the bytecode is not instrumented correctly, which causes the verification to fail and the exception if you use -noverify.
Regarding the verification error, it indicates that there is an error in Joiner.withKeyValueSeparator(). The code of this method tries to invoke another method with incompatible method arguments. Could you give the instrumented bytecode of the withKeyValueSeparator() method? (and perferrably the non-instrumented as well)
The error you see with -noverify occurs in the constructor of the Joiner, there seems to be nothing wrong with the Joiner.on() method. Again, could you post the bytecode of the Joiner. method? (instrumented and non-instrumented)

Categories

Resources