9

I have two example class files, one from an example Java app and one from an example C app (compiled to bytecode using LLJVM).

Looking at their outputs, I can see through javap -c -p that for initializing the (static) fields, the Java app shows the following block:

static {};
Code: 
0: sipush 1339
3: putstatic   #7     //Field SRV_ID
etc

Which is basically the <clinit> method, if I understand. Or detected as such by a VM I am using.

The C-app however has this:

public {};
Code: 
0: sipush 1339
3: putstatic   #7     //Field SRV_ID
etc

What is this? My VM does not detect it.

Sample class files. THe first one is from a Java App that prints a message and waits 20s, repeat. The second is a C app that does roughly the same.

http://www.fast-files.com/getfile.aspx?file=156962

http://www.fast-files.com/getfile.aspx?file=156961

Apologies for doing it this way - I do not immediately know how to attach files or efficiently show .class files.

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
Sven
  • 173
  • 8
  • 1
    Can you please be more specific with examples? I'm having hard time understanding why do you have empty static class initializer blocks in your code. – M. Prokhorov Mar 13 '18 at 15:32

1 Answers1

5

This seems to be a nonstandard declaration that javap did not consider. Normally, static initializers are compiled to bytecode methods named <clinit> having a static modifier. Apparently, javap decodes them by just printing the human readable form of the modifier and omitting the <clinit> name.

Here, it encountered a method named <clinit> and having the public modifier (no static modifier) and just did the same as usual, printing the modifier and omitting the <clinit> name.

The code generated by LLJVM seems to rely on an old oddity:

In a class file whose version number is 51.0 or above, the method must additionally have its ACC_STATIC flag (§4.6) set in order to be the class or interface initialization method.

This requirement was introduced in Java SE 7. In a class file whose version number is 50.0 or below, a method named <clinit> that is void and takes no arguments is considered the class or interface initialization method regardless of the setting of its ACC_STATIC flag.

To me, it was truly surprising to read that in previous versions, the ACC_STATIC modifier was not mandated and I don’t see any reason to ever exploit that oddity. It seems very natural that a class initializer (that is declared static {} in Java) should have the ACC_STATIC flag and I can’t even imagine the supposed semantics of an omitted ACC_STATIC flag. It means that one of two odd things should happen, a) it is invoked without an instance despite not have the ACC_STATIC flag (being invoked as-if having it) or b) it is invoked with an instance that must have been created “magically”.

The specification says the following about any non-standard <clinit> method:

Other methods named <clinit> in a class file are of no consequence. They are not class or interface initialization methods. They cannot be invoked by any Java Virtual Machine instruction and are never invoked by the Java Virtual Machine itself.

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
  • 4
    It was a JVM bug [JDK-6845426](https://bugs.openjdk.java.net/browse/JDK-6845426) fixed in JDK 7. I don't think lljvm exploited it intentionally, probably they just forgot to add ACC_STATIC, and the problem wasn't noticed as long as JVM accepted such classes. Seems like lljvm development ceased in 2010, even before Java 7 release. – apangin Mar 14 '18 at 11:56
  • Thank you for the detailed explanation. For the future: A way to deal with this problem is compiling the C app to a jasmin file instead of bytecode. The Jasmin file can then be edited by replacing the above public {} with static {}. When the Jasmin is then converted to bytecode, it does seem to give the standard, detectable clinit method. – Sven Mar 14 '18 at 12:31