BeamFile - An interface to the BEAM file format and a decompiler

BeamFile is mainly a wrapper around the Erlang module :beam_lib.
BeamFile provides different views to the data in a BEAM file: BeamFile.elixir_code/2, BeamFile.erl_code/1, BeamFile.abstract_code/1 and BeamFile.byte_code/1. The reconstructed Elixir code is not the original code. In this code all macros and reference are resolved.

Example:

defmodule Say do
  def hello(name \\ "World"), do: name |> format() |> puts()

  def format(name), do: "Hello, #{name}!"

  defdelegate puts(item), to: IO
end
iex> {:ok, code} = BeamFile.elixir_code(Say)
iex> IO.puts(code)
defmodule Elixir.Say do
  def hello(name) do
    puts(format(name))
  end

  def hello do
    hello("World")
  end

  def format(name) do
    <<"Hello, ", String.Chars.to_string(name)::binary(), "!">>
  end

  def puts(item) do
    IO.puts(item)
  end
end
:ok
iex> {:ok, code} = BeamFile.erl_code(Say)
iex> IO.puts(code)
file("test/fixtures/default.ex", 7).

-module('Elixir.Say').

-compile([no_auto_import]).

-export(['__info__'/1,
         format/1,
         hello/0,
         hello/1,
         puts/1]).

-spec '__info__'(attributes |
                 compile |
                 functions |
                 macros |
                 md5 |
                 exports_md5 |
                 module |
                 deprecated) -> any().

'__info__'(module) -> 'Elixir.Say';
'__info__'(functions) ->
    [{format, 1}, {hello, 0}, {hello, 1}, {puts, 1}];
'__info__'(macros) -> [];
'__info__'(exports_md5) -> <<"VÑ>Qlf`zâ\202--*ÐP®">>;
'__info__'(Key = attributes) ->
    erlang:get_module_info('Elixir.Say', Key);
'__info__'(Key = compile) ->
    erlang:get_module_info('Elixir.Say', Key);
'__info__'(Key = md5) ->
    erlang:get_module_info('Elixir.Say', Key);
'__info__'(deprecated) -> [].

format(_name@1) ->
    <<"Hello, ",
      case _name@1 of
          _@1 when erlang:is_binary(_@1) -> _@1;
          _@1 -> 'Elixir.String.Chars':to_string(_@1)
      end/binary,
      "!">>.

hello() -> hello(<<"World">>).

hello(_name@1) -> puts(format(_name@1)).

puts(_item@1) -> 'Elixir.IO':puts(_item@1).
:ok
iex> {:ok, code} = BeamFile.byte_code(Say)
iex> IO.inspect(code)
{:beam_file, Say,
 [
   {:__info__, 1, 2},
   {:format, 1, 9},
   {:hello, 0, 13},
   {:hello, 1, 15},
   {:module_info, 0, 19},
   {:module_info, 1, 21},
   {:puts, 1, 17}
 ], [vsn: [328337295702536866380643469106329237584]],
 [
   version: '7.6.7',
   options: [:dialyzer, :no_spawn_compiler_process, :from_core,
    :no_core_prepare, :no_auto_import],
   source: '.../say.ex'
 ],
 [
   {:function, :__info__, 1, 2,
    [
      {:label, 1},
      {:line, 0},
      {:func_info, {:atom, Say}, {:atom, :__info__}, 1},
      {:label, 2},
      {:select_val, {:x, 0}, {:f, 1},
       {:list,
        [
          atom: :attributes,
          f: 7,
          atom: :compile,
          f: 7,
          atom: :deprecated,
          f: 6,
          atom: :exports_md5,
          f: 5,
          atom: :functions,
          f: 4,
          atom: :macros,
          f: 6,
          atom: :md5,
          f: 7,
          atom: :module,
          f: 3
        ]}},
      {:label, 3},
      {:move, {:atom, Say}, {:x, 0}},
      :return,
      {:label, 4},
      {:move, {:literal, [format: 1, hello: 0, hello: 1, puts: 1]}, {:x, 0}},
      :return,
      {:label, 5},
      {:move,
       {:literal,
        <<86, 209, 62, 81, 108, 102, 96, 122, 226, 130, 45, 45, 42, 208, 80,
          174>>}, {:x, 0}},
      :return,
      {:label, 6},
      {:move, nil, {:x, 0}},
      :return,
      {:label, 7},
      {:move, {:x, 0}, {:x, 1}},
      {:move, {:atom, Say}, {:x, 0}},
      {:line, 0},
      {:call_ext_only, 2, {:extfunc, :erlang, :get_module_info, 2}}
    ]},
   {:function, :format, 1, 9,
    [
      {:line, 1},
      {:label, 8},
      {:func_info, {:atom, Say}, {:atom, :format}, 1},
      {:label, 9},
      {:allocate, 1, 1},
      {:move, {:x, 0}, {:y, 0}},
      {:test, :is_binary, {:f, 10}, [x: 0]},
      {:move, {:y, 0}, {:x, 0}},
      {:jump, {:f, 11}},
      {:label, 10},
      {:init, {:y, 0}},
      {:line, 1},
      {:call_ext, 1, {:extfunc, String.Chars, :to_string, 1}},
      {:label, 11},
      {:line, 1},
      {:gc_bif, :byte_size, {:f, 0}, 1, [x: 0], {:x, 1}},
      {:bs_add, {:f, 0}, [{:x, 1}, {:integer, 8}, 1], {:x, 1}},
      {:line, 1},
      {:bs_init2, {:f, 0}, {:x, 1}, 0, 2, {:field_flags, 0}, {:x, 1}},
      {:bs_put_string, 7, {:string, 'Hello, '}},
      {:bs_put_binary, {:f, 0}, {:atom, :all}, 8, {:field_flags, 0}, {:x, 0}},
      {:bs_put_string, 1, {:string, '!'}},
      {:move, {:x, 1}, {:x, 0}},
      {:deallocate, 1},
      :return
    ]},
   {:function, :hello, 0, 13,
    [
      {:line, 2},
      {:label, 12},
      {:func_info, {:atom, Say}, {:atom, :hello}, 0},
      {:label, 13},
      {:move, {:literal, "World"}, {:x, 0}},
      {:call_only, 1, {Say, :hello, 1}}
    ]},
   {:function, :hello, 1, 15,
    [
      {:line, 2},
      {:label, 14},
      {:func_info, {:atom, Say}, {:atom, :hello}, 1},
      {:label, 15},
      {:allocate, 0, 1},
      {:line, 2},
      {:call, 1, {Say, :format, 1}},
      {:call_last, 1, {Say, :puts, 1}, 0}
    ]},
   {:function, :puts, 1, 17,
    [
      {:line, 3},
      {:label, 16},
      {:func_info, {:atom, Say}, {:atom, :puts}, 1},
      {:label, 17},
      {:line, 3},
      {:call_ext_only, 1, {:extfunc, IO, :puts, 1}}
    ]},
   {:function, :module_info, 0, 19,
    [
      {:line, 0},
      {:label, 18},
      {:func_info, {:atom, Say}, {:atom, :module_info}, 0},
      {:label, 19},
      {:move, {:atom, Say}, {:x, 0}},
      {:line, 0},
      {:call_ext_only, 1, {:extfunc, :erlang, :get_module_info, 1}}
    ]},
   {:function, :module_info, 1, 21,
    [
      {:line, 0},
      {:label, 20},
      {:func_info, {:atom, Say}, {:atom, :module_info}, 1},
      {:label, 21},
      {:move, {:x, 0}, {:x, 1}},
      {:move, {:atom, Say}, {:x, 0}},
      {:line, 0},
      {:call_ext_only, 2, {:extfunc, :erlang, :get_module_info, 2}}
    ]}
 ]}
7 Likes