Skip to content
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

Trouble using Jansi with GraalVM native-image on MacOS #199

Closed
greenatatlassian opened this issue Mar 2, 2021 · 11 comments · May be fixed by gnodet/jansi#1
Closed

Trouble using Jansi with GraalVM native-image on MacOS #199

greenatatlassian opened this issue Mar 2, 2021 · 11 comments · May be fixed by gnodet/jansi#1

Comments

@greenatatlassian
Copy link

Looks like there was already some work done to address issue #162, but that doesn't seem to quite do everything that was required in my case to get things working with Jansi in a GraalVM native image.

The problem was that native-image build was running the code that finds and links the necessary native library image at build time, so the library was not linked at run time, resulting in an UnsatisfiedLinkError error at runtime:

Exception in thread "main" java.lang.UnsatisfiedLinkError: org.fusesource.jansi.internal.CLibrary.isatty(I)I [symbol: Java_org_fusesource_jansi_internal_CLibrary_isatty or Java_org_fusesource_jansi_internal_CLibrary_isatty__I]
	at com.oracle.svm.jni.access.JNINativeLinkage.getOrFindEntryPoint(JNINativeLinkage.java:153)
	at com.oracle.svm.jni.JNIGeneratedMethodSupport.nativeCallAddress(JNIGeneratedMethodSupport.java:57)
	at org.fusesource.jansi.internal.CLibrary.isatty(CLibrary.java)
        ...

I was able to overcome this by telling the native image that the relevant classes must be initialised at runtime rather than at build time. I achieved this by adding the following class to my codebase:

@AutomaticFeature
public class JansiFeature implements Feature {

    JansiFeature() { /* empty constructor required for Feature operation */ }

    @Override
    public void afterRegistration(AfterRegistrationAccess access) {
        RuntimeClassInitialization.initializeAtRunTime("org.fusesource.jansi.internal");
    }

    @Override
    public void beforeAnalysis(BeforeAnalysisAccess access) {
        JNIRuntimeAccess.register(CLibrary.class);
        JNIRuntimeAccess.register(CLibrary.class.getDeclaredFields());
    }
}

It may be possible to add the necessary configuration (either this file, or appropriate .json config files) to the Jansi library itself so that it will work correctly "out of the box" with native-image builds on GraalVM. If not, perhaps this will at least be useful to someone else who encounters the same problem.

@gnodet
Copy link
Member

gnodet commented Mar 16, 2021

Do you think you could provide a PR that would provide the needed json files ?

@greenatatlassian
Copy link
Author

Might take me a little while, but I'll try to put one together for you.

@williamwebb
Copy link

Spent some time trying to get this working with graalvm to no success.

I tested with
jansi: 2.3.4
graalvm-ce: 21.2.0
native image executed against: scratch, gcr.io/distroless/static

For reference, when I ran the graalvm native-image-agent against a jar using jansi I got the following ouptput.

resource-config.json

{
  "resources":{
    "includes":[
      {"pattern":"\\QMETA-INF/maven/org.fusesource.jansi/jansi/pom.properties\\E"},
      {"pattern":"\\Qorg/fusesource/jansi/internal/native/Linux/x86_64/libjansi.so\\E"}
    ]},
  "bundles":[]
}

jni-config.json

[
  {
    "name":"java.lang.ClassLoader",
    "methods":[
      {"name":"getPlatformClassLoader","parameterTypes":[] },
      {"name":"loadClass","parameterTypes":["java.lang.String"] }
    ]
  },
  {
    "name":"jdk.internal.loader.ClassLoaders$PlatformClassLoader"
  },
  {
    "name":"org.fusesource.jansi.internal.CLibrary",
    "fields":[
      {"name":"HAVE_ISATTY"},
      {"name":"HAVE_TTYNAME"},
      {"name":"TCSADRAIN"},
      {"name":"TCSAFLUSH"},
      {"name":"TCSANOW"},
      {"name":"TIOCGETD"},
      {"name":"TIOCGWINSZ"},
      {"name":"TIOCSETD"},
      {"name":"TIOCSWINSZ"}
    ]
  },
  {
    "name":"org.graalvm.nativebridge.jni.JNIExceptionWrapperEntryPoints",
    "methods":[{"name":"getClassName","parameterTypes":["java.lang.Class"] }]
  }
]

When running a graalvm native-image using these configs jansi does not work, with the following error printed.

Failed to load native library:jansi-2.3.4-65da82f66e242fcc-libjansi.so. osinfo: Linux/x86_64
java.lang.UnsatisfiedLinkError: Can't load library: /tmp/jansi-2.3.4-65da82f66e242fcc-libjansi.so

@williamwebb
Copy link

Further information
1.18 works with no error message
2.0 <-> 2.0.1 works, with error message

@gnodet gnodet closed this as completed in 2cf4461 Oct 13, 2021
@gnodet
Copy link
Member

gnodet commented Oct 13, 2021

With 2cf4461, I've been able to compile jansi jar to native using GraalVM CE 21.1.0 and run it successfully on OSX. I haven't had any needs for the delayed initialisation. I'll give it a try on the latest GraalVM to be sure.

@xtaixe
Copy link

xtaixe commented Sep 20, 2022

@gnodet FYI I'm hitting this with Jansi 2.4.0 and GraalVM 22.2.0 on an ARM Mac:

Jansi 2.4.0

library.jansi.path=
library.jansi.version=
Jansi native library loaded from /var/folders/qd/msrxtq5j1n9fg4bbsw4xgsbh0000gn/T/jansi-2.4.0-f98251e4f71e7e4-libjansi.jnilib
   which was auto-extracted from jar:file:/Users/xtaixe/dev/cli/app/target/native-image-source-jar/lib/org.fusesource.jansi.jansi-2.4.0.jar!/org/fusesource/jansi/internal/native/Mac/arm64/libjansi.jnilib

os.name= Mac OS X, os.version= 12.6, os.arch= aarch64
file.encoding= UTF-8
java.version= 17.0.4, java.vendor= Oracle Corporation, java.home= null

jansi.graceful=
jansi.mode=
jansi.out.mode=
jansi.err.mode=
jansi.colors=
jansi.out.colors=
jansi.err.colors=
jansi.passthrough= false
jansi.strip= false
jansi.force= false
jansi.noreset= false
org.fusesource.jansi.Ansi.disable= false

IS_WINDOWS: false

Exception in thread "main" java.lang.UnsatisfiedLinkError: org.fusesource.jansi.internal.CLibrary.isatty(I)I [symbol: Java_org_fusesource_jansi_internal_CLibrary_isatty or Java_org_fusesource_jansi_internal_CLibrary_isatty__I]
	at com.oracle.svm.jni.access.JNINativeLinkage.getOrFindEntryPoint(JNINativeLinkage.java:154)
	at com.oracle.svm.jni.JNIGeneratedMethodSupport.nativeCallAddress(JNIGeneratedMethodSupport.java:52)
	at org.fusesource.jansi.internal.CLibrary.isatty(CLibrary.java)

For now I haven't been able to apply the fix above and if I use --initialize-at-run-time=org.fusesource.jansi.internal.CLibrary I get Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class org.fusesource.jansi.internal.CLibrary.

I'll report if I find out more.

@xtaixe
Copy link

xtaixe commented Sep 20, 2022

Ah, using --initialize-at-run-time=org.fusesource.jansi.internal works.

According to https://medium.com/graalvm/updates-on-class-initialization-in-graalvm-native-image-generation-c61faca461f7, libraries can be shipped with these these command line options in native-image.properties files.

@xtaixe
Copy link

xtaixe commented Sep 20, 2022

Also, I see CLibrary does:

static {
        LOADED = JansiLoader.initialize();
        if (LOADED) {
            init();
        }
    }

If it's possible to move that and other similar initializations to non static blocks, that might also work.

@xtaixe
Copy link

xtaixe commented Sep 21, 2022

Still getting the same error on CI builds though:

Failed to load native library:jansi-2.4.0-ef27179001bc2f21-libjansi.so. osinfo: Linux/x86_64
java.lang.UnsatisfiedLinkError: Can't load library: /tmp/jansi-2.4.0-ef27179001bc2f21-libjansi.so

@rsenden
Copy link

rsenden commented Dec 19, 2022

Still getting the same error on CI builds though:

Failed to load native library:jansi-2.4.0-ef27179001bc2f21-libjansi.so. osinfo: Linux/x86_64
java.lang.UnsatisfiedLinkError: Can't load library: /tmp/jansi-2.4.0-ef27179001bc2f21-libjansi.so

@xtaixe Any chance you are building a statically linked image in your CI builds? If so, see #246 on why you may be seeing this error.

@xtaixe
Copy link

xtaixe commented Dec 19, 2022

@rsenden yes, that is correct. I recently discovered this was happening when static linking, but didn't have time to investigate further.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants