Awesome
BeamFile
A little fun project to get a peek into BEAM files. For now, this project is in an early beta state.
BeamFile is mainly a wrapper around the Erlang module :beam_lib
.
BeamFile provides different views to the data in a BEAM file:
- abstract code
- byte code
- Erlang code
- Elixir code.
The reconstructed Elixir code is not the original code. In this code all macros and reference are resolved.
A mix
task is also provided for decompiling modules.
$ mix beam.file MyModule --elixir
$ mix beam.file MyModule --erlang
$ mix beam.file MyModule --byte-code
Example
Assume we have an BEAM file compiled form the following source.
defmodule Example.Math do
@moduledoc "Math is Fun"
def add(number_a, number_b), do: number_a + number_b
def odd_or_even(a) do
if rem(a, 2) == 0 do
:even
else
:odd
end
end
end
The module must be compiled and loaded so that it can be found by name, see
BeamFile.which/1
and :erlang.which/1. To give
BaemFile
in iex
a try see "Example (iex)" in the next section.
Than we can reconstruct Elixir code:
iex> {:ok, code} = BeamFile.elixir_code(Example.Math, docs: true)
iex> IO.puts(code)
defmodule Elixir.Example.Math do
@moduledoc "Math is Fun"
def add(number_a, number_b) do
:erlang.+(number_a, number_b)
end
def odd_or_even(a) do
case(:erlang.==(:erlang.rem(a, 2), 0)) do
false ->
:odd
true ->
:even
end
end
end
Take a look on the Erlang code:
iex> {:ok, code} = BeamFile.erl_code(Example.Math)
iex> IO.puts(code)
-file("test/fixtures/example/math.ex", 1).
-module('Elixir.Example.Math').
-compile([no_auto_import]).
-export(['__info__'/1, add/2, odd_or_even/1]).
-spec '__info__'(attributes | compile | functions |
macros | md5 | module | deprecated) -> any().
'__info__'(module) -> 'Elixir.Example.Math';
'__info__'(functions) -> [{add, 2}, {odd_or_even, 1}];
'__info__'(macros) -> [];
'__info__'(Key = attributes) ->
erlang:get_module_info('Elixir.Example.Math', Key);
'__info__'(Key = compile) ->
erlang:get_module_info('Elixir.Example.Math', Key);
'__info__'(Key = md5) ->
erlang:get_module_info('Elixir.Example.Math', Key);
'__info__'(deprecated) -> [].
add(_number_a@1, _number_b@1) ->
_number_a@1 + _number_b@1.
odd_or_even(_a@1) ->
case _a@1 rem 2 == 0 of
false -> odd;
true -> even
end.
Or byte code:
iex> {:ok, code} = BeamFile.byte_code(Example.Math)
iex> IO.puts(code)
{:ok,
{:beam_file, Example.Math,
[
{:__info__, 1, 2},
{:add, 2, 8},
{:module_info, 0, 13},
{:module_info, 1, 15},
{:odd_or_even, 1, 10}
], [vsn: [213009173131107396303781325243687557035]],
[
version: '7.5.3',
options: [:no_spawn_compiler_process, :from_core,
:no_auto_import],
source: '/Users/kruse/Projects/beam_file/test/fixtures/example/math.ex'
],
[
{:function, :__info__, 1, 2,
[
{:label, 1},
{:line, 0},
{:func_info, {:atom, Example.Math}, {:atom, :__info__}, 1},
{:label, 2},
{:select_val, {:x, 0}, {:f, 1},
{:list,
[
atom: :compile,
f: 6,
atom: :md5,
f: 6,
atom: :attributes,
f: 6,
atom: :functions,
f: 5,
atom: :macros,
f: 4,
atom: :deprecated,
f: 4,
atom: :module,
f: 3
]}},
{:label, 3},
{:move, {:atom, Example.Math}, {:x, 0}},
:return,
{:label, 4},
{:move, nil, {:x, 0}},
:return,
{:label, 5},
{:move, {:literal, [add: 2, odd_or_even: 1]}, {:x, 0}},
:return,
{:label, 6},
{:move, {:x, 0}, {:x, 1}},
{:move, {:atom, Example.Math}, {:x, 0}},
{:line, 0},
{:call_ext_only, 2,
{:extfunc, :erlang, :get_module_info, 2}}
]},
{:function, :add, 2, 8,
[
{:line, 1},
{:label, 7},
{:func_info, {:atom, Example.Math}, {:atom, :add}, 2},
{:label, 8},
{:line, 1},
{:gc_bif, :+, {:f, 0}, 2, [x: 0, x: 1], {:x, 0}},
:return
]},
{:function, :odd_or_even, 1, 10,
[
{:line, 2},
{:label, 9},
{:func_info, {:atom, Example.Math}, {:atom, :odd_or_even},
1},
{:label, 10},
{:line, 3},
{:gc_bif, :rem, {:f, 0}, 1, [x: 0, integer: 2], {:x, 0}},
{:test, :is_eq_exact, {:f, 11}, [x: 0, integer: 0]},
{:move, {:atom, :even}, {:x, 0}},
:return,
{:label, 11},
{:move, {:atom, :odd}, {:x, 0}},
:return
]},
{:function, :module_info, 0, 13,
[
{:line, 0},
{:label, 12},
{:func_info, {:atom, Example.Math}, {:atom, :module_info},
0},
{:label, 13},
{:move, {:atom, Example.Math}, {:x, 0}},
{:line, 0},
{:call_ext_only, 1,
{:extfunc, :erlang, :get_module_info, 1}}
]},
{:function, :module_info, 1, 15,
[
{:line, 0},
{:label, 14},
{:func_info, {:atom, Example.Math}, {:atom, :module_info},
1},
{:label, 15},
{:move, {:x, 0}, {:x, 1}},
{:move, {:atom, Example.Math}, {:x, 0}},
{:line, 0},
{:call_ext_only, 2,
{:extfunc, :erlang, :get_module_info, 2}}
]}
]}}
Example (iex)
Disasamble some Elixir code:
iex(1)> defmodule Foo do
...(1)> def bar(x), do: if x == 0, do: false, else: true
...(1)> end |> BeamFile.elixir_code!() |> IO.puts()
defmodule Elixir.Foo do
def bar(x) do
case :erlang.==(x, 0) do
false -> true
true -> false
end
end
end
:ok
Take a look to the AST:
iex(1)> defmodule Foo do
...(1)> def bar(x), do: if x == 0, do: false, else: true
...(1)> end |> BeamFile.elixir_quoted!()
{:defmodule, [context: Elixir, import: Kernel],
[
{:__aliases__, [alias: false], [Foo]},
[
do: {:__block__, [],
[
{:def, [line: 4],
[
{:bar, [], [{:x, [version: 0, line: 4], nil}]},
[
do: {:case, [line: 4, optimize_boolean: true],
[
{{:., [line: 4], [:erlang, :==]}, [line: 4],
[{:x, [version: 0, line: 4], nil}, 0]},
[
do: [
{:->, [generated: true, line: 4], [[false], true]},
{:->, [generated: true, line: 4], [[true], false]}
]
]
]}
]
]}
]}
]
]}
Use of BeamFile.erl_code/1
with binary
:
iex(1)> {:module, Foo, binary, _} = defmodule Foo do
...(1)> def bar, do: :bar
...(1)> end
iex(2)> BeamFile.erl_code!(binary) |> IO.puts()
-file("iex", 1).
-module('Elixir.Foo').
-compile([no_auto_import]).
-export(['__info__'/1, bar/0]).
-spec '__info__'(attributes |
compile |
functions |
macros |
md5 |
exports_md5 |
module |
deprecated) -> any().
'__info__'(module) -> 'Elixir.Foo';
'__info__'(functions) -> [{bar, 0}];
'__info__'(macros) -> [];
'__info__'(exports_md5) ->
<<"®\r¦c\004í.©¾uHÀ¯\217,">>;
'__info__'(Key = attributes) ->
erlang:get_module_info('Elixir.Foo', Key);
'__info__'(Key = compile) ->
erlang:get_module_info('Elixir.Foo', Key);
'__info__'(Key = md5) ->
erlang:get_module_info('Elixir.Foo', Key);
'__info__'(deprecated) -> [].
bar() -> bar.
:ok
Resources
More information about BEAM files:
- BEAM File Format on BEAM Wisdom
- The Feature That No One Knew About in Elixir 1.5 by José Valim
- Disassemble Elixir code. And check if Erlang dead code elimination works by Gaspar Chilingarov