Skip to content

Instantly share code, notes, and snippets.

@Kr328
Created March 6, 2025 16:45
Show Gist options
  • Save Kr328/2b3107cc5010211e4e0507304048f0ed to your computer and use it in GitHub Desktop.
Save Kr328/2b3107cc5010211e4e0507304048f0ed to your computer and use it in GitHub Desktop.
A function to load JNI Invocation functions for executable binary in Android.
#![allow(non_snake_case, non_camel_case_types)]
#![cfg(target_os = "android")]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
use core::ptr::NonNull;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("unsupported sdk version: {0}")]
UnsupportedSdkVersion(i32),
#[error("open library: {0}")]
OpenLibrary(String),
#[error("find symbol: {0}")]
FindSymbol(String),
#[error("create invocation failed")]
CreateInvocationFailed,
#[error("initial invocation failed")]
InitialInvocationFailed,
}
#[derive(Debug, Copy, Clone, Default)]
pub struct Options {
pub native_helper_lib: Option<&'static core::ffi::CStr>,
pub art_lib: Option<&'static core::ffi::CStr>,
}
pub type JavaVM = *const ();
pub type jsize = i32;
pub type jint = i32;
pub type c_void = core::ffi::c_void;
#[derive(Copy, Clone)]
pub struct JniInvocationFunctions {
pub JNI_GetDefaultJavaVMInitArgs: unsafe extern "C" fn(args: *mut c_void) -> jint,
pub JNI_GetCreatedJavaVMs: unsafe extern "C" fn(vmBuf: *mut *mut JavaVM, bufLen: jsize, nVMs: *mut jsize) -> jint,
pub JNI_CreateJavaVM: unsafe extern "C" fn(pvm: *mut *mut JavaVM, penv: *mut *mut c_void, args: *mut c_void) -> jint,
}
unsafe impl Send for JniInvocationFunctions {}
unsafe impl Sync for JniInvocationFunctions {}
fn get_sdk_version() -> i32 {
unsafe {
let mut buffer = [0u8; 32];
let n = libc::__system_property_get(c"ro.build.version.sdk".as_ptr(), buffer.as_mut_ptr().cast());
if n < 0 {
return -1;
}
core::str::from_utf8(&buffer[..n as usize])
.ok()
.and_then(|s| s.parse::<i32>().ok())
.unwrap_or(-1)
}
}
fn get_dlfn_error() -> Option<&'static str> {
unsafe {
let err = libc::dlerror();
if err.is_null() {
None
} else {
Some(core::ffi::CStr::from_ptr(err).to_str().unwrap())
}
}
}
fn open_library(name: &'static core::ffi::CStr) -> Result<NonNull<core::ffi::c_void>, Error> {
unsafe {
let handle = libc::dlopen(name.as_ptr(), libc::RTLD_NOW | libc::RTLD_GLOBAL);
if handle.is_null() {
return Err(Error::OpenLibrary(get_dlfn_error().unwrap_or("unknown error").to_string()));
}
Ok(NonNull::new(handle).unwrap())
}
}
fn find_symbol<R>(handle: NonNull<core::ffi::c_void>, name: &'static core::ffi::CStr) -> Result<R, Error> {
unsafe {
let ret = libc::dlsym(handle.as_ptr(), name.as_ptr());
if ret.is_null() {
return Err(Error::FindSymbol(get_dlfn_error().unwrap_or("unknown error").to_string()));
}
Ok((&raw const ret).cast::<R>().read())
}
}
pub fn load_jni_invocation_functions(options: Options) -> Result<JniInvocationFunctions, Error> {
let sdk_version = get_sdk_version();
if sdk_version < 31 {
return Err(Error::UnsupportedSdkVersion(sdk_version));
}
let handle = open_library(options.native_helper_lib.unwrap_or(c"libnativehelper.so"))?;
let funcs = JniInvocationFunctions {
JNI_GetDefaultJavaVMInitArgs: find_symbol(handle, c"JNI_GetDefaultJavaVMInitArgs")?,
JNI_GetCreatedJavaVMs: find_symbol(handle, c"JNI_GetCreatedJavaVMs")?,
JNI_CreateJavaVM: find_symbol(handle, c"JNI_CreateJavaVM")?,
};
let jni_invocation_create: extern "C" fn() -> *mut core::ffi::c_void = find_symbol(handle, c"JniInvocationCreate")?;
let jni_invocation_destroy: extern "C" fn(*mut core::ffi::c_void) = find_symbol(handle, c"JniInvocationDestroy")?;
let jni_invocation_init: extern "C" fn(*mut core::ffi::c_void, *const core::ffi::c_char) -> core::ffi::c_int =
find_symbol(handle, c"JniInvocationInit")?;
let jni_invocation_handle = jni_invocation_create();
if jni_invocation_handle.is_null() {
return Err(Error::CreateInvocationFailed);
}
let jni_invocation_init_ret = jni_invocation_init(
jni_invocation_handle,
options.art_lib.map(|s| s.as_ptr()).unwrap_or(core::ptr::null()),
);
if jni_invocation_init_ret == 0 {
jni_invocation_destroy(jni_invocation_handle);
return Err(Error::InitialInvocationFailed);
}
Ok(funcs)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment