コンパイラの解析 (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 2026 ExpyDoc