Skip to content

Commit 85fac39

Browse files
committed
Static-compatible init of JNA native layer
When operating under static linkage in SVM (Native Image), JNA's `JNI_OnLoad` hooks are not run. We need to sanity-check at the first JNI border and run static initialization manually. Additionally, `JNI_OnLoad` should be provided in static contexts as `JNI_OnLoad_jnidispatch`. This changeset fixes both issues. Signed-off-by: Sam Gammon <[email protected]> Signed-off-by: Dario Valdespino <[email protected]>
1 parent 5beedfc commit 85fac39

File tree

4 files changed

+90
-19
lines changed

4 files changed

+90
-19
lines changed

Diff for: common.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<property name="jni.revision" value="2"/>
2828
<property name="jni.build" value="0"/> <!--${build.number}-->
2929
<property name="jni.version" value="${jni.major}.${jni.minor}.${jni.revision}"/>
30-
<property name="jni.md5" value="5fb98531302accd485c534c452dd952a"/>
30+
<property name="jni.md5" value="eea709d4f4531903eb04ab51ff3840ce"/>
3131
<property name="spec.title" value="Java Native Access (JNA)"/>
3232
<property name="spec.vendor" value="${vendor}"/>
3333
<property name="spec.version" value="${jna.major}"/>

Diff for: native/Makefile

+21-1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ JAVA_INCLUDES=-I"$(JAVA_HOME)/include" \
6262
BUILD=../build/native
6363
JAVAH=$(BUILD)
6464
INSTALLDIR=../build/$(OS)
65+
JNA_STATIC=no
6566
JNIDISPATCH_OBJS=$(BUILD)/dispatch.o $(BUILD)/callback.o $(EXTRAOBJS)
6667
RSRC=$(BUILD)/rsrc.o
6768
DLLCB=$(BUILD)/dll-callback.o
@@ -342,6 +343,10 @@ ifeq ($(ARCH),ppc)
342343
endif
343344
endif
344345

346+
ifeq ($(JNA_STATIC),yes)
347+
COPT+=-DJNA_STATIC=1
348+
endif
349+
345350
# CC_OPTS only applied to objects build for jnidispatch, not for libffi
346351
# -Wno-unknown-warning-option
347352
# => Suppress warning for unknown warnings
@@ -497,8 +502,23 @@ $(LIBRARY): $(JNIDISPATCH_OBJS) $(FFI_LIB)
497502
$(LD) $(LDFLAGS) $(JNIDISPATCH_OBJS) $(FFI_LIB) $(LIBS)
498503
$(STRIP) $@
499504

500-
$(LIBRARY_STATIC): $(JNIDISPATCH_OBJS) $(FFI_LIB)
505+
static-objs: $(FFI_LIB) $(JNIDISPATCH_OBJS)
506+
@# the rebuild enters here
507+
508+
$(LIBRARY_STATIC): $(LIBRARY) $(JNIDISPATCH_OBJS) $(FFI_LIB)
509+
@# we force a rebuild of the JNI dispatch objects, but in static mode. this is safe
510+
@# because they are cleaned afterward, and `$(LIBRARY)` is guaranteed to have finished
511+
@# by now, along with `$(JNIDISPATCH_OBJS)`. we replace the objects again once we are
512+
@# finished, to avoid poisoning the shared library on the next build.
513+
@#
514+
@# this looks like it may be trickery, but it really isn't. see `dispatch.c` for how the
515+
@# macro is used; it causes `JNI_OnLoad` to be exported as `JNI_OnLoad_jnidispatch` etc
516+
@# instead, so that it can safely be loaded for static JNI.
517+
rm -f $(JNIDISPATCH_OBJS)
518+
$(MAKE) JNA_STATIC=yes static-objs
501519
$(AR) rcs $@ $(JNIDISPATCH_OBJS)
520+
rm -f $(JNIDISPATCH_OBJS)
521+
$(MAKE) STATIC=no static-objs
502522

503523
$(TESTLIB): $(BUILD)/testlib.o
504524
$(LD) $(LDFLAGS) $< $(LIBS)

Diff for: native/dispatch.c

+52-17
Original file line numberDiff line numberDiff line change
@@ -3314,6 +3314,20 @@ is_protected() {
33143314
return JNI_FALSE;
33153315
}
33163316

3317+
jint initializeJnaStatics(JNIEnv *env) {
3318+
int result = JNI_VERSION_1_4;
3319+
const char* err;
3320+
if ((err = JNA_init(env)) != NULL) {
3321+
fprintf(stderr, "JNA: Problems loading core IDs: %s\n", err);
3322+
result = 0;
3323+
}
3324+
else if ((err = JNA_callback_init(env)) != NULL) {
3325+
fprintf(stderr, "JNA: Problems loading callback IDs: %s\n", err);
3326+
result = 0;
3327+
}
3328+
return result;
3329+
}
3330+
33173331
JNIEXPORT jboolean JNICALL
33183332
Java_com_sun_jna_Native_isProtected(JNIEnv *UNUSED(env), jclass UNUSED(classp)) {
33193333
return is_protected();
@@ -3335,6 +3349,10 @@ Java_com_sun_jna_Native_getNativeVersion(JNIEnv *env, jclass UNUSED(classp)) {
33353349
#ifndef JNA_JNI_VERSION
33363350
#define JNA_JNI_VERSION "undefined"
33373351
#endif
3352+
// sanity check initialization
3353+
if (classString == NULL) {
3354+
initializeJnaStatics(env);
3355+
}
33383356
return newJavaString(env, JNA_JNI_VERSION, CHARSET_UTF8);
33393357
}
33403358

@@ -3346,39 +3364,46 @@ Java_com_sun_jna_Native_getAPIChecksum(JNIEnv *env, jclass UNUSED(classp)) {
33463364
return newJavaString(env, CHECKSUM, CHARSET_UTF8);
33473365
}
33483366

3349-
JNIEXPORT jint JNICALL
3350-
JNI_OnLoad(JavaVM *jvm, void *UNUSED(reserved)) {
3367+
JNIEXPORT jboolean JNICALL Java_com_sun_jna_Native_isStaticEnabled(JNIEnv * UNUSED(env), jclass UNUSED(classp)) {
3368+
return JNI_TRUE;
3369+
}
3370+
3371+
JNIEXPORT jint JNICALL Java_com_sun_jna_Native_initializeStatic(JNIEnv *env, jclass UNUSED(classp)) {
3372+
return initializeJnaStatics(env);
3373+
}
3374+
3375+
jint setupJna(JavaVM *jvm) {
33513376
JNIEnv* env;
3352-
int result = JNI_VERSION_1_4;
33533377
int attached = (*jvm)->GetEnv(jvm, (void *)&env, JNI_VERSION_1_4) == JNI_OK;
3354-
const char* err;
3355-
33563378
if (!attached) {
33573379
if ((*jvm)->AttachCurrentThread(jvm, (void *)&env, NULL) != JNI_OK) {
33583380
fprintf(stderr, "JNA: Can't attach native thread to VM on load\n");
33593381
return 0;
33603382
}
33613383
}
3362-
3363-
if ((err = JNA_init(env)) != NULL) {
3364-
fprintf(stderr, "JNA: Problems loading core IDs: %s\n", err);
3365-
result = 0;
3366-
}
3367-
else if ((err = JNA_callback_init(env)) != NULL) {
3368-
fprintf(stderr, "JNA: Problems loading callback IDs: %s\n", err);
3369-
result = 0;
3370-
}
3384+
int result = initializeJnaStatics(env);
33713385
if (!attached) {
33723386
if ((*jvm)->DetachCurrentThread(jvm) != 0) {
33733387
fprintf(stderr, "JNA: could not detach thread on initial load\n");
33743388
}
33753389
}
3376-
33773390
return result;
33783391
}
33793392

3380-
JNIEXPORT void JNICALL
3381-
JNI_OnUnload(JavaVM *vm, void *UNUSED(reserved)) {
3393+
#ifdef JNA_STATIC
3394+
JNIEXPORT jint JNICALL
3395+
JNI_OnLoad_jnidispatch(JavaVM *jvm, void *UNUSED(reserved)) {
3396+
setupJna(jvm);
3397+
return JNI_VERSION_1_8; // upgrade to JNI_VERSION_1_8; required for static JNI
3398+
}
3399+
#else
3400+
JNIEXPORT jint JNICALL
3401+
JNI_OnLoad(JavaVM *jvm, void *UNUSED(reserved)) {
3402+
return setupJna(jvm);
3403+
}
3404+
#endif
3405+
3406+
void unloadJna(JavaVM *vm) {
33823407
jobject* refs[] = {
33833408
&classObject, &classClass, &classMethod,
33843409
&classString,
@@ -3443,6 +3468,16 @@ JNI_OnUnload(JavaVM *vm, void *UNUSED(reserved)) {
34433468
}
34443469
}
34453470

3471+
#ifdef JNA_STATIC
3472+
JNIEXPORT void JNICALL JNI_OnUnload_jnidispatch(JavaVM *jvm, void *UNUSED(reserved)) {
3473+
unloadJna(jvm);
3474+
}
3475+
#else
3476+
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *UNUSED(reserved)) {
3477+
unloadJna(jvm);
3478+
}
3479+
#endif
3480+
34463481
JNIEXPORT void JNICALL
34473482
Java_com_sun_jna_Native_unregister(JNIEnv *env, jclass UNUSED(ncls), jclass cls, jlongArray handles) {
34483483
jlong* data = (*env)->GetLongArrayElements(env, handles, NULL);

Diff for: src/com/sun/jna/Native.java

+16
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.io.IOException;
3434
import java.io.InputStream;
3535
import java.io.UnsupportedEncodingException;
36+
import java.lang.System;
3637
import java.lang.ref.Reference;
3738
import java.lang.ref.WeakReference;
3839
import java.lang.reflect.Array;
@@ -947,6 +948,18 @@ private static void loadNativeDispatchLibrary() {
947948
}
948949

949950
String libName = System.getProperty("jna.boot.library.name", "jnidispatch");
951+
952+
// if in static init mode, load the library by name and force-run the initializer
953+
if (!Boolean.getBoolean("jna.skipStatic")) {
954+
try {
955+
System.loadLibrary(libName);
956+
assert isStaticEnabled();
957+
assert initializeStatic() == 0x00010004;
958+
return;
959+
} catch (UnsatisfiedLinkError err) {
960+
// continue
961+
}
962+
}
950963
String bootPath = System.getProperty("jna.boot.library.path");
951964
if (bootPath != null) {
952965
// String.split not available in 1.4
@@ -1198,6 +1211,8 @@ else if (!Boolean.getBoolean("jna.nounpack")) {
11981211
**/
11991212
private static native int sizeof(int type);
12001213

1214+
private static synchronized native boolean isStaticEnabled();
1215+
private static synchronized native int initializeStatic();
12011216
private static native String getNativeVersion();
12021217
private static native String getAPIChecksum();
12031218

@@ -2020,6 +2035,7 @@ public static void main(String[] args) {
20202035
? pkg.getImplementationVersion() : DEFAULT_BUILD;
20212036
if (version == null) version = DEFAULT_BUILD;
20222037
System.out.println("Version: " + version);
2038+
System.out.println(" Static: " + isStaticEnabled());
20232039
System.out.println(" Native: " + getNativeVersion() + " ("
20242040
+ getAPIChecksum() + ")");
20252041
System.out.println(" Prefix: " + Platform.RESOURCE_PREFIX);

0 commit comments

Comments
 (0)