Created
March 6, 2025 16:45
-
-
Save Kr328/2b3107cc5010211e4e0507304048f0ed to your computer and use it in GitHub Desktop.
A function to load JNI Invocation functions for executable binary in Android.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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