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年くらいには出てた気がします。
[…] CLでJavaのアセンブラ/逆アセンブラを作った CLでプログラムを書くのも大分慣れてきたおかげか、比較的楽に書くことができました。 […]