Erlang でモジュールをディスアセンブルする
自分用メモ。全然ドキュメントがないので困っている。OTP のソースを眺めていたら beam_disasm というモジュールを発見した。どうもこれをつかえばいいようだ。
test.erl
-module(test). -export([add/2, add/3]). add(X, Y) -> X + Y. add(X, Y, Z) -> add(add(X, Y), Z).
上記を作成したあと、erl で
1> c(test). {ok,test} 2> import(beam_disasm). ok 3> beam_disasm:df(test). ok 4> q(). ok
とかすると、test.beam と一緒に test.dis というファイルができている。
test.dis の中身:
{file,"/tmp/test.beam"}. {function,add,2,2}. {label,1}. {func_info,{atom,test},{atom,add},2}. {label,2}. {gc_bif,'+',{f,0},2,[{x,0},{x,1}],{x,0}}. return. {function,add,3,4}. {label,3}. {func_info,{atom,test},{atom,add},3}. {label,4}. {allocate,1,3}. {move,{x,2},{y,0}}. {call,2,{test,add,2}}. {move,{y,0},{x,1}}. {call_last,2,{test,add,2},1}. {function,module_info,0,6}. {label,5}. {func_info,{atom,test},{atom,module_info},0}. {label,6}. {move,{atom,test},{x,0}}. {call_ext_only,1,{extfunc,erlang,get_module_info,1}}. {function,module_info,1,8}. {label,7}. {func_info,{atom,test},{atom,module_info},1}. {label,8}. {move,{x,0},{x,1}}. {move,{atom,test},{x,0}}. {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
見たところ、
{function,module_info,1,6}.
- 2 番目の要素が関数名
- 3 番目の要素が arity
- 4 番目の要素が開始ラベル番号
となっているようだ。
頻繁に出てくる {x,0}
、これは多分レジスタだろう。そうだとすると、スタックマシンじゃないのが意外。いや、もしかしてバイトコードレベルではレジスタで、VM はスタックマシンってことがあるかもしれないけど (あるのか?)
で、
{move,{x,2},{y,0}}. {call,2,{test,add,2}}. {move,{y,0},{x,1}}. {call_last,2,{test,add,2},1}.
この部分から類推するに {y,0}
はローカル変数。
- {x,2} (3 番目の引数を) {y,0} に待避する (次の call で破壊される可能性があるので)
- {x,0} {x,1} はそのままに add/2 を呼び出す
- 2. の結果は {x,0} に入っているので、{x,1} に {y,0} の内容を代入し
- もう一度 add/2 を呼び出す
ということをしてるんじゃないかと。
時間ができたらもっと調べてみる。