Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8366737

LinkageError using ClassFile API with Instrumentation API

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P4 P4
    • tbd
    • 24
    • core-svc

      ADDITIONAL SYSTEM INFORMATION :
      java version "24.0.1" 2025-04-15
      Java(TM) SE Runtime Environment Oracle GraalVM 24.0.1+9.1 (build 24.0.1+9-jvmci-b01)
      Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 24.0.1+9.1 (build 24.0.1+9-jvmci-b01, mixed mode, sharing)

      Same issue with OpenJDK 24.0.1

      A DESCRIPTION OF THE PROBLEM :
      Context: I wanted to print out the call tree of a program by adding printlns when entering or exiting a method. I wrote a ClassFileTransformer that utilizes ClassFile API to instrument code. It runs as a Java Agent.

      Gist in [3] for reference.

      Problem: VM exits with LinkageError when attempting to transform Guava's ImmutableList and a few others (overall, a small minority of classes).

      Workaround: Either of the following works partially (i.e. skips instrumenting the troublesome classes)
      (1) Stop transforming a list of bad classes. I made a bad-list by repeatedly running the program finding a class that fails and adding to the bad-list. This was a small handful of classes out of 100s.
      (2) Change the ClassHierarchyResolver from the default (which uses System ClassLoader) to Platform ClassLoader. This still doesn't instrument the bad-list because ClassHierarchyResolver throws.

      What I see happening based on debugging
      (1) JVM invokes the Transformer, asking to transform the bytes representing ImmutableList
      (2) Transformer instruments the class bytes using ClassFile.of().transform(..) and returns it
      (3) Somewhere within ClassFile.of().transform(..), the default ClassHierarchyResolver is invoked which in-turn calls Class.forName("ImmutableList") [1]. This class-loads the ImmutableList class.
      (4) So, by the time the Transformer returns the instrumented code, the VM already has loaded ImmutableList
      (5) VM exits with LinkageError. The error seems to come from [2]
      (6) Even if Transformer detects ImmutableList has already been loaded with "instrumentation.getAllLoadedClasses()" and returns null (indicating it doesn't want to transform the class), the VM still exits

      I am not an expert in instrumentation but Class.forName(..) in (3) seems odd. Why does a class manipulation library have a dependency on classes in the running JVM? I can understand it for one-off transformations where all its dependencies are unchanging.

      [1]: https://github.com/openjdk/jdk/blob/6705a9255d28f351950e7fbca9d05e73942a4e27/src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java#L223
      [2]: https://github.com/openjdk/jdk/blob/6705a9255d28f351950e7fbca9d05e73942a4e27/src/hotspot/share/classfile/systemDictionary.cpp#L1638

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Package CallChainPrinterTransformer.java and CallContextPrinter.java from [1] with Agent-Launcher-Class manifest attribute set and launch it from command line with "-javaagent=.." on any java program that includes Guava's ImmutableList version 31.1


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      No errors, classes are transformed.
      ACTUAL -
      LinkageError as in [1]

            sspitsyn Serguei Spitsyn
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: