Graal: [native-image] linking error when having java.lang.Thread in reflection config

Created on 20 Nov 2019  路  3Comments  路  Source: oracle/graal

I have this in my reflection config:

{
    "name":"java.lang.Thread",
    "allPublicMethods":true,
    "allPublicFields": true,
    "allPublicConstructors": true
}

On macOS 10.14.6, GraalVM JDK 8 19.3.0 this fails with this output: https://github.com/borkdude/babashka/issues/121

Relevant parts:

Undefined symbols for architecture x86_64:
  "_Java_java_lang_Thread_countStackFrames", referenced from:
      ___svm_version_info in bb.o
ld: symbol(s) not found for architecture x86_64
Undefined symbols for architecture x86_64:
  "_Java_java_lang_Thread_countStackFrames", referenced from:
      ___svm_version_info in bb.o
ld: symbol(s) not found for architecture x86_64

It seems GraalVM is making deprecated methods reachable using this config, which then makes the linker fail.

I can work around the issue by enumerating things myself. I'm making a scripting environment where I use reflection to evaluate calls to a selected number of classes, that's why I need this and it's very convenient to not have to enumerate all methods explicitly.

native-image

Most helpful comment

Generating the JSON for all the method names is relatively easy from Clojure:

;; invoke with: clojure -Sdeps '{:deps {cheshire {:mvn/version "RELEASE"}}}' methods.clj <class>
;; where <class> is e.g. java.lang.Thread

(require '[cheshire.core :as cheshire])

(defn public-declared-method? [c m]
  (and (= c (.getDeclaringClass m))
       (not (.getAnnotation m Deprecated))))

(defn public-declared-method-names [c]
  (->> (.getMethods c)
       (keep (fn [m]
               (when (public-declared-method? c m)
                 {:name (.getName m)})) )
       (distinct )))

(def the-class
  (Class/forName (first *command-line-args*)))

(println
 (cheshire/generate-string (public-declared-method-names the-class)))
$ clojure -Sdeps '{:deps {cheshire {:mvn/version "RELEASE"}}}' methods.clj java.lang.Thread
[{"name":"run"},{"name":"toString"},{"name":"isInterrupted"},{"name":"currentThread"},{"name":"getName"},{"name":"join"},{"name":"getThreadGroup"},{"name":"getStackTrace"},{"name":"holdsLock"},{"name":"checkAccess"},{"name":"dumpStack"},{"name":"yield"},{"name":"setPriority"},{"name":"setDaemon"},{"name":"start"},{"name":"sleep"},{"name":"interrupt"},{"name":"interrupted"},{"name":"isAlive"},{"name":"getPriority"},{"name":"setName"},{"name":"activeCount"},{"name":"enumerate"},{"name":"isDaemon"},{"name":"getContextClassLoader"},{"name":"setContextClassLoader"},{"name":"getAllStackTraces"},{"name":"getId"},{"name":"getState"},{"name":"setDefaultUncaughtExceptionHandler"},{"name":"getDefaultUncaughtExceptionHandler"},{"name":"getUncaughtExceptionHandler"},{"name":"setUncaughtExceptionHandler"}]

All 3 comments

As stated in the issue, the workaround for the GraalVM 19.3 release and earlier is to not register all public methods of java.lang.Thread for reflection.

An alternative workaround is to pre-index the reflection members that you need during static init, and stick them in a map or whatever you need; then you can look them up from there at run time.

Generating the JSON for all the method names is relatively easy from Clojure:

;; invoke with: clojure -Sdeps '{:deps {cheshire {:mvn/version "RELEASE"}}}' methods.clj <class>
;; where <class> is e.g. java.lang.Thread

(require '[cheshire.core :as cheshire])

(defn public-declared-method? [c m]
  (and (= c (.getDeclaringClass m))
       (not (.getAnnotation m Deprecated))))

(defn public-declared-method-names [c]
  (->> (.getMethods c)
       (keep (fn [m]
               (when (public-declared-method? c m)
                 {:name (.getName m)})) )
       (distinct )))

(def the-class
  (Class/forName (first *command-line-args*)))

(println
 (cheshire/generate-string (public-declared-method-names the-class)))
$ clojure -Sdeps '{:deps {cheshire {:mvn/version "RELEASE"}}}' methods.clj java.lang.Thread
[{"name":"run"},{"name":"toString"},{"name":"isInterrupted"},{"name":"currentThread"},{"name":"getName"},{"name":"join"},{"name":"getThreadGroup"},{"name":"getStackTrace"},{"name":"holdsLock"},{"name":"checkAccess"},{"name":"dumpStack"},{"name":"yield"},{"name":"setPriority"},{"name":"setDaemon"},{"name":"start"},{"name":"sleep"},{"name":"interrupt"},{"name":"interrupted"},{"name":"isAlive"},{"name":"getPriority"},{"name":"setName"},{"name":"activeCount"},{"name":"enumerate"},{"name":"isDaemon"},{"name":"getContextClassLoader"},{"name":"setContextClassLoader"},{"name":"getAllStackTraces"},{"name":"getId"},{"name":"getState"},{"name":"setDefaultUncaughtExceptionHandler"},{"name":"getDefaultUncaughtExceptionHandler"},{"name":"getUncaughtExceptionHandler"},{"name":"setUncaughtExceptionHandler"}]
Was this page helpful?
0 / 5 - 0 ratings