New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue with StackMap.Shifter not correctly shifting stack map table offsets. #339
Comments
I wanted to add, that I am willing to write my own I somewhat understand the frames are ordered and the affected bytecode location is calculated with previous frames, but it seems frames have both explicit and implicit |
Have you tried |
@chibash Yes I have and it works well, I mentioned this in my original comment but it might have gotten lost in the rest of it:
In other words, I want to avoid using I was able to build a I do think there is still a bug in Javassist's stack map table shifter, specifically I think the javassist/src/main/javassist/bytecode/StackMapTable.java Lines 883 to 886 in b70a41b
When inserting a new gap at the beginning of the code via insertEx , the where value is 0 and exclusive is true , only the first frame should be updated, subsequent frames don't have to be updated because of the formula offset_delta + 1 mentioned in the JVM Spec section 4.7.4. When we enter the first frame via the Walker , oldPos is 0 , making the the statement oldPos < where false. I believe exclusive here means a bit the opposite, exclusive comes from the premise that the gap will be inserted 'exclusively' before the where location, therefore if the range of bytecode positions covered by the frame includes the gap location, then it must be shifted. Which makes me believe that all we need to do is invert the check:
Let me know if that makes sense, if you agree I can try to make a Pull Request. |
Sorry, I didn't read your first post. I'll check your suggestion is correct. |
Hi @chibash, just wanted to ping you back if you had any updates on this issue? It puts us in an awkward position because if we release a version of our library with the workaround and this ends up getting fixed in Javassit, the workaround will break since we will over adjust the offsets. So in a way we have to either wait for this fix in Javassit or deal and explain to our users some versioning nightmare to avoid the issue. Thank you so much! |
Ah, OK, I'll. |
I believe that StackMapTable is not wrong. This issue is more complicated than you expect. Let me try to explain this. First of all, when you insert a code fragment exclusively at byte position 0, you must insert a new stack map entry for the old first block. Inserting a fragment at position 0 is necessary when the original code is like this:
The bytecode will include GOTO for going back to the first instruction in the body. So inserting something at position 0 creates a new basic block and the old first basic block is turned into the second basic block. Note that the reason your fix worked is that the first statement in your example was not a loop. In this case, you don't have to create a new stack map entry since the insertion is accidentally equivalent to inserting inclusively. To create a new stack map entry, you must learn the details of the Java bytecode lots, so I don't recommend you to do that. A possible workaround is to insert a code fragment inclusively. It works unless the first statement is a loop. |
Thanks, yeah I definitely don't want to be creating my own stack map frames, that is very complex. I am really relying on inserting exclusively ( If you think Using your example:
which produces:
I would transform it to:
Notice how I only update the first frame of the stack map (the first non-implicit frame), since the code to be inserted does not need to be covered by a frame. |
OK, thank you for the information. The stack map produced by javac was different what I expect. |
jboss-javassist/javassist#305 jboss-javassist/javassist#328 jboss-javassist/javassist#339 jboss-javassist/javassist#350 jboss-javassist/javassist#357 jboss-javassist/javassist#363 Change-Id: I29963013cf637731fe1064425b9d2e80d63bd9d3 Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
jboss-javassist/javassist#305 jboss-javassist/javassist#328 jboss-javassist/javassist#339 jboss-javassist/javassist#350 jboss-javassist/javassist#357 jboss-javassist/javassist#363 Change-Id: I29963013cf637731fe1064425b9d2e80d63bd9d3 Signed-off-by: Robert Varga <robert.varga@pantheon.tech> (cherry picked from commit 0df0ba3)
jboss-javassist/javassist#305 jboss-javassist/javassist#328 jboss-javassist/javassist#339 jboss-javassist/javassist#350 jboss-javassist/javassist#357 jboss-javassist/javassist#363 Change-Id: I29963013cf637731fe1064425b9d2e80d63bd9d3 Signed-off-by: Robert Varga <robert.varga@pantheon.tech> (cherry picked from commit 0df0ba3)
Hi - It seems that when using a
CodeIterator
to insert some new instructions, theStackMapTable#shiftPc
stack map table shifter is not correctly updating theoffset_delta
of the stack map frames.The transform I am trying to do is a simple addition of an
invokespecial
at the start of a method, from this:to this:
My approach is to use a
CodeIterator
as follow:I also update the max stack and max locals, but following through the code of
insertEx
it seems both the exception table and the stack map table should be properly updated, but I still get ajava.lang.VerifyError: StackMapTable error: bad offset
when executing the code.I am aware there is a
rebuildStackMap
and when used things work, but I have the limitation that myClassPool
is limited and does not contain the full classpath that was used during compilation, therefore it might be that when rebuilding the stack map table, types have to be fixed and loaded from the pool which will fail to happen. I hope that makes sense.My first question is, should I be relying on
insertEx
to update the exception table and stack map table or should I try to do that myself? If the former is the case, then might there be a bug with the StackMapTable Shifter?I have a sample project to demonstrate my issue: javassit-bad-stack-map.zip
In it you'll find a bash script called
run.sh
that will execute the necessary commands to reproduce the issue (working directory must be the script's dir, you might need tochmod +x
bothgradlew
andrun.sh
). It will:Foo.java
andBar.java
which are the sources that will produce the bytecode I wish to transform.Main.java
which is where I use javassit to perform the transform.Foo.class
The transform code is in
javassit-bad-stack-map/src/main/java/Main.java
The sources and classes transformed are in
javassit-bad-stack-map/src/main/resources
The relevant section of the bytecode is as follow:
rebuildStackMap
:javac
if the originalFoo.java
had the super call.Let me know if you need more information, thanks!
The text was updated successfully, but these errors were encountered: