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 を呼び出す
ということをしてるんじゃないかと。
時間ができたらもっと調べてみる。