From 3923f7aa283d8b07655ad0d7030913cc06d0bff4 Mon Sep 17 00:00:00 2001 From: oiupoyt Date: Tue, 29 Sep 2026 22:11:13 +0530 Subject: [PATCH] feat(jni): implement synthetic JavaVM and JNIEnv dispatch tables --- crates/molotov-core/src/jni/env.rs | 102 +++++++++++++++++++ crates/molotov-core/src/jni/mod.rs | 4 + crates/molotov-core/src/jni/types.rs | 86 ++++++++++++++++ crates/molotov-core/src/jni/vm.rs | 59 +++++++++++ crates/molotov-core/src/lib.rs | 1 + crates/molotov-core/tests/jni_tables_test.rs | 18 ++++ 6 files changed, 270 insertions(+) create mode 100644 crates/molotov-core/src/jni/env.rs create mode 100644 crates/molotov-core/src/jni/mod.rs create mode 100644 crates/molotov-core/src/jni/types.rs create mode 100644 crates/molotov-core/src/jni/vm.rs create mode 100644 crates/molotov-core/tests/jni_tables_test.rs diff --git a/crates/molotov-core/src/jni/env.rs b/crates/molotov-core/src/jni/env.rs new file mode 100644 index 0000000..b947081 --- /dev/null +++ b/crates/molotov-core/src/jni/env.rs @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +#![allow(dead_code)] +use super::types::{JNIEnvPtr, JNINativeInterface, JNI_VERSION_1_6}; +use std::ptr; + +unsafe extern "C" fn jni_get_version(_env: JNIEnvPtr) -> i32 { + JNI_VERSION_1_6 +} + +unsafe extern "C" fn jni_exception_describe(_env: JNIEnvPtr) { + tracing::debug!("JNI: ExceptionDescribe called"); +} + +unsafe extern "C" fn jni_exception_clear(_env: JNIEnvPtr) { + tracing::debug!("JNI: ExceptionClear called"); +} + +pub struct JNIEnvWrapper { + table: Box, + table_ptr: *const JNINativeInterface, +} + +impl Default for JNIEnvWrapper { + fn default() -> Self { + Self::new() + } +} + +impl JNIEnvWrapper { + pub fn new() -> Self { + let table = Box::new(JNINativeInterface { + reserved0: ptr::null_mut(), + reserved1: ptr::null_mut(), + reserved2: ptr::null_mut(), + reserved3: ptr::null_mut(), + GetVersion: Some(jni_get_version), + DefineClass: ptr::null_mut(), + FindClass: None, + FromReflectedMethod: ptr::null_mut(), + FromReflectedField: ptr::null_mut(), + ToReflectedMethod: ptr::null_mut(), + GetSuperclass: ptr::null_mut(), + IsAssignableFrom: ptr::null_mut(), + ToReflectedField: ptr::null_mut(), + Throw: ptr::null_mut(), + ThrowNew: ptr::null_mut(), + ExceptionOccurred: ptr::null_mut(), + ExceptionDescribe: Some(jni_exception_describe), + ExceptionClear: Some(jni_exception_clear), + FatalError: ptr::null_mut(), + PushLocalFrame: ptr::null_mut(), + PopLocalFrame: ptr::null_mut(), + NewGlobalRef: None, + DeleteGlobalRef: None, + DeleteLocalRef: None, + IsSameObject: ptr::null_mut(), + NewLocalRef: ptr::null_mut(), + EnsureLocalCapacity: ptr::null_mut(), + AllocObject: ptr::null_mut(), + NewObject: ptr::null_mut(), + NewObjectA: ptr::null_mut(), + GetObjectClass: ptr::null_mut(), + IsInstanceOf: ptr::null_mut(), + GetMethodID: None, + CallObjectMethod: ptr::null_mut(), + CallBooleanMethod: ptr::null_mut(), + CallByteMethod: ptr::null_mut(), + CallCharMethod: ptr::null_mut(), + CallShortMethod: ptr::null_mut(), + CallIntMethod: ptr::null_mut(), + CallLongMethod: ptr::null_mut(), + CallFloatMethod: ptr::null_mut(), + CallDoubleMethod: ptr::null_mut(), + CallVoidMethod: ptr::null_mut(), + GetStaticMethodID: None, + CallStaticObjectMethod: ptr::null_mut(), + CallStaticBooleanMethod: ptr::null_mut(), + CallStaticByteMethod: ptr::null_mut(), + CallStaticCharMethod: ptr::null_mut(), + CallStaticShortMethod: ptr::null_mut(), + CallStaticIntMethod: ptr::null_mut(), + CallStaticLongMethod: ptr::null_mut(), + CallStaticFloatMethod: ptr::null_mut(), + CallStaticDoubleMethod: ptr::null_mut(), + CallStaticVoidMethod: ptr::null_mut(), + GetFieldID: ptr::null_mut(), + GetStaticFieldID: ptr::null_mut(), + NewStringUTF: None, + GetStringUTFLength: None, + GetStringUTFChars: None, + ReleaseStringUTFChars: None, + }); + + let table_ptr = &*table as *const JNINativeInterface; + + Self { table, table_ptr } + } + + pub fn as_raw_ptr(&self) -> JNIEnvPtr { + &self.table_ptr as *const *const JNINativeInterface as JNIEnvPtr + } +} diff --git a/crates/molotov-core/src/jni/mod.rs b/crates/molotov-core/src/jni/mod.rs new file mode 100644 index 0000000..02c0395 --- /dev/null +++ b/crates/molotov-core/src/jni/mod.rs @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pub mod env; +pub mod types; +pub mod vm; diff --git a/crates/molotov-core/src/jni/types.rs b/crates/molotov-core/src/jni/types.rs new file mode 100644 index 0000000..3dab55f --- /dev/null +++ b/crates/molotov-core/src/jni/types.rs @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +#![allow(non_snake_case, dead_code)] +use std::ffi::c_void; + +pub const JNI_OK: i32 = 0; +pub const JNI_ERR: i32 = -1; +pub const JNI_VERSION_1_6: i32 = 0x00010006; + +pub type JNIEnvPtr = *mut *const JNINativeInterface; +pub type JavaVMPtr = *mut *const JNIInvokeInterface; + +#[repr(C)] +pub struct JNIInvokeInterface { + pub reserved0: *mut c_void, + pub reserved1: *mut c_void, + pub reserved2: *mut c_void, + pub DestroyJavaVM: Option i32>, + pub AttachCurrentThread: Option i32>, + pub DetachCurrentThread: Option i32>, + pub GetEnv: Option i32>, + pub AttachCurrentThreadAsDaemon: Option i32>, +} + +#[repr(C)] +pub struct JNINativeInterface { + pub reserved0: *mut c_void, + pub reserved1: *mut c_void, + pub reserved2: *mut c_void, + pub reserved3: *mut c_void, + pub GetVersion: Option i32>, + pub DefineClass: *mut c_void, + pub FindClass: Option *mut c_void>, + pub FromReflectedMethod: *mut c_void, + pub FromReflectedField: *mut c_void, + pub ToReflectedMethod: *mut c_void, + pub GetSuperclass: *mut c_void, + pub IsAssignableFrom: *mut c_void, + pub ToReflectedField: *mut c_void, + pub Throw: *mut c_void, + pub ThrowNew: *mut c_void, + pub ExceptionOccurred: *mut c_void, + pub ExceptionDescribe: Option, + pub ExceptionClear: Option, + pub FatalError: *mut c_void, + pub PushLocalFrame: *mut c_void, + pub PopLocalFrame: *mut c_void, + pub NewGlobalRef: Option *mut c_void>, + pub DeleteGlobalRef: Option, + pub DeleteLocalRef: Option, + pub IsSameObject: *mut c_void, + pub NewLocalRef: *mut c_void, + pub EnsureLocalCapacity: *mut c_void, + pub AllocObject: *mut c_void, + pub NewObject: *mut c_void, + pub NewObjectA: *mut c_void, + pub GetObjectClass: *mut c_void, + pub IsInstanceOf: *mut c_void, + pub GetMethodID: Option *mut c_void>, + pub CallObjectMethod: *mut c_void, + pub CallBooleanMethod: *mut c_void, + pub CallByteMethod: *mut c_void, + pub CallCharMethod: *mut c_void, + pub CallShortMethod: *mut c_void, + pub CallIntMethod: *mut c_void, + pub CallLongMethod: *mut c_void, + pub CallFloatMethod: *mut c_void, + pub CallDoubleMethod: *mut c_void, + pub CallVoidMethod: *mut c_void, + pub GetStaticMethodID: Option *mut c_void>, + pub CallStaticObjectMethod: *mut c_void, + pub CallStaticBooleanMethod: *mut c_void, + pub CallStaticByteMethod: *mut c_void, + pub CallStaticCharMethod: *mut c_void, + pub CallStaticShortMethod: *mut c_void, + pub CallStaticIntMethod: *mut c_void, + pub CallStaticLongMethod: *mut c_void, + pub CallStaticFloatMethod: *mut c_void, + pub CallStaticDoubleMethod: *mut c_void, + pub CallStaticVoidMethod: *mut c_void, + pub GetFieldID: *mut c_void, + pub GetStaticFieldID: *mut c_void, + pub NewStringUTF: Option *mut c_void>, + pub GetStringUTFLength: Option i32>, + pub GetStringUTFChars: Option *const libc::c_char>, + pub ReleaseStringUTFChars: Option, +} diff --git a/crates/molotov-core/src/jni/vm.rs b/crates/molotov-core/src/jni/vm.rs new file mode 100644 index 0000000..778ec5a --- /dev/null +++ b/crates/molotov-core/src/jni/vm.rs @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +#![allow(dead_code)] +use super::env::JNIEnvWrapper; +use super::types::{JNIInvokeInterface, JavaVMPtr, JNI_OK, JNI_VERSION_1_6}; +use std::ffi::c_void; +use std::ptr; + +unsafe extern "C" fn jni_get_env(_vm: JavaVMPtr, penv: *mut *mut c_void, _version: i32) -> i32 { + if penv.is_null() { + return super::types::JNI_ERR; + } + // Return standard JNIEnv + let env = Box::new(JNIEnvWrapper::new()); + *penv = env.as_raw_ptr() as *mut c_void; + std::mem::forget(env); + JNI_OK +} + +unsafe extern "C" fn jni_attach_current_thread(_vm: JavaVMPtr, penv: *mut *mut c_void, _args: *mut c_void) -> i32 { + jni_get_env(_vm, penv, JNI_VERSION_1_6) +} + +unsafe extern "C" fn jni_detach_current_thread(_vm: JavaVMPtr) -> i32 { + JNI_OK +} + +pub struct JavaVMWrapper { + table: Box, + table_ptr: *const JNIInvokeInterface, +} + +impl Default for JavaVMWrapper { + fn default() -> Self { + Self::new() + } +} + +impl JavaVMWrapper { + pub fn new() -> Self { + let table = Box::new(JNIInvokeInterface { + reserved0: ptr::null_mut(), + reserved1: ptr::null_mut(), + reserved2: ptr::null_mut(), + DestroyJavaVM: None, + AttachCurrentThread: Some(jni_attach_current_thread), + DetachCurrentThread: Some(jni_detach_current_thread), + GetEnv: Some(jni_get_env), + AttachCurrentThreadAsDaemon: Some(jni_attach_current_thread), + }); + + let table_ptr = &*table as *const JNIInvokeInterface; + + Self { table, table_ptr } + } + + pub fn as_raw_ptr(&self) -> JavaVMPtr { + &self.table_ptr as *const *const JNIInvokeInterface as JavaVMPtr + } +} diff --git a/crates/molotov-core/src/lib.rs b/crates/molotov-core/src/lib.rs index 60ffa62..f00998e 100644 --- a/crates/molotov-core/src/lib.rs +++ b/crates/molotov-core/src/lib.rs @@ -2,6 +2,7 @@ pub mod audio; pub mod dynamic; pub mod elf; +pub mod jni; pub mod linker; pub mod mmap; pub mod reloc; diff --git a/crates/molotov-core/tests/jni_tables_test.rs b/crates/molotov-core/tests/jni_tables_test.rs new file mode 100644 index 0000000..aef46d4 --- /dev/null +++ b/crates/molotov-core/tests/jni_tables_test.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +use molotov_core::jni::env::JNIEnvWrapper; +use molotov_core::jni::types::JNI_VERSION_1_6; +use molotov_core::jni::vm::JavaVMWrapper; + +#[test] +fn test_synthetic_javavm_and_jnienv_version() { + let _vm = JavaVMWrapper::new(); + let env = JNIEnvWrapper::new(); + + unsafe { + let env_ptr = env.as_raw_ptr(); + let get_version = (**env_ptr).GetVersion; + assert!(get_version.is_some()); + let ver = get_version.unwrap()(env_ptr); + assert_eq!(ver, JNI_VERSION_1_6); + } +}