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は例外ハンドラです。
という訳で、それっぽく動いています。
めでたしめでたし。

2 Responses to “Javaのアセンブラ/逆アセンブラをLispで作った”

  1. nfunato より:

    面白そうですね。私もJDKが出た頃、まずはということで :-? 逆アセンブラをCLで作りました。仕様を読みながら、defbc というマクロ上にバイトコード仕様を表現していって、defbc の定義は後から逆アセンブラ用に書くという手順です。
    定義といっても必要な情報を表に格納するくらいなんですが、仕様の表記をスタックの使用前・使用後のパターンとかの素なものにしておいて、そこから情報を導出してたのが CLならではかもしれません。
    後で定義を変えて最適化アセンブラ!、は目論見だけでしませんでしたが、こちらの分野では、McGillのJimple( http://citeseer.ist.psu.edu/old/rai98jimple.html , Sootプロジェクトはまだ続いているっぽい!)の前身が96年くらいには出てた気がします。

  2. […] CLでJavaのアセンブラ/逆アセンブラを作った CLでプログラムを書くのも大分慣れてきたおかげか、比較的楽に書くことができました。 […]

Leave a Reply