Marcus

Marcus

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}}
    ]}
 ]}

Most Liked

Marcus

Marcus

Version 0.5.0 is released. I have added BeamFile.elixir_quoted/1 to get the AST from the beam file. All functions now accept {:module, Foo, <<...>>, []} as input. With this change experiments in the iex are simpler.

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
iex(2)> defmodule Foo do
...(2)>   def bar(x), do: if x == 0, do: false, else: true
...(2)> 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]}
                  ]
                ]
              ]}
           ]
         ]}
      ]}
   ]
 ]}

Marcus

Marcus

Version 0.6.0 has been released with some updates to work with Elixir 1.16.

The function BeamFileDocs now has some options for formatting and filtering the output:

iex(1)> BeamFile.docs(Date, since: "~> 1.12", format: :since)
{:ok,
 {[
    {{:function, :after?, 2}, [since: "1.15.0"]},
    {{:function, :before?, 2}, [since: "1.15.0"]},
    {{:function, :range, 3}, [since: "1.12.0"]}
  ], [since: nil]}}
iex(2)> BeamFile.docs(Enum, format: :info, deprecated: true)
{:ok,
 {[
    {{:function, :chunk, 2}, [since: nil, hidden: true, deprecated: true]},
    {{:function, :chunk, 3}, [since: nil, hidden: true, deprecated: true]},
    {{:function, :chunk, 4}, [since: nil, hidden: true, deprecated: true]},
    {{:function, :filter_map, 3}, [since: nil, hidden: true, deprecated: true]},
    {{:function, :partition, 2}, [since: nil, hidden: true, deprecated: true]},
    {{:function, :uniq, 2}, [since: nil, hidden: true, deprecated: true]}
  ], [since: nil, hidden: false, deprecated: false]}}

Where Next?

Popular in Announcing Top

wmnnd
Hi there, for my project DBLSQD, I needed a file storage solution that is a bit more flexible than Arc. Because I thought others might f...
New
ostinelli
Let’s write a database! Well not really, but I think it’s a little sad that there doesn’t seem to be a simple in-memory distributed KV da...
New
tmbb
I’ve been working on two packages (not on hex.pm yet) to build admin interfaces for phoenix apps: bureaucrat - which contains a bunch ...
New
maltoe
Hello! Came here to announce ChromicPDF, a pet project PDF generator I’ve been working on for the past few months. Why another PDF gener...
New
kip
Image is an image processing library for Elixir. It is based upon the fabulous vix library that provides a libvips wrapper for Elixir. I...
622 18934 194
New
ahamez
Hi everyone, I’ve been working on this protobuf library for 3 years. We use it in the company I work for, EasyMile, to communicate with ...
New
cjen07
parameterized pipe in elixir: |n&gt; edit: negative index in |n&gt; and mixed usage with |&gt; are supported example: use ParamPipe ...
New
Jskalc
Hi! Today, after a couple weeks of development I’ve released v0.1 of LiveVue. It’s a seamless integration of Vue and Phoenix LiveView, i...
New
marcuslankenau
I feel kind of stuck with the absence of a proper xml library for Elixir. Currently I use SweetXML which was ok for me more or less to pa...
New
woylie
Flop is an Elixir library that applies filtering, ordering and pagination parameters to your Ecto queries. offset-based pagination with...
New

Other popular topics Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41989 114
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52673 488
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement