Javaのアセンブラ/逆アセンブラをLispで作った
Javaのアセンブラと逆アセンブラをCommon Lispで作りました。
一部対応してない命令がありますが、大体動作します。
アセンブリはもちろんS式で記述します。読み込むときはreadするだけ。
オペランドのない命令はアトム、オペランド付きの命令はリストとなっています。
とりあえずhello world。
;; ljTest.lja
(class "ljTest" "java/lang/Object" (public super)
method
("<init>" "()V" (public)
aload_0
(invokespecial "java/lang/Object" "" "()V")
return)
method
("main" "([Ljava/lang/String;)V" (public static)
(meta max-stack 2)
(getstatic "java/lang/System" "out" "Ljava/io/PrintStream;")
(ldc "hello world")
(invokevirtual "java/io/PrintStream" "println" "(Ljava/lang/String;)V")
return))
hello worldだけなのに長いです。さすがJava。
<init>というのはコンストラクタです。
% clisp > (load "ljasm.lisp") > (assemble-file "ljTest.java") > (exit) % java ljTest hello world
こんな感じに動作します。
次は逆アセンブル。
// le.java
public class le{
public static void main(String args[]) {
try {
for (int i=5; i>=0; i--) {
System.out.println(100 / i);
}
} catch (ArithmeticException e) {
System.out.println("Nice exception...");
}
}
}
ループと例外が使われています。
% javac le.java % clisp > (load "ljasm.lisp") > (disassemble-file "le.class") > (exit)
これで次のようなファイルが作られます。
(整形は私が手作業でやりました)
(CLASS "le" "java/lang/Object" (PUBLIC SUPER)
INTERFACE NIL
METHOD
("" "()V" (PUBLIC)
(META MAX-STACK 1)
(META MAX-LOCAL 1)
ALOAD_0
(INVOKESPECIAL "java/lang/Object" "" "()V")
RETURN)
METHOD
("main" "([Ljava/lang/String;)V" (PUBLIC STATIC)
(META MAX-STACK 3)
(META MAX-LOCAL 2)
:L0
ICONST_5 ISTORE_1
:L2
ILOAD_1 (IFLT :L22)
(GETSTATIC "java/lang/System" "out" "Ljava/io/PrintStream;")
(BIPUSH 100) ILOAD_1 IDIV
(INVOKEVIRTUAL "java/io/PrintStream" "println" "(I)V")
(IINC 1 255) (GOTO :L2)
:L22
(GOTO :L34)
:L25
ASTORE_1 ;store exception object
(GETSTATIC "java/lang/System" "out" "Ljava/io/PrintStream;")
(LDC "Nice exception...")
(INVOKEVIRTUAL "java/io/PrintStream" "println" "(Ljava/lang/String;)V")
:L34
RETURN
(META EXCEPTION :L0 :L22 :L25 "java/lang/ArithmeticException"))
キーワードはラベルと見なされます。
逆アセンブリするときはラベル名は自動生成されます。
例外の指定は非常に地味です。 (META EXCEPTION …) の
最初の:L0と:L22は例外を捕まえる範囲、:L25は例外ハンドラです。
という訳で、それっぽく動いています。
めでたしめでたし。
面白そうですね。私もJDKが出た頃、まずはということで :-? 逆アセンブラをCLで作りました。仕様を読みながら、defbc というマクロ上にバイトコード仕様を表現していって、defbc の定義は後から逆アセンブラ用に書くという手順です。
定義といっても必要な情報を表に格納するくらいなんですが、仕様の表記をスタックの使用前・使用後のパターンとかの素なものにしておいて、そこから情報を導出してたのが CLならではかもしれません。
後で定義を変えて最適化アセンブラ!、は目論見だけでしませんでしたが、こちらの分野では、McGillのJimple( http://citeseer.ist.psu.edu/old/rai98jimple.html , Sootプロジェクトはまだ続いているっぽい!)の前身が96年くらいには出てた気がします。