溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何進行JNI的使用

發(fā)布時間:2022-01-06 18:47:02 來源:億速云 閱讀:160 作者:柒染 欄目:網(wǎng)絡安全

這篇文章給大家介紹如何進行JNI的使用,內(nèi)容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

Java Native Interface(JNI) 是一種使用java語言和原生C/C++語言相互調(diào)用、混合編程的方法,它允許在Java虛擬機(VM)內(nèi)運行的Java代碼與應用其他編程語言(如C、C++和匯編)編寫的應用程序和庫進行互操作,它支持從動態(tài)鏈接庫中加載代碼, 并能使用C/C++的高效的特性。

如果要基本了解JNI的功能與使用,可以閱讀Java Native Interface文檔https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html

同時,本文將介紹一些在可能在剛開始會注意不到的使用細節(jié)。

FindClass找不到類的問題

JNI類名由包名開始, 由'/'分隔,例如

”java/lang/String”

如果要查找的是一個數(shù)組類, 類名應為簽名形式,所以一個一維String數(shù)組的類定義為

[Ljava/lang/String;

可以參照https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp9502

但是目前即使全都傳入簽名形式的類名,F(xiàn)indClass依然會找到對應的類。原因是歷史遺留問題,參考https://bugs.openjdk.java.net/browse/JDK-6411605

為了保證正確性,可以在運行class文件時,加參數(shù)-Xcheck:jni可以校驗類名構(gòu)造是否規(guī)范。

javah生成的函數(shù)名不正確的問題

在函數(shù)重載和內(nèi)部類類型作為某個重載函數(shù)的參數(shù)時,會觸發(fā)javah生成的函數(shù)名不正確的問題,javah會按照JNI的名稱解析規(guī)則生成JNI函數(shù)名,但是JVM在運行時卻不會按照這個名字查找函數(shù),因此會出現(xiàn)運行時找不到函數(shù)的情況。

參考https://bugs.openjdk.java.net/browse/JDK-8145897

package p;

class A {

  static class B {}

  static class C {

    native static long Foo(B bar);

  }

}

運行javah -jni -o jni_libA.h -classpath ./ p.A

預期的輸出是:

//Method: Foo

//Signature: (Lp/A$B;)J

JNIEXPORT jlong JNICALLJava_p_A_00024C_Foo__Lp_A_00024B_2

但實際會輸出:

//Method: Foo

//Signature: (Lp/A/B;)J

JNIEXPORT jlong JNICALLJava_p_A_00024C_Foo__Lp_A_B_2

內(nèi)部類B的’$’被忽略。

可選的解決方法有兩種:

1.      不把有內(nèi)部類作為參數(shù)的重載函數(shù)寫入本地方法

2.      在生成方法后手動添加’$’(‘00024’)

JNI中的異常機制

在有異常等待處理時不能調(diào)用大多數(shù)的JNI方法。要通過函數(shù)ExceptionCheck或ExceptionOccurred的返回值預期到有異常并返回,或處理并清除異常。

如果要獲得異常的描述,需要找到Throwable類,然后調(diào)用它的getMessage "()Ljava/lang/String;"方法,用GetStringUTFChars來獲取內(nèi)容。

很多JNI方法會拋出異常,但有些異常不會被拋出,需要在應當拋出異常的地方添加條件判斷,用ThrowNew構(gòu)造異常,最常見的是java.lang.ArithmeticException與 Java.lang.NullPointerException

JNI中原生數(shù)組的取值與賦值

如果想對數(shù)組進行寫入或讀出,Get<Type>ArrayElements和GetStringChars是非常有用的方式。

Get<PrimitiveType>ArrayElements系列函數(shù)允許返回一個指向?qū)嶋H元素的指針,或者分配一些內(nèi)存來拷貝數(shù)據(jù)。 返回的原始數(shù)據(jù)的指針在調(diào)用釋放方法前是保證一直有效的,而且必須手動釋放每個獲取的數(shù)組。同時可以通過傳一個非空指針作為isCopy的參數(shù)來決定是否拷貝數(shù)據(jù)。

   jbyte* data = env->GetByteArrayElements(array, NULL);   

if (data != NULL) {

   memcpy(buffer, data, len);       

   env->ReleaseByteArrayElements(array, data, JNI_ABORT);   

}

上面的代碼是一個簡單的舉例,獲取了數(shù)組,然后拷貝了len長度的byte,最后釋放掉數(shù)組。

還有一個更簡單實現(xiàn)同樣功能的方式是

   env->GetByteArrayRegion(array, 0, len, buffer);

這種方式有幾個好處:

1.      只需要一個JNI調(diào)用,減少開銷.

2.      不需要對原始數(shù)據(jù)進行限制或者額外的拷貝數(shù)據(jù)

3.      減少在某些出錯后忘記釋放的風險

4.      會在越界時拋出異常

類似的,Set<Type>ArrayRegion系列方法是將數(shù)組中的元素賦值, GetStringRegion或者GetStringUTFRegion是獲得String中的字符。

通付盾SDK保護系統(tǒng)應用輸入為aar包,通過分析aar結(jié)構(gòu),將待保護的class文件轉(zhuǎn)換為AST(抽象語法樹),對AST進行同態(tài)翻譯,將其轉(zhuǎn)換為對應的本地代碼。本地化的轉(zhuǎn)換規(guī)則與AST節(jié)點數(shù)目一一對應。由于字節(jié)碼是托管代碼,運行于VM中(java代碼運行于JVM,安卓字節(jié)碼運行在Dalvik),對于操作VM內(nèi)存模型,以及涉及VM特性的行為,如創(chuàng)建對象,拋出異常等操作,本地代碼無法表達對應的語義。對于帶有這樣語義的AST節(jié)點,本地代碼的翻譯遵循JNI調(diào)用規(guī)范,在確保本地化翻譯的正確性的同時兼顧了兼容性。

關于如何進行JNI的使用就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

jni
AI