Skip to content
void-communityPublic

About

Generated JNI and JVMTI bindings for .NET, with NativeAOT agents and automated JVM compatibility testing.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

JvmBridge

CI License

Write JVM agents in C#. Generate the native bindings. Test the actual binaries.

JvmBridge combines generated JNI/JVMTI declarations, explicit Java reference ownership, and NativeAOT agent entry-point generation in one NuGet package. It works with ordinary Java applications and provides a standalone JVM-hosting API for .NET applications.

JvmBridge is published on NuGet, with companion packages: JvmBridge.Testing for xUnit tests, JvmBridge.Templates for a dotnet new agent template and JvmBridge.Logging for Microsoft.Extensions.Logging adapters. To start without writing a project file, run dotnet new install JvmBridge.Templates, then dotnet new jvmbridge-agent -n MyAgent.

A native agent in a few lines

Create a .NET 10 class library and reference JvmBridge:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <JvmBridgeAgent>true</JvmBridgeAgent>
    <PublishAot>true</PublishAot>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="JvmBridge" Version="<version>" />
  </ItemGroup>
</Project>

dotnet add package JvmBridge writes this line with the latest version from nuget.org; pin the one you test against. PublishAot is explicit because the SDK decides on NativeAOT while it evaluates the project, before the package's build assets can act on JvmBridgeAgent, so the package cannot imply it. Leaving it out fails the build with JVMB006; without that check dotnet publish would succeed and produce only managed assemblies, and no native library.

using JvmBridge.Agents;
using JvmBridge.Runtime;

public sealed class MyAgent : JavaAgent
{
    public override void OnLoad(AgentContext context)
    {
        context.Log("loaded with options: " + context.Options);
    }

    public override void OnVmInit(AgentContext context, JavaEnvironment environment)
    {
        using JavaLocalReference text = environment.NewString("Hello from C#!");
        context.Log(environment.GetString(text.Handle));
    }
}

No attribute or manually exported entry point is required. The generator finds the single concrete JavaAgent subclass and emits Agent_OnLoad, Agent_OnAttach, and Agent_OnUnload into your assembly. Override Configure to request capabilities before events are installed. OnAttach runs after callbacks are active, so it can immediately retransform existing classes. Abstract agent base classes are allowed; ambiguous or invalid agents produce compiler errors. context.Log writes to standard error without Console, which needs libicu to initialize.

Publish for the JVM process's operating system and architecture:

dotnet publish -c Release -r linux-arm64
java -agentpath:/absolute/path/MyAgent.so=hello -jar application.jar

Windows uses .dll; macOS uses .dylib; Linux uses .so. Native publishing requires the platform's C/C++ toolchain. The resulting library includes its .NET runtime; Java users do not need to install .NET. To load an agent through JAVA_TOOL_OPTIONS or into a running JVM, see Loading and attaching. A Linux agent needs the glibc of the machine that published it or newer; publish against .NET's sysroot for one that loads on glibc 2.17 and later, see Publishing for Linux.

On Unix HotSpot, use the JDK's signal-chaining library when hosting both runtimes in one process. For a typical Linux JDK:

LD_PRELOAD="$JAVA_HOME/lib/libjsig.so" java -agentpath:/absolute/path/MyAgent.so -jar application.jar

Locations differ across distributions and Java versions; Java 8 commonly places the library beneath jre/lib/<architecture>. macOS uses DYLD_INSERT_LIBRARIES with libjsig.dylib. The test runner discovers the correct library. Do not inject HotSpot's library into OpenJ9.

Capabilities

  • Generated native API: JNI/JVMTI types, constants, function-table entries, and callback signatures from pinned OpenJDK headers. C# raw declarations live in JvmBridge.Native and preserve native identifiers.
  • Runtime helpers: JVM creation, thread attachment, UTF-16 string conversion, modified UTF-8 names/options, checked field access, constructor and typed method calls, native registration, and owned local/global references.
  • Agent lifecycle: startup and late attachment, VM initialization/death, class preparation, copied JVMTI class/member metadata, capability negotiation, and retransformation.
  • Class files and patches: a read-only class-file model with named opcodes and instruction matchers, an editor whose edits keep every constant index and code offset, a writer and code builder for helper classes without byte literals, and declarative ClassPatches (redirects, constants, replaced constructions, removed fluent calls, added getters, setters and invokers, access flags, matchers, conditions on a method's code and Before/After method advice) with results a test can assert on, all without a JVM.
  • Helper classes: written in Java (JavaSource, compiled with javac against the application's jars or stub sources, with an optional pinned JDK download for machines that have none) or in C# ([JavaClass] and [JavaMethod]), in the bootstrap loader or the application's own.
  • Events and JVM state: thread, class-load, exception, method, garbage-collection and monitor events with capability negotiation, thread and stack wrappers, and class redefinition.
  • Java interop: arrays, exception throwing, monitors and local frames, [JavaMethod] with arrays, and typed proxies for the classes of a jar.
  • Logging and options: levels, a replaceable sink, an ILogger adapter and a parsed option string.
  • Testing: JvmBridge.Testing supplies checksum-pinned JDKs and Maven artifacts, javac and java runners, published-agent lookup, class-file assertions, disassembly snapshots, patches applied across a jar, and a link check of helpers against real classes.
  • Delivery and tooling: -agentpath, JAVA_TOOL_OPTIONS, the Attach API and java -javaagent with a jar from the jvmbridge command, which also disassembles classes and previews an agent's patches without a JVM.
  • One package: the runtime, source generator, and MSBuild integration ship together. Consumers do not need Clang or header files.
  • Real validation: CI publishes the example against the packed NuGet package and loads it into each available pinned JVM.

Documentation

Compatibility

The manifest tracks every NativeAOT target in scope and every Java major from 8 through 26, with HotSpot and OpenJ9 inventories. Agents also run on Java 6 and 7 from the same build when JavaRelease is 6 or 7; CI runs that on linux-x64 with Zulu 6 and 7, including glibc 2.12 (see Java 6 and 7). Archive availability varies by version and architecture. Mobile targets and platforms without a suitable JVM are explicitly experimental or unavailable.

See the generated compatibility inventory. It shows available downloads, not a blanket support claim. The matching commit's CI reports show which native builds and runtime tests actually passed. Historical patch releases are not exhaustively tested. JVM agent support does not follow automatically from .NET target support.

Examples

  • HelloAgent: emits a Java 8-compatible native callback helper in C#, registers native methods, preserves Unicode across threads/global references, transforms fixture method bodies, and supports late attachment.
  • LegacyAgent: one build for Java 6 to 26 (JavaRelease 6): a constant patch, a redirect to a C# helper and to a typed Java helper in the application's loader, replaced constructions, a removed fluent call, advice with a hook compiled from Java, events, and a thread attached from .NET.
  • JavaHost: creates a JVM from an explicit native-library path, invokes Java, handles exceptions, and demonstrates ownership.

See automation setup and agent instructions. Original code is MIT licensed; see upstream notices for header provenance.

About

Generated JNI and JVMTI bindings for .NET, with NativeAOT agents and automated JVM compatibility testing.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages