Given that the zip initialization is tricky, I agree that it's risky to delay the loading of zip library for JDK 8u.

Volker - thanks for the instruction to reproduce ESE. Does your patch address the ServiceLoader issue? Charset calling sun.misc.Launcher.getBootstrapClassPath would still cause the bootstrapping issue AFAICT. That one is tricky to resolve.

As for ESE, the static findBuiltinLib method finds if JNI_OnLoad_$LIBNAME entry exists. It is not intended to trigger any shared library to be loaded. Your patch would work. As findBuiltinLib doesn't load any shared library, it confuses JNI FindClass to call NativeLibrary::getFromClass. Another option is to move the findBuiltinLib method out from NativeLibrary class:
http://cr.openjdk.java.net/~mchung/jdk9/webrevs/8081674/webrev.00/

Mandy

On 06/09/2015 11:07 AM, Xueming Shen wrote:
On 06/09/2015 11:03 AM, Mandy Chung wrote:
Does my patch work fine on 8u? If it works fine, I prefer to get that simple fix in 8u and take the time to have a better fix in 9 (jdk9 is still under development and I have assumed that it's not a show stopper to you. Let me know if it blocks you).

A question to Sherman - do we have adequate tests to verify the move of System.loadLibrary("zip") from system initialization to ZipFile initialization for 8u?

I don't think we have "adequate" tests for the "zip" initialization. It had tricky problems in the past. I would assume it might be safe to move it from the System to the zip package for 9 as we no longer have those jar files to load the classes, but that probably not the case for 8u, yet(?).

-Sherman


Mandy

On 06/09/2015 10:09 AM, Volker Simonis wrote:
Hi Mandy,

thanks for looking into this.
Uunfortunately your fix only helps to fix "java -version" :(

Running even a minimal HelloWorld will still fail with the following
stack trace which is still caused by the same EmptyStackException:

Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.ExceptionInInitializerError
     at sun.misc.Unsafe.ensureClassInitialized(Native Method)
at sun.misc.SharedSecrets.getJavaUtilZipFileAccess(SharedSecrets.java:158)
     at sun.misc.URLClassPath$JarLoader.<clinit>(URLClassPath.java:765)
     at sun.misc.URLClassPath$3.run(URLClassPath.java:530)
     at sun.misc.URLClassPath$3.run(URLClassPath.java:520)
     at java.security.AccessController.doPrivileged(Native Method)
     at sun.misc.URLClassPath.getLoader(URLClassPath.java:519)
     at sun.misc.URLClassPath.getLoader(URLClassPath.java:492)
     at sun.misc.URLClassPath.getNextLoader(URLClassPath.java:457)
     at sun.misc.URLClassPath.access$100(URLClassPath.java:64)
     at sun.misc.URLClassPath$1.next(URLClassPath.java:239)
     at sun.misc.URLClassPath$1.hasMoreElements(URLClassPath.java:250)
     at java.net.URLClassLoader$3$1.run(URLClassLoader.java:601)
     at java.net.URLClassLoader$3$1.run(URLClassLoader.java:599)
     at java.security.AccessController.doPrivileged(Native Method)
     at java.net.URLClassLoader$3.next(URLClassLoader.java:598)
at java.net.URLClassLoader$3.hasMoreElements(URLClassLoader.java:623)
     at sun.misc.CompoundEnumeration.next(CompoundEnumeration.java:45)
at sun.misc.CompoundEnumeration.hasMoreElements(CompoundEnumeration.java:54)
     at sun.misc.CompoundEnumeration.next(CompoundEnumeration.java:45)
at sun.misc.CompoundEnumeration.hasMoreElements(CompoundEnumeration.java:54) at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:354) at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:393)
     at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:474)
     at java.nio.charset.Charset$1.getNext(Charset.java:350)
     at java.nio.charset.Charset$1.hasNext(Charset.java:365)
     at java.nio.charset.Charset$2.run(Charset.java:410)
     at java.nio.charset.Charset$2.run(Charset.java:407)
     at java.security.AccessController.doPrivileged(Native Method)
     at java.nio.charset.Charset.lookupViaProviders(Charset.java:406)
     at java.nio.charset.Charset.lookup2(Charset.java:477)
     at java.nio.charset.Charset.lookup(Charset.java:464)
     at java.nio.charset.Charset.isSupported(Charset.java:505)
at sun.launcher.LauncherHelper.makePlatformString(LauncherHelper.java:580)
Caused by: java.util.EmptyStackException
     at java.util.Stack.peek(Stack.java:102)
at java.lang.ClassLoader$NativeLibrary.getFromClass(ClassLoader.java:1759) at java.lang.ClassLoader$NativeLibrary.findBuiltinLib(Native Method)
     at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1870)
     at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1843)
     at java.lang.Runtime.loadLibrary0(Runtime.java:870)
     at java.lang.System.loadLibrary(System.java:1122)
     at java.util.zip.ZipFile.<clinit>(ZipFile.java:86)
     ... 34 more

It's obvious that the way jni_FindClass is looking for the class
context by calling the NativeLibrary::getFromClass method is hacky but
I think that the proposed fix is the quite simple and non-intrusive.
And we probably don't want a complete rework of this code for 8
anyway. So why not fix it the proposed way in 8 and 9 for now? That
will still leave us time to come up with a better clean-up at least
for 9.

What do you think?

Regards,
Volker

On Tue, Jun 9, 2015 at 1:35 AM, Mandy Chung <mandy.ch...@oracle.com> wrote:
Hi Volker,

Your patch reminds me the question I have about loading zip library.

In JDK 9 (and earlier release), zip library is loaded by the VM during
startup (see ClassLoader::load_zip_library). I think loadLibrary("zip") is no longer needed to be called from System::initializeSystemClass method and
instead it can be loaded by java.util.zip.ZipFile static initializer.

Do you mind to try the patch (below)?  That may be a simpler fix.

I never like the way jni_FindClass to look for the class context by calling the NativeLibrary::getFromClass method. I will have to look deeper other alternative to clean that up. If taking out loadLibrary("zip") resolves your issue, this will give us time to come up with a better clean-up in the
future.

Mandy
[1] https://bugs.openjdk.java.net/browse/JDK-4429040

diff --git a/src/share/classes/java/lang/System.java
b/src/share/classes/java/lang/System.java
--- a/src/share/classes/java/lang/System.java
+++ b/src/share/classes/java/lang/System.java
@@ -1192,10 +1192,6 @@
          setOut0(newPrintStream(fdOut,
props.getProperty("sun.stdout.encoding")));
          setErr0(newPrintStream(fdErr,
props.getProperty("sun.stderr.encoding")));

- // Load the zip library now in order to keep java.util.zip.ZipFile
-        // from trying to use itself to load this library later.
-        loadLibrary("zip");
-
          // Setup Java signal handlers for HUP, TERM, and INT (where
available).
          Terminator.setup();

diff --git a/src/share/classes/java/util/zip/ZipFile.java
b/src/share/classes/java/util/zip/ZipFile.java
--- a/src/share/classes/java/util/zip/ZipFile.java
+++ b/src/share/classes/java/util/zip/ZipFile.java
@@ -83,6 +83,7 @@

      static {
/* Zip library is loaded from System.initializeSystemClass */
+        System.loadLibrary("zip");
          initIDs();

      }



On 06/08/2015 07:23 AM, Volker Simonis wrote:
Hi,

can I please get a review at least for the part of this fix which is
common for jdk8 and jdk9. It's only a few lines in
src/share/vm/prims/jni.cpp for the hotspot repository and one line
src/java.base/share/classes/java/lang/ClassLoader.java for the jdk
repository.

Thanks,
Volker


On Tue, Jun 2, 2015 at 6:12 PM, Volker Simonis <volker.simo...@gmail.com>
wrote:
Hi,

can I please have review (and a sponsor) for these changes:

https://bugs.openjdk.java.net/browse/JDK-8081674
http://cr.openjdk.java.net/~simonis/webrevs/2015/8081674.jdk
http://cr.openjdk.java.net/~simonis/webrevs/2015/8081674.hs

Please notice that the fix requires synchronous changes in the jdk and
the
hotspot forest.

The changes themselves are by far not that big as this explanation but I found the problem to be quite intricate so I tried to explain it as good
as
I could. I'd suggest to read the HTML-formatted explanation in the webrev
although the content is equal to the one in this mail:

Using an unsupported character encoding (e.g. vi_VN.TCVN on Linux)
results
in an immediate VM failure with jdk 8 and 9:

export LANG=vi_VN.TCVN
java -version
Error occurred during initialization of VM
java.util.EmptyStackException
         at java.util.Stack.peek(Stack.java:102)
         at
java.lang.ClassLoader$NativeLibrary.getFromClass(ClassLoader.java:1751) at java.lang.ClassLoader$NativeLibrary.findBuiltinLib(Native
Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1862)
         at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1835)
         at java.lang.Runtime.loadLibrary0(Runtime.java:870)
         at java.lang.System.loadLibrary(System.java:1119)
         at java.lang.System.initializeSystemClass(System.java:1194)

This is a consequence of "8005716: Enhance JNI specification to allow
support of static JNI libraries in Embedded JREs".

With jdk 9 we get this error even if we're running with a supported
charset
which is in the ExtendedCharsets (as opposed to being in the
StandardCharsets) class which is a consequence of delegating the loading
of
the ExtendedCharsets class to the ServiceLoader in jdk 9.

export LANG=eo.iso-8859-3
output-jdk9/images/jdk/bin/java -version
Error occurred during initialization of VM
java.util.EmptyStackException
         at java.util.Stack.peek(Stack.java:102)
         at
java.lang.ClassLoader$NativeLibrary.getFromClass(ClassLoader.java:1737) at java.lang.ClassLoader$NativeLibrary.findBuiltinLib(Native
Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1866)
         at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1840)
         at java.lang.Runtime.loadLibrary0(Runtime.java:874)
         at java.lang.System.loadLibrary(System.java:1111)
         at java.lang.System.initializeSystemClass(System.java:1186)

Here's why the exception happens for an unsupported charset (see the
mixed
stack trace below for the full details):

java.lang.System.loadLibrary() wants to load libzip.so. It calls
java.lang.Runtime.loadLibrary0() which at the very beginning calls the native method ClassLoader$NativeLibrary.findBuiltinLib() which checks if
the
corresponding library is already statically linked into the VM
(introduced
by 8005716).
Java_java_lang_ClassLoader_00024NativeLibrary_findBuiltinLib(),
the native implementation of findBuiltinLib() in Classloader.c calls
GetStringPlatformChars() to convert the library name into the native
platform encoding. GetStringPlatformChars() calls the helper function jnuEncodingSupported() to check if the platform encoding which is stored
in
the property "sun.jnu.encoding" is supported by Java.
jnuEncodingSupported()
is implemented as follows:

static jboolean isJNUEncodingSupported = JNI_FALSE;
static jboolean jnuEncodingSupported(JNIEnv *env) {
      jboolean exe;
      if (isJNUEncodingSupported == JNI_TRUE) {
          return JNI_TRUE;
      }
isJNUEncodingSupported = (jboolean) JNU_CallStaticMethodByName (
                                      env, &exe,
"java/nio/charset/Charset",
                                      "isSupported",
"(Ljava/lang/String;)Z",
                                      jnuEncoding).z;
      return isJNUEncodingSupported;
}

Once the function finds that the platform encoding is supported (by
calling
java.nio.charset.Charset.isSupported()) it caches this value and always returns it on following calls. However if the platform encoding is not
supported, it ALWAYS calls java.nio.charset.Charset.isSupported() an
every
subsequent invocation.

In order to call the Java method Charset.isSupported() (in
JNU_CallStaticMethodByName() in file jni_util.c), we have to call
jni_FindClass() to convert the symbolic class name
"java.nio.charset.Charset" into a class reference.

But unfortunately, jni_FindClass() (from jni.cpp in libjvm.so) has a
special
handling if called from java.lang.ClassLoader$NativeLibrary to ensure
that
JNI_OnLoad/JNI_OnUnload are executed in the correct class context:

instanceKlassHandle k (THREAD, thread->security_get_caller_class(0));
    if (k.not_null()) {
      loader = Handle(THREAD, k->class_loader());
// Special handling to make sure JNI_OnLoad and JNI_OnUnload are
executed
      // in the correct class context.
      if (loader.is_null() &&
k->name() == vmSymbols::java_lang_ClassLoader_NativeLibrary()) {
        JavaValue result(T_OBJECT);
        JavaCalls::call_static(&result, k,
vmSymbols::getFromClass_name(),
vmSymbols::void_class_signature(),
                                        thread);
        if (HAS_PENDING_EXCEPTION) {
          Handle ex(thread, thread->pending_exception());
          CLEAR_PENDING_EXCEPTION;
          THROW_HANDLE_0(ex);
        }

So if that's the case and jni_FindClass() was reallycalled from
ClassLoader$NativeLibrary, then jni_FindClass() calles
ClassLoader$NativeLibrary().getFromClass() to find out the corresponding
context class which is supposed to be saved there in a field of type
java.util.Stack named "nativeLibraryContext":

// Invoked in the VM to determine the context class in
// JNI_Load/JNI_Unload
static Class<?> getFromClass() {
      return ClassLoader.nativeLibraryContext.peek().fromClass;
}

Unfortunately, "nativeLibraryContext" doesn't contain any entry at this point and the invocation of Stack.peek() will throw the exception shown before. In general, the "nativeLibraryContext" stack will be filled later
on
in Runtime.loadLibrary0() like this:

NativeLibrary lib = new NativeLibrary(fromClass, name, isBuiltin);
nativeLibraryContext.push(lib);
try {
      lib.load(name, isBuiltin);
} finally {
      nativeLibraryContext.pop();
}

such that it always contains at least one element later when
jni_FindClass()
will be invoked.

So in summary, the problem is that the implementors of 8005716 didn't
took
into account that calling ClassLoader$NativeLibrary.findBuiltinLib() may trigger a call to jni_FindClass() if we are running on a system with an
unsupported character encoding.

I'd suggest the following fix for this problem:

Change ClassLoader$NativeLibrary().getFromClass() to return null if the
stack is empty instead of throwing an exception:

static Class<?> getFromClass() {
      return ClassLoader.nativeLibraryContext.empty() ?
          null : ClassLoader.nativeLibraryContext.peek().fromClass;
}

Unfortunately this also requires a HotSpot change in jni_FindClass() in
order to properly handle the new 'null' return value:

    if (k.not_null()) {
      loader = Handle(THREAD, k->class_loader());
// Special handling to make sure JNI_OnLoad and JNI_OnUnload are
executed
      // in the correct class context.
      if (loader.is_null() &&
k->name() == vmSymbols::java_lang_ClassLoader_NativeLibrary()) {
        JavaValue result(T_OBJECT);
        JavaCalls::call_static(&result, k,
vmSymbols::getFromClass_name(),
vmSymbols::void_class_signature(),
                                        thread);
        if (HAS_PENDING_EXCEPTION) {
          Handle ex(thread, thread->pending_exception());
          CLEAR_PENDING_EXCEPTION;
          THROW_HANDLE_0(ex);
        }
        oop mirror = (oop) result.get_jobject();
        if (oopDesc::is_null(mirror)) {
loader = Handle(THREAD, SystemDictionary::java_system_loader());
        } else {
          loader = Handle(THREAD,

InstanceKlass::cast(java_lang_Class::as_Klass(mirror))->class_loader());
          protection_domain = Handle(THREAD,


InstanceKlass::cast(java_lang_Class::as_Klass(mirror))->protection_domain());
        }
      }
    } else {
// We call ClassLoader.getSystemClassLoader to obtain the system
class
loader.
loader = Handle(THREAD, SystemDictionary::java_system_loader());
    }

These changes are sufficient to solve the problem in Java 8.
Unfortunately,
that's still not enough in Java 9 because there the loading of the
extended
charsets has been delegated to ServiceLoader. But ServiceLoader calls
ClassLoader.getBootstrapResources() which calls
sun.misc.Launcher.getBootstrapClassPath(). This leads to another problem
during class initialization of sun.misc.Launcher if running on an
unsupported locale.

The first thing done in sun.misc.Launcher.<clinit> is the initialisation
of
the bootstrap URLClassPath in the Launcher. However this initialisation
will
eventually call Charset.isSupported() and if we are running on an
unsupported locale this will inevitably end in another recursive call to
ServiceLoader. But as explained below, ServiceLoader will query the
Launcher's bootstrap URLClassPath which will be still uninitialized at
that
point.

So we'll have to additionally guard guard against this situation on JDK 9
like this:

private static Charset lookupExtendedCharset(String charsetName) {
      if (!sun.misc.VM.isBooted() ||             // see
lookupViaProviders()
          sun.misc.Launcher.getBootstrapClassPath() == null)
          return null;

This fixes the crashes, but still at the price of not having the extended
charsets available during initialization until
Launcher.getBootstrapClassPath is set up properly. This may be still a problem if the jdk is installed in a directory which contains characters specific to an extended encoding or if we have such characters in the
command line arguments.

Thank you and best regards,
Volker


Mixed stack trace of the initial EmptyStackException for unsupported
charsets described before:

Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native
code)
j  java.util.Stack.peek()Ljava/lang/Object;+1
j java.lang.ClassLoader$NativeLibrary.getFromClass()Ljava/lang/Class;+3
v  ~StubRoutines::call_stub
V  [libjvm.so+0x9d279a] JavaCalls::call_helper(JavaValue*,
methodHandle*,
JavaCallArguments*, Thread*)+0x6b4
V [libjvm.so+0xcad591] os::os_exception_wrapper(void (*)(JavaValue*, methodHandle*, JavaCallArguments*, Thread*), JavaValue*, methodHandle*,
JavaCallArguments*, Thread*)+0x45
V  [libjvm.so+0x9d20cf]  JavaCalls::call(JavaValue*, methodHandle,
JavaCallArguments*, Thread*)+0x8b
V [libjvm.so+0x9d1d3b] JavaCalls::call_static(JavaValue*, KlassHandle,
Symbol*, Symbol*, JavaCallArguments*, Thread*)+0x139
V [libjvm.so+0x9d1e3f] JavaCalls::call_static(JavaValue*, KlassHandle,
Symbol*, Symbol*, Thread*)+0x9d
V  [libjvm.so+0x9e6588]  jni_FindClass+0x428
C  [libjava.so+0x20208]  JNU_CallStaticMethodByName+0xff
C  [libjava.so+0x21cae]  jnuEncodingSupported+0x61
C  [libjava.so+0x22125] JNU_GetStringPlatformChars+0x125
C  [libjava.so+0xedcd]
Java_java_lang_ClassLoader_00024NativeLibrary_findBuiltinLib+0x8b
j

java.lang.ClassLoader$NativeLibrary.findBuiltinLib(Ljava/lang/String;)Ljava/lang/String;+0 j java.lang.ClassLoader.loadLibrary0(Ljava/lang/Class;Ljava/io/File;)Z+4
j

java.lang.ClassLoader.loadLibrary(Ljava/lang/Class;Ljava/lang/String;Z)V+228
j
java.lang.Runtime.loadLibrary0(Ljava/lang/Class;Ljava/lang/String;)V+54
j  java.lang.System.loadLibrary(Ljava/lang/String;)V+7
j  java.lang.System.initializeSystemClass()V+113
v  ~StubRoutines::call_stub
V  [libjvm.so+0x9d279a] JavaCalls::call_helper(JavaValue*,
methodHandle*,
JavaCallArguments*, Thread*)+0x6b4
V [libjvm.so+0xcad591] os::os_exception_wrapper(void (*)(JavaValue*, methodHandle*, JavaCallArguments*, Thread*), JavaValue*, methodHandle*,
JavaCallArguments*, Thread*)+0x45
V  [libjvm.so+0x9d20cf]  JavaCalls::call(JavaValue*, methodHandle,
JavaCallArguments*, Thread*)+0x8b
V [libjvm.so+0x9d1d3b] JavaCalls::call_static(JavaValue*, KlassHandle,
Symbol*, Symbol*, JavaCallArguments*, Thread*)+0x139
V [libjvm.so+0x9d1e3f] JavaCalls::call_static(JavaValue*, KlassHandle,
Symbol*, Symbol*, Thread*)+0x9d
V  [libjvm.so+0xe3cceb] call_initializeSystemClass(Thread*)+0xb0
V  [libjvm.so+0xe44444]
Threads::initialize_java_lang_classes(JavaThread*,
Thread*)+0x21a
V [libjvm.so+0xe44b12] Threads::create_vm(JavaVMInitArgs*, bool*)+0x4a6
V  [libjvm.so+0xa19bd7]  JNI_CreateJavaVM+0xc7
C  [libjli.so+0xa520]  InitializeJVM+0x154
C  [libjli.so+0x8024]  JavaMain+0xcc





Reply via email to