コンパイラの解析 (2) GCJのデータ構造 - 1 Suguru ARAKAWA Faculty of Computer and Information Sciences, Hosei University Table of Contents おさらい Cygnus Native Interface インスタンス構造の推測 Virtual Method Table Instance Fields おさらい (1) GNU Java Compiler (gcj)が使用するJavaの 実行時ライブラリ Java VM + Java APIをコンパイルしたもの これを外側から使用すれば、Javaコンパイラの 作成が可能 おさらい (2) ちょっとしたルールさえ知っていれば、JavaのAPI をC言語からも使える 例:sin.java.lang.Math.sinの外部利用 (2) 実行例 double _ZN4java4lang4Math3sinEd(double); int main() { printf("sin(3.14) = %lf\n", _ZN4java4lang4Math3sinEd(3.14)); } おさらい (3) Javaの機能を全て実現するには、下記のことも考慮し なければならない Javaの名前空間とオブジェクトファイルの名前空間 クラスの登録 クラスの初期化 インスタンスの生成 ポリモーフィズムの実現 配列の扱い インスタンスの破棄 ガーベジコレクタとの調和 例外の処理 synchronizeの処理 Cygnus Native Interface (CNI) GCJでC++を使ってネィティブメソッドを書く方法 JNIより効率よくメソッド呼び出し 移植性は低い (GCJでしか動かない) CNIの利用 (1) まずは単純なクラスを作成 nativeメソッドとして宣言 public class Cni { public static native void main(String[] args); } CNIの利用 (2) 次のコマンドを入力 javac Cni.java gcjh Cni javac Cni.java クラスファイルを生成 (gcj –C Cni.java と同様) gcjh Cni CniクラスのCNIヘッダを生成 CNIの利用 (3) 以下のような宣言を含むヘッダが生成される class Cni : public ::java::lang::Object { public: Cni (); static void main (JArray< ::java::lang::String *> *); static ::java::lang::Class class$; }; CNIの利用 (4) 次のように書ける #include "Cni.h" #include <stdio.h> #include <gcj/array.h> #include <java/lang/String.h> void Cni::main(JArray<java::lang::String *> *args) { puts("Hello, CNI!"); } CNIの利用 (5) コンパイルして実行 $ gcj --main=Cni Cni.class natCni.cc $ ./a.out Hello, CNI! 仮説 同じようなオブジェクトコードを生成すればJava から呼び出せる C言語からJavaの関数を呼び出せるのは前述 の通り 問題点 GCJはJavaをC言語に変換できない -S でアセンブルコードには変換できる アセンブラを読む/オブジェクトコードを読む デバッガも併用 大規模なので相当がんばる必要あり インスタンス構造の推測 インスタンスは2つの情報を持つ インスタンスごとに固有の情報 インスタンスフィールド 同じクラスのインスタンスで共通の情報 (instance of java.lang.Class) Virtual Method Table (vtable) クラスの情報 For your information… 古いOfficial Java VMでのインスタンス構造 Instance Fields + Instance Methods Virtual Method Table (VTable) 仮想メソッド(Javaの普通のメソッド)を呼び出す ためのテーブル ポリモーフィズムの実現に Interface Method Tableもある VTableの構造 (1) 基底クラスでは通常のメソッドテーブル call obj->vtable[5] : Object::toString() VTableの構造 (2) オーバーライドしたメソッドはテーブルを上書き call obj->vtable[6] : Hoge::toString() VTableの構造 (3) 同じシーケンスで子クラスのメソッドを呼べる インスタンスごとに何を呼ぶのか変動する Virtual Method呼び出しのトレース Hoge.javaをコンパイルして追ってみる Hoge::foo _ZN4Hoge3fooEv: ... (prologue) movl (%eax), %edx addl $20, %edx >vtable[5]) pushl %eax movl (%edx), %eax call *%eax ; ; ; ; Hoge::foo() eax = this edx = this->vtable edx = &(this- ; args[0] = this ; eax = this->vtable[5] ; call this->vtable[5] 解析1 – VTableを探す (1) どうやって解析しよう? VTableを探す (2) 下記(Hoge.s:34)にブレークポイントを貼る _ZN4Hoge3fooEv: ... (prologue) movl ; Hoge::foo() ; eax = this (%eax), %edx ; edx = this->vtable addl $20, %edx >vtable[5]) pushl %eax ; edx = &(this; args[0] = this movl (%edx), %eax ; eax = this->vtable[5] call *%eax ; call this->vtable[5] VTableを探す (3) Break Point + Examine Memory $ gdb a.out (gdb) break Hoge.s:34 (gdb) run Breakpoint 1, Hoge::foo () at Hoge.s:34 34 movl (%eax), %edx (gdb) x *$eax 0x80492c8 <_ZTVN4HogeE+8>: ... VTableを探す (4) Hoge.s:163 – 明らかにVTable _ZTVN4HogeE: .long 0 .long 0 .long _ZN4Hoge6class$E .long 4 .long _ZN4java4lang6Object8finalizeEv .long _ZN4java4lang6Object8hashCodeEv .long _ZN4java4lang6Object6equalsEPS1_ .long _ZN4Hoge8toStringEv .long _ZN4java4lang6Object5cloneEv .long _ZN4Hoge3fooEv インスタンスの構造 (1) ここまでは判明 解析2 – Instance Fieldを探す (1) 今度はどうする? Instance Fieldを探す (2) 方針 インスタンスフィールドを使うプログラムを用意 アセンブルファイルへとコンパイル ハンドトレース デバッガで確認 Instance Fieldを探す (3) 下記のようなクラスを作ってコンパイル gcj –S –O0 Foo.java # -> Foo.s public class Foo { int field0; int field1; void m0() { this.field0 = 123; } void m1() { this.field1 = 456; } } Instance Fieldを探す (4) ハンドトレースすると自明なものが登場 デバッガでトレースするまでも無い _ZN3Foo2m0Ev: .. (prologue) ; this.field0 = 123 ; eax = this movl $123, 4(%eax) ; *(int *)(this+4) = 123 _ZN3Foo2m1Ev: .. (prologue) ; this.field1 = 456 ; eax = this movl $456, 8(%eax) ; *(int *)(this+8) = 456 インスタンスの構造 (2) 最低限は判明 次回 興味のあるところから Javaの名前空間とオブジェクトファイルの名前空間 クラスの登録 クラスの初期化 インスタンスの生成 ポリモーフィズムの実現 : 今回分 配列の扱い インスタンスの破棄 ガーベジコレクタとの調和 例外の処理 synchronizeの処理
© Copyright 2024 ExpyDoc