ProguardでJNIのnativeメソッドが削除された

Androidをビルドしたら、ProguardがJNIのnativeメソッドを削除したため実行時にエラーが発生したという件に関する@tetsu_kobaさんと@kmt_tさんとのやり取り。
12
l.b. @l_b__

アプリが起動しないという連絡を受け、確認したログを元にパッケージから取り出したclassをjavapにかけた。やはりソースにあるメソッドが消えている... どんなオプションでビルドしたらこんなことになるんだろう。

2011-01-13 21:58:37
おっさん @kmt_t

冷蔵庫開けたらプリンとか入ってないかな。入ってないけど。

2011-01-15 00:16:40
l.b. @l_b__

そーいや、アプリが起動できなかった件、Proguardのせいだった。Proguardが未使用なPublicメソッドを削除→そのメソッドが呼ぶprivate nativeなメソッドも削除。でJava側は削除されるがJNIで呼ばれるC側は変更なし。

2011-01-15 00:22:08
koba @tetsu_koba

あ、冷蔵庫にプリンが入っているのを思い出した。食べよう。

2011-01-15 00:24:00
koba @tetsu_koba

呼ばれないなら問題無いのでは? @l_b__ アプリが起動できなかった件、Proguardのせいだった。Proguardが未使用なPublicメソッドを削除→そのメソッドが呼ぶprivate nativeなメソッドも削除。でJava側は削除されるがJNIで呼ばれるC側は変更なし。

2011-01-15 00:27:37
l.b. @l_b__

で、アプリを起動→DalvikがJNI_OnLoadを実行→レジストされているCとJavaの関数をチェック→C側はあるが、対応するJava側が無い→エラー。起動できませんという罠に嵌っていたのでした。Proguardが何をどこまでやるのか調べておかないとなあ。

2011-01-15 00:24:25
koba @tetsu_koba

なるほど。 @l_b__ で、アプリを起動→DalvikがJNI_OnLoadを実行→レジストされているCとJavaの関数をチェック→C側はあるが、対応するJava側が無い→エラー。起動できませんという罠に嵌っていたのでした。Proguardが何をどこまでやるのか調べておかないと

2011-01-15 00:29:02
おっさん @kmt_t

JavaのJNIって確かメソッド名の規約でJava側とネイティブ側を結びつけてるんじゃなかったっけ。Dalvikって違うの?

2011-01-15 00:31:08
koba @tetsu_koba

ほうほう。 @Tennetiss うちもNDKでの開発にVS使ってますなあ / I learned something today: Android Debugging, Visual Studio style! http://htn.to/gRak3x

2011-01-15 00:31:52
おっさん @kmt_t

ちなみになぜ自分の冷蔵庫にプリンが入ってないかというと、買ってもその日のうちに食べてしまうから。自分は食料の備蓄がでいない性格らしい。

2011-01-15 00:33:33
koba @tetsu_koba

明示的に登録する方法もある。これもJNIの正規な方法でDalvik独自じゃない。 @kmt_t JavaのJNIって確かメソッド名の規約でJava側とネイティブ側を結びつけてるんじゃなかったっけ。Dalvikって違うの?

2011-01-15 00:33:47
l.b. @l_b__

Dalvikは通常のJNIの他に、NativeとJavaを紐付けてレジストする方法もあるんですよ。RT @kmt_t: JavaのJNIって確かメソッド名の規約でJava側とネイティブ側を結びつけてるんじゃなかったっけ。Dalvikって違うの?

2011-01-15 00:34:10
l.b. @l_b__

.@kmt_t Dalvikのソース内ドキュメントに解説あります。 http://ht.ly/3DU4L

2011-01-15 00:36:41
おっさん @kmt_t

@tetsu_koba さん、@l_b_ さん、ご教授ありがとうございます。なるほど、JNIまわりは全然ソースが読めてないので知りませんでした。

2011-01-15 00:37:05
おっさん @kmt_t

@l_b__ さん、JNI_OnLoadで>RegisterNativesしろって書いてありますね。

2011-01-15 00:39:18
l.b. @l_b__

正規な仕様だったのか。RT @tetsu_koba: 明示的に登録する方法もある。これもJNIの正規な方法でDalvik独自じゃない。 @kmt_t JavaのJNIって確かメソッド名の規約でJava側とネイティブ側を結びつけてるんじゃなかったっけ。Dalvikって違うの?

2011-01-15 00:39:33
l.b. @l_b__

@kmt_t で、C関数とJavaメソッドを紐付けた配列を記述しておいたのですが、Java側がProguardに消されちゃってDalvikの起動時チェックにエラーだったんです。関数規約での宣言なら起動時チェックは無いのでエラー起きなかったと思います。

2011-01-15 00:42:11
koba @tetsu_koba

そうだね。 @l_b__ @kmt_t @kmt_t で、C関数とJavaメソッドを紐付けた配列を記述しておいたのですが、Java側がProguardに消されちゃってDalvikの起動時チェックにエラーだったんです。関数規約での宣言なら起動時チェックは無いのでエラー起きなかったと

2011-01-15 00:44:05
おっさん @kmt_t

@l_b__ さん、理解できました。Proguardの仕様がよくないっぽいですね。

2011-01-15 00:44:07
koba @tetsu_koba

RegisterNativesで登録するならJNI_OnLoadの中でやれって感じかな。 @kmt_t @l_b__ JNI_OnLoadで>RegisterNativesしろって書いてありますね。

2011-01-15 00:49:20
l.b. @l_b__

@kmt_t 使ってないからってデフォルトでPublicメソッドを消すのってどうよって感じです。それでもJavaだけなら問題なかったんですが、JNI周りはやはり難読化ツールの嵌るところですね。

2011-01-15 00:49:27
l.b. @l_b__

その辺りはFrameworkのJNI使っているところにたくさんサンプルになる実装ありますね。@tetsu_koba: RegisterNativesで登録するならJNI_OnLoadの中でやれって感じかな。 @kmt_t JNI_OnLoadで>RegisterNativesしろ

2011-01-15 00:51:55
koba @tetsu_koba

DalvikVMのコアクラスのnativeメソッドは全てRegisterNativesの方法を使っている。こっちだとダイナミックリンカの機能を使わないので、スタティックリンクしている場合でも大丈夫。命名規則の方法だと必ずダイナミックリンクライブラリにする必要があるはず。

2011-01-15 00:52:59
おっさん @kmt_t

@tetsu_koba さん、そうですね(^^; JNIの仕様ってかならずしも自然な感じではない気がします。実装の都合で用意されているAPIも多い印象ですね。

2011-01-15 00:59:22
koba @tetsu_koba

10数年前にダイナミックリンクのしくみがないOSの上にJavaVMの載せてJNIを実装したことがある。そのときは関数名から関数のアドレスを求める仕組みを独自でつくった。

2011-01-15 00:59:50