/code/beam_modules_chapter/src/beamfile.erl

https://github.com/happi/theBeamBook · Erlang · 169 lines · 139 code · 26 blank · 4 comment · 0 complexity · bf16fbb6e68cf909df96a21c0ac9eaf7 MD5 · raw file

  1. -module(beamfile).
  2. -export([read/1]).
  3. read(Filename) ->
  4. {ok, File} = file:read_file(Filename),
  5. <<"FOR1",
  6. Size:32/integer,
  7. "BEAM",
  8. Chunks/binary>> = File,
  9. {Size, parse_chunks(read_chunks(Chunks, []),[])}.
  10. read_chunks(<<N,A,M,E, Size:32/integer, Tail/binary>>, Acc) ->
  11. %% Align each chunk on even 4 bytes
  12. ChunkLength = align_by_four(Size),
  13. <<Chunk:ChunkLength/binary, Rest/binary>> = Tail,
  14. read_chunks(Rest, [{[N,A,M,E], Size, Chunk}|Acc]);
  15. read_chunks(<<>>, Acc) -> lists:reverse(Acc).
  16. align_by_four(N) -> (4 * ((N+3) div 4)).
  17. parse_chunks([{"Atom", _Size, <<_Numberofatoms:32/integer, Atoms/binary>>} | Rest], Acc) ->
  18. parse_chunks(Rest,[{atoms,parse_atoms(Atoms)}|Acc]);
  19. parse_chunks([{"ExpT", _Size,
  20. <<_Numberofentries:32/integer, Exports/binary>>}
  21. | Rest], Acc) ->
  22. parse_chunks(Rest,[{exports,parse_table(Exports)}|Acc]);
  23. parse_chunks([{"ImpT", _Size,
  24. <<_Numberofentries:32/integer, Imports/binary>>}
  25. | Rest], Acc) ->
  26. parse_chunks(Rest,[{imports,parse_table(Imports)}|Acc]);
  27. parse_chunks([{"Code", Size, <<SubSize:32/integer, Chunk/binary>>} | Rest], Acc) ->
  28. <<Info:SubSize/binary, Code/binary>> = Chunk,
  29. OpcodeSize = Size - SubSize - 8, %% 8 is size of CunkSize & SubSize
  30. <<OpCodes:OpcodeSize/binary, _Align/binary>> = Code,
  31. parse_chunks(Rest,[{code,parse_code_info(Info), OpCodes}|Acc]);
  32. parse_chunks([{"StrT", _Size, <<Strings/binary>>} | Rest], Acc) ->
  33. parse_chunks(Rest,[{strings,binary_to_list(Strings)}|Acc]);
  34. parse_chunks([{"Attr", Size, Chunk} | Rest], Acc) ->
  35. <<Bin:Size/binary, _Pad/binary>> = Chunk,
  36. Attribs = binary_to_term(Bin),
  37. parse_chunks(Rest,[{attributes,Attribs}|Acc]);
  38. parse_chunks([{"CInf", Size, Chunk} | Rest], Acc) ->
  39. <<Bin:Size/binary, _Pad/binary>> = Chunk,
  40. CInfo = binary_to_term(Bin),
  41. parse_chunks(Rest,[{compile_info,CInfo}|Acc]);
  42. parse_chunks([{"LocT", _Size,
  43. <<_Numberofentries:32/integer, Locals/binary>>}
  44. | Rest], Acc) ->
  45. parse_chunks(Rest,[{locals,parse_table(Locals)}|Acc]);
  46. parse_chunks([{"LitT", _ChunkSize,
  47. <<_CompressedTableSize:32, Compressed/binary>>}
  48. | Rest], Acc) ->
  49. <<_NumLiterals:32,Table/binary>> = zlib:uncompress(Compressed),
  50. Literals = parse_literals(Table),
  51. parse_chunks(Rest,[{literals,Literals}|Acc]);
  52. parse_chunks([{"Abst", _ChunkSize, <<>>} | Rest], Acc) ->
  53. parse_chunks(Rest,Acc);
  54. parse_chunks([{"Abst", _ChunkSize, <<AbstractCode/binary>>} | Rest], Acc) ->
  55. parse_chunks(Rest,[{abstract_code,binary_to_term(AbstractCode)}|Acc]);
  56. parse_chunks([{"Line", _ChunkSize, <<LineTable/binary>>} | Rest], Acc) ->
  57. <<Ver:32,Bits:32,NumLineInstrs:32,NumLines:32,NumFnames:32,
  58. Lines:NumLines/binary,Fnames/binary>> = LineTable,
  59. parse_chunks(Rest,[{line,
  60. [{version,Ver},
  61. {bits,Bits},
  62. {num_line_instrunctions,NumLineInstrs},
  63. {lines,decode_lineinfo(binary_to_list(Lines),0)},
  64. {function_names,Fnames}]}|Acc]);
  65. parse_chunks([Chunk|Rest], Acc) -> %% Not yet implemented chunk
  66. parse_chunks(Rest, [Chunk|Acc]);
  67. parse_chunks([],Acc) -> Acc.
  68. parse_atoms(<<Atomlength, Atom:Atomlength/binary, Rest/binary>>) when Atomlength > 0->
  69. [list_to_atom(binary_to_list(Atom)) | parse_atoms(Rest)];
  70. parse_atoms(_Alignment) -> [].
  71. parse_table(<<Function:32/integer,
  72. Arity:32/integer,
  73. Label:32/integer,
  74. Rest/binary>>) ->
  75. [{Function, Arity, Label} | parse_table(Rest)];
  76. parse_table(<<>>) -> [].
  77. parse_code_info(<<Instructionset:32/integer,
  78. OpcodeMax:32/integer,
  79. NumberOfLabels:32/integer,
  80. NumberOfFunctions:32/integer,
  81. Rest/binary>>) ->
  82. [{instructionset, Instructionset},
  83. {opcodemax, OpcodeMax},
  84. {numberoflabels, NumberOfLabels},
  85. {numberofFunctions, NumberOfFunctions} |
  86. case Rest of
  87. <<>> -> [];
  88. _ -> [{newinfo, Rest}]
  89. end].
  90. parse_literals(<<Size:32,Literal:Size/binary,Tail/binary>>) ->
  91. [binary_to_term(Literal) | parse_literals(Tail)];
  92. parse_literals(<<>>) -> [].
  93. -define(tag_i, 1).
  94. -define(tag_a, 2).
  95. decode_tag(?tag_i) -> i;
  96. decode_tag(?tag_a) -> a.
  97. decode_int(Tag,B,Bs) when (B band 16#08) =:= 0 ->
  98. %% N < 16 = 4 bits, NNNN:0:TTT
  99. N = B bsr 4,
  100. {{Tag,N},Bs};
  101. decode_int(Tag,B,[]) when (B band 16#10) =:= 0 ->
  102. %% N < 2048 = 11 bits = 3:8 bits, NNN:01:TTT, NNNNNNNN
  103. Val0 = B band 2#11100000,
  104. N = (Val0 bsl 3),
  105. {{Tag,N},[]};
  106. decode_int(Tag,B,Bs) when (B band 16#10) =:= 0 ->
  107. %% N < 2048 = 11 bits = 3:8 bits, NNN:01:TTT, NNNNNNNN
  108. [B1|Bs1] = Bs,
  109. Val0 = B band 2#11100000,
  110. N = (Val0 bsl 3) bor B1,
  111. {{Tag,N},Bs1};
  112. decode_int(Tag,B,Bs) ->
  113. {Len,Bs1} = decode_int_length(B,Bs),
  114. {IntBs,RemBs} = take_bytes(Len,Bs1),
  115. N = build_arg(IntBs),
  116. {{Tag,N},RemBs}.
  117. decode_lineinfo([B|Bs], F) ->
  118. Tag = decode_tag(B band 2#111),
  119. {{Tag,Num},RemBs} = decode_int(Tag,B,Bs),
  120. case Tag of
  121. i ->
  122. [{F, Num} | decode_lineinfo(RemBs, F)];
  123. a ->
  124. [B2|Bs2] = RemBs,
  125. Tag2 = decode_tag(B2 band 2#111),
  126. {{Tag2,Num2},RemBs2} = decode_int(Tag2,B2,Bs2),
  127. [{Num, Num2} | decode_lineinfo(RemBs2, Num2)]
  128. end;
  129. decode_lineinfo([],_) -> [].
  130. decode_int_length(B, Bs) ->
  131. {B bsr 5 + 2, Bs}.
  132. take_bytes(N, Bs) ->
  133. take_bytes(N, Bs, []).
  134. take_bytes(N, [B|Bs], Acc) when N > 0 ->
  135. take_bytes(N-1, Bs, [B|Acc]);
  136. take_bytes(0, Bs, Acc) ->
  137. {lists:reverse(Acc), Bs}.
  138. build_arg(Bs) ->
  139. build_arg(Bs, 0).
  140. build_arg([B|Bs], N) ->
  141. build_arg(Bs, (N bsl 8) bor B);
  142. build_arg([], N) ->
  143. N.