PageRenderTime 58ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/stdlib/src/beam_lib.erl

https://github.com/bsmr-erlang/otp
Erlang | 1208 lines | 978 code | 157 blank | 73 comment | 9 complexity | f4d664fefd1d970fdc62ee52711e6c86 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0
  1. %%
  2. %% %CopyrightBegin%
  3. %%
  4. %% Copyright Ericsson AB 2000-2018. All Rights Reserved.
  5. %%
  6. %% Licensed under the Apache License, Version 2.0 (the "License");
  7. %% you may not use this file except in compliance with the License.
  8. %% You may obtain a copy of the License at
  9. %%
  10. %% http://www.apache.org/licenses/LICENSE-2.0
  11. %%
  12. %% Unless required by applicable law or agreed to in writing, software
  13. %% distributed under the License is distributed on an "AS IS" BASIS,
  14. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. %% See the License for the specific language governing permissions and
  16. %% limitations under the License.
  17. %%
  18. %% %CopyrightEnd%
  19. %%
  20. -module(beam_lib).
  21. -behaviour(gen_server).
  22. %% Avoid warning for local function error/1 clashing with autoimported BIF.
  23. -compile({no_auto_import,[error/1]}).
  24. %% Avoid warning for local function error/2 clashing with autoimported BIF.
  25. -compile({no_auto_import,[error/2]}).
  26. -export([info/1,
  27. cmp/2,
  28. cmp_dirs/2,
  29. chunks/2,
  30. chunks/3,
  31. all_chunks/1,
  32. diff_dirs/2,
  33. strip/1,
  34. strip/2,
  35. strip_files/1,
  36. strip_files/2,
  37. strip_release/1,
  38. strip_release/2,
  39. significant_chunks/0,
  40. build_module/1,
  41. version/1,
  42. md5/1,
  43. format_error/1]).
  44. %% The following functions implement encrypted debug info.
  45. -export([crypto_key_fun/1, clear_crypto_key_fun/0]).
  46. -export([init/1,handle_call/3,handle_cast/2,handle_info/2,
  47. terminate/2,code_change/3]).
  48. -export([make_crypto_key/2, get_crypto_key/1]). %Utilities used by compiler
  49. -export_type([attrib_entry/0, compinfo_entry/0, labeled_entry/0, label/0]).
  50. -import(lists, [append/1, delete/2, foreach/2, keysort/2,
  51. member/2, reverse/1, sort/1, splitwith/2]).
  52. %%-------------------------------------------------------------------------
  53. -type beam() :: file:filename() | binary().
  54. -type debug_info() :: {DbgiVersion :: atom(), Backend :: module(), Data :: term()} | 'no_debug_info'.
  55. -type forms() :: [erl_parse:abstract_form() | erl_parse:form_info()].
  56. -type abst_code() :: {AbstVersion :: atom(), forms()} | 'no_abstract_code'.
  57. -type dataB() :: binary().
  58. -type index() :: non_neg_integer().
  59. -type label() :: integer().
  60. -type chunkid() :: nonempty_string(). % approximation of the strings below
  61. %% "Abst" | "Dbgi" | "Attr" | "CInf" | "ExpT" | "ImpT" | "LocT" | "Atom" | "AtU8".
  62. -type chunkname() :: 'abstract_code' | 'debug_info'
  63. | 'attributes' | 'compile_info'
  64. | 'exports' | 'labeled_exports'
  65. | 'imports' | 'indexed_imports'
  66. | 'locals' | 'labeled_locals'
  67. | 'atoms'.
  68. -type chunkref() :: chunkname() | chunkid().
  69. -type attrib_entry() :: {Attribute :: atom(), [AttributeValue :: term()]}.
  70. -type compinfo_entry() :: {InfoKey :: atom(), term()}.
  71. -type labeled_entry() :: {Function :: atom(), arity(), label()}.
  72. -type chunkdata() :: {chunkid(), dataB()}
  73. | {'abstract_code', abst_code()}
  74. | {'debug_info', debug_info()}
  75. | {'attributes', [attrib_entry()]}
  76. | {'compile_info', [compinfo_entry()]}
  77. | {'exports', [{atom(), arity()}]}
  78. | {'labeled_exports', [labeled_entry()]}
  79. | {'imports', [mfa()]}
  80. | {'indexed_imports', [{index(), module(), Function :: atom(), arity()}]}
  81. | {'locals', [{atom(), arity()}]}
  82. | {'labeled_locals', [labeled_entry()]}
  83. | {'atoms', [{integer(), atom()}]}.
  84. %% Error reasons
  85. -type info_rsn() :: {'chunk_too_big', file:filename(),
  86. chunkid(), ChunkSize :: non_neg_integer(),
  87. FileSize :: non_neg_integer()}
  88. | {'invalid_beam_file', file:filename(),
  89. Position :: non_neg_integer()}
  90. | {'invalid_chunk', file:filename(), chunkid()}
  91. | {'missing_chunk', file:filename(), chunkid()}
  92. | {'not_a_beam_file', file:filename()}
  93. | {'file_error', file:filename(), file:posix()}.
  94. -type chnk_rsn() :: {'unknown_chunk', file:filename(), atom()}
  95. | {'key_missing_or_invalid', file:filename(),
  96. 'abstract_code' | 'debug_info'}
  97. | info_rsn().
  98. -type cmp_rsn() :: {'modules_different', module(), module()}
  99. | {'chunks_different', chunkid()}
  100. | 'different_chunks'
  101. | info_rsn().
  102. %%-------------------------------------------------------------------------
  103. %%
  104. %% Exported functions
  105. %%
  106. -spec info(Beam) -> [InfoPair] | {'error', 'beam_lib', info_rsn()} when
  107. Beam :: beam(),
  108. InfoPair :: {'file', Filename :: file:filename()}
  109. | {'binary', Binary :: binary()}
  110. | {'module', Module :: module()}
  111. | {'chunks', [{ChunkId :: chunkid(),
  112. Pos :: non_neg_integer(),
  113. Size :: non_neg_integer()}]}.
  114. info(File) ->
  115. read_info(beam_filename(File)).
  116. -spec chunks(Beam, ChunkRefs) ->
  117. {'ok', {module(), [chunkdata()]}} |
  118. {'error', 'beam_lib', chnk_rsn()} when
  119. Beam :: beam(),
  120. ChunkRefs :: [chunkref()].
  121. chunks(File, Chunks) ->
  122. read_chunk_data(File, Chunks).
  123. -spec chunks(Beam, ChunkRefs, Options) ->
  124. {'ok', {module(), [ChunkResult]}} |
  125. {'error', 'beam_lib', chnk_rsn()} when
  126. Beam :: beam(),
  127. ChunkRefs :: [chunkref()],
  128. Options :: ['allow_missing_chunks'],
  129. ChunkResult :: chunkdata() | {ChunkRef :: chunkref(), 'missing_chunk'}.
  130. chunks(File, Chunks, Options) ->
  131. try read_chunk_data(File, Chunks, Options)
  132. catch Error -> Error end.
  133. -spec all_chunks(beam()) ->
  134. {'ok', 'beam_lib', [{chunkid(), dataB()}]} | {'error', 'beam_lib', info_rsn()}.
  135. all_chunks(File) ->
  136. read_all_chunks(File).
  137. -spec cmp(Beam1, Beam2) -> 'ok' | {'error', 'beam_lib', cmp_rsn()} when
  138. Beam1 :: beam(),
  139. Beam2 :: beam().
  140. cmp(File1, File2) ->
  141. try cmp_files(File1, File2)
  142. catch Error -> Error end.
  143. -spec cmp_dirs(Dir1, Dir2) ->
  144. {Only1, Only2, Different} | {'error', 'beam_lib', Reason} when
  145. Dir1 :: atom() | file:filename(),
  146. Dir2 :: atom() | file:filename(),
  147. Only1 :: [file:filename()],
  148. Only2 :: [file:filename()],
  149. Different :: [{Filename1 :: file:filename(), Filename2 :: file:filename()}],
  150. Reason :: {'not_a_directory', term()} | info_rsn().
  151. cmp_dirs(Dir1, Dir2) ->
  152. catch compare_dirs(Dir1, Dir2).
  153. -spec diff_dirs(Dir1, Dir2) -> 'ok' | {'error', 'beam_lib', Reason} when
  154. Dir1 :: atom() | file:filename(),
  155. Dir2 :: atom() | file:filename(),
  156. Reason :: {'not_a_directory', term()} | info_rsn().
  157. diff_dirs(Dir1, Dir2) ->
  158. catch diff_directories(Dir1, Dir2).
  159. -spec strip(Beam1) ->
  160. {'ok', {module(), Beam2}} | {'error', 'beam_lib', info_rsn()} when
  161. Beam1 :: beam(),
  162. Beam2 :: beam().
  163. strip(FileName) ->
  164. strip(FileName, []).
  165. -spec strip(Beam1, AdditionalChunks) ->
  166. {'ok', {module(), Beam2}} | {'error', 'beam_lib', info_rsn()} when
  167. Beam1 :: beam(),
  168. AdditionalChunks :: [chunkid()],
  169. Beam2 :: beam().
  170. strip(FileName, AdditionalChunks) ->
  171. try strip_file(FileName, AdditionalChunks)
  172. catch Error -> Error end.
  173. -spec strip_files(Files) ->
  174. {'ok', [{module(), Beam}]} | {'error', 'beam_lib', info_rsn()} when
  175. Files :: [beam()],
  176. Beam :: beam().
  177. strip_files(Files) ->
  178. strip_files(Files, []).
  179. -spec strip_files(Files, AdditionalChunks) ->
  180. {'ok', [{module(), Beam}]} | {'error', 'beam_lib', info_rsn()} when
  181. Files :: [beam()],
  182. AdditionalChunks :: [chunkid()],
  183. Beam :: beam().
  184. strip_files(Files, AdditionalChunks) when is_list(Files) ->
  185. try strip_fils(Files, AdditionalChunks)
  186. catch Error -> Error end.
  187. -spec strip_release(Dir) ->
  188. {'ok', [{module(), file:filename()}]}
  189. | {'error', 'beam_lib', Reason} when
  190. Dir :: atom() | file:filename(),
  191. Reason :: {'not_a_directory', term()} | info_rsn().
  192. strip_release(Root) ->
  193. strip_release(Root, []).
  194. -spec strip_release(Dir, AdditionalChunks) ->
  195. {'ok', [{module(), file:filename()}]}
  196. | {'error', 'beam_lib', Reason} when
  197. Dir :: atom() | file:filename(),
  198. AdditionalChunks :: [chunkid()],
  199. Reason :: {'not_a_directory', term()} | info_rsn().
  200. strip_release(Root, AdditionalChunks) ->
  201. catch strip_rel(Root, AdditionalChunks).
  202. -spec version(Beam) ->
  203. {'ok', {module(), [Version :: term()]}} |
  204. {'error', 'beam_lib', chnk_rsn()} when
  205. Beam :: beam().
  206. version(File) ->
  207. case catch read_chunk_data(File, [attributes]) of
  208. {ok, {Module, [{attributes, Attrs}]}} ->
  209. {vsn, Version} = lists:keyfind(vsn, 1, Attrs),
  210. {ok, {Module, Version}};
  211. Error ->
  212. Error
  213. end.
  214. -spec md5(Beam) ->
  215. {'ok', {module(), MD5}} | {'error', 'beam_lib', chnk_rsn()} when
  216. Beam :: beam(),
  217. MD5 :: binary().
  218. md5(File) ->
  219. case catch read_significant_chunks(File, md5_chunks()) of
  220. {ok, {Module, Chunks0}} ->
  221. Chunks = filter_funtab(Chunks0),
  222. {ok, {Module, erlang:md5([C || {_Id, C} <- Chunks])}};
  223. Error ->
  224. Error
  225. end.
  226. -spec format_error(Reason) -> io_lib:chars() when
  227. Reason :: term().
  228. format_error({error, Error}) ->
  229. format_error(Error);
  230. format_error({error, Module, Error}) ->
  231. Module:format_error(Error);
  232. format_error({unknown_chunk, File, ChunkName}) ->
  233. io_lib:format("~tp: Cannot find chunk ~p~n", [File, ChunkName]);
  234. format_error({invalid_chunk, File, ChunkId}) ->
  235. io_lib:format("~tp: Invalid contents of chunk ~p~n", [File, ChunkId]);
  236. format_error({not_a_beam_file, File}) ->
  237. io_lib:format("~tp: Not a BEAM file~n", [File]);
  238. format_error({file_error, File, Reason}) ->
  239. io_lib:format("~tp: ~tp~n", [File, file:format_error(Reason)]);
  240. format_error({missing_chunk, File, ChunkId}) ->
  241. io_lib:format("~tp: Not a BEAM file: no IFF \"~s\" chunk~n",
  242. [File, ChunkId]);
  243. format_error({invalid_beam_file, File, Pos}) ->
  244. io_lib:format("~tp: Invalid format of BEAM file near byte number ~p~n",
  245. [File, Pos]);
  246. format_error({chunk_too_big, File, ChunkId, Size, Len}) ->
  247. io_lib:format("~tp: Size of chunk \"~s\" is ~p bytes, "
  248. "but only ~p bytes could be read~n",
  249. [File, ChunkId, Size, Len]);
  250. format_error({chunks_different, Id}) ->
  251. io_lib:format("Chunk \"~s\" differs in the two files~n", [Id]);
  252. format_error(different_chunks) ->
  253. "The two files have different chunks\n";
  254. format_error({modules_different, Module1, Module2}) ->
  255. io_lib:format("Module names ~p and ~p differ in the two files~n",
  256. [Module1, Module2]);
  257. format_error({not_a_directory, Name}) ->
  258. io_lib:format("~tp: Not a directory~n", [Name]);
  259. format_error({key_missing_or_invalid, File, ChunkId}) ->
  260. io_lib:format("~tp: Cannot decrypt ~ts because key is missing or invalid",
  261. [File, ChunkId]);
  262. format_error(badfun) ->
  263. "not a fun or the fun has the wrong arity";
  264. format_error(exists) ->
  265. "a fun has already been installed";
  266. format_error(E) ->
  267. io_lib:format("~tp~n", [E]).
  268. %%
  269. %% Exported functions for encrypted debug info.
  270. %%
  271. -type mode() :: 'des3_cbc'.
  272. -type crypto_fun_arg() :: 'init'
  273. | 'clear'
  274. | {'debug_info', mode(), module(), file:filename()}.
  275. -type crypto_fun() :: fun((crypto_fun_arg()) -> term()).
  276. -spec crypto_key_fun(CryptoKeyFun) -> 'ok' | {'error', Reason} when
  277. CryptoKeyFun :: crypto_fun(),
  278. Reason :: badfun | exists | term().
  279. crypto_key_fun(F) ->
  280. call_crypto_server({crypto_key_fun, F}).
  281. -spec clear_crypto_key_fun() -> 'undefined' | {'ok', Result} when
  282. Result :: 'undefined' | term().
  283. clear_crypto_key_fun() ->
  284. call_crypto_server(clear_crypto_key_fun).
  285. -spec make_crypto_key(mode(), string()) ->
  286. {mode(), [binary()], binary(), integer()}.
  287. make_crypto_key(des3_cbc=Type, String) ->
  288. <<K1:8/binary,K2:8/binary>> = First = erlang:md5(String),
  289. <<K3:8/binary,IVec:8/binary>> = erlang:md5([First|reverse(String)]),
  290. {Type,[K1,K2,K3],IVec,8}.
  291. -spec build_module(Chunks) -> {'ok', Binary} when
  292. Chunks :: [{chunkid(), dataB()}],
  293. Binary :: binary().
  294. build_module(Chunks0) ->
  295. Chunks = list_to_binary(build_chunks(Chunks0)),
  296. Size = byte_size(Chunks),
  297. 0 = Size rem 4, % Assertion: correct padding?
  298. {ok, <<"FOR1", (Size+4):32, "BEAM", Chunks/binary>>}.
  299. %%
  300. %% Local functions
  301. %%
  302. read_info(File) ->
  303. try
  304. {ok, Module, Data} = scan_beam(File, info),
  305. [if
  306. is_binary(File) -> {binary, File};
  307. true -> {file, File}
  308. end, {module, Module}, {chunks, Data}]
  309. catch Error -> Error end.
  310. diff_directories(Dir1, Dir2) ->
  311. {OnlyDir1, OnlyDir2, Diff} = compare_dirs(Dir1, Dir2),
  312. diff_only(Dir1, OnlyDir1),
  313. diff_only(Dir2, OnlyDir2),
  314. foreach(fun(D) -> io:format("** different: ~tp~n", [D]) end, Diff),
  315. ok.
  316. diff_only(_Dir, []) ->
  317. ok;
  318. diff_only(Dir, Only) ->
  319. io:format("Only in ~tp: ~tp~n", [Dir, Only]).
  320. %% -> {OnlyInDir1, OnlyInDir2, Different} | throw(Error)
  321. compare_dirs(Dir1, Dir2) ->
  322. R1 = sofs:relation(beam_files(Dir1)),
  323. R2 = sofs:relation(beam_files(Dir2)),
  324. F1 = sofs:domain(R1),
  325. F2 = sofs:domain(R2),
  326. {O1, Both, O2} = sofs:symmetric_partition(F1, F2),
  327. OnlyL1 = sofs:image(R1, O1),
  328. OnlyL2 = sofs:image(R2, O2),
  329. B1 = sofs:to_external(sofs:restriction(R1, Both)),
  330. B2 = sofs:to_external(sofs:restriction(R2, Both)),
  331. Diff = compare_files(B1, B2, []),
  332. {sofs:to_external(OnlyL1), sofs:to_external(OnlyL2), Diff}.
  333. compare_files([], [], Acc) ->
  334. lists:reverse(Acc);
  335. compare_files([{_,F1} | R1], [{_,F2} | R2], Acc) ->
  336. NAcc = case catch cmp_files(F1, F2) of
  337. {error, _Mod, _Reason} ->
  338. [{F1, F2} | Acc];
  339. ok ->
  340. Acc
  341. end,
  342. compare_files(R1, R2, NAcc).
  343. beam_files(Dir) ->
  344. ok = assert_directory(Dir),
  345. L = filelib:wildcard(filename:join(Dir, "*.beam")),
  346. [{filename:basename(Path), Path} || Path <- L].
  347. %% -> ok | throw(Error)
  348. cmp_files(File1, File2) ->
  349. {ok, {M1, L1}} = read_all_but_useless_chunks(File1),
  350. {ok, {M2, L2}} = read_all_but_useless_chunks(File2),
  351. if
  352. M1 =:= M2 ->
  353. cmp_lists(L1, L2);
  354. true ->
  355. error({modules_different, M1, M2})
  356. end.
  357. cmp_lists([], []) ->
  358. ok;
  359. cmp_lists([{Id, C1} | R1], [{Id, C2} | R2]) ->
  360. if
  361. C1 =:= C2 ->
  362. cmp_lists(R1, R2);
  363. true ->
  364. error({chunks_different, Id})
  365. end;
  366. cmp_lists(_, _) ->
  367. error(different_chunks).
  368. strip_rel(Root, AdditionalChunks) ->
  369. ok = assert_directory(Root),
  370. strip_fils(filelib:wildcard(filename:join(Root, "lib/*/ebin/*.beam")), AdditionalChunks).
  371. %% -> {ok, [{Mod, BinaryOrFileName}]} | throw(Error)
  372. strip_fils(Files, AdditionalChunks) ->
  373. {ok, [begin {ok, Reply} = strip_file(F, AdditionalChunks), Reply end || F <- Files]}.
  374. %% -> {ok, {Mod, FileName}} | {ok, {Mod, binary()}} | throw(Error)
  375. strip_file(File, AdditionalChunks) ->
  376. {ok, {Mod, Chunks}} = read_significant_chunks(File, AdditionalChunks ++ significant_chunks()),
  377. {ok, Stripped0} = build_module(Chunks),
  378. Stripped = compress(Stripped0),
  379. case File of
  380. _ when is_binary(File) ->
  381. {ok, {Mod, Stripped}};
  382. _ ->
  383. FileName = beam_filename(File),
  384. case file:open(FileName, [raw, binary, write]) of
  385. {ok, Fd} ->
  386. case file:write(Fd, Stripped) of
  387. ok ->
  388. ok = file:close(Fd),
  389. {ok, {Mod, FileName}};
  390. Error ->
  391. ok = file:close(Fd),
  392. file_error(FileName, Error)
  393. end;
  394. Error ->
  395. file_error(FileName, Error)
  396. end
  397. end.
  398. build_chunks([{Id, Data} | Chunks]) ->
  399. BId = list_to_binary(Id),
  400. Size = byte_size(Data),
  401. Chunk = [<<BId/binary, Size:32>>, Data | pad(Size)],
  402. [Chunk | build_chunks(Chunks)];
  403. build_chunks([]) ->
  404. [].
  405. pad(Size) ->
  406. case Size rem 4 of
  407. 0 -> [];
  408. Rem -> lists:duplicate(4 - Rem, 0)
  409. end.
  410. %% -> {ok, {Module, Chunks}} | throw(Error)
  411. read_all_but_useless_chunks(File0) when is_atom(File0);
  412. is_list(File0);
  413. is_binary(File0) ->
  414. File = beam_filename(File0),
  415. {ok, Module, ChunkIds0} = scan_beam(File, info),
  416. ChunkIds = [Name || {Name,_,_} <- ChunkIds0,
  417. not is_useless_chunk(Name)],
  418. {ok, Module, Chunks} = scan_beam(File, ChunkIds),
  419. {ok, {Module, lists:reverse(Chunks)}}.
  420. is_useless_chunk("CInf") -> true;
  421. is_useless_chunk(_) -> false.
  422. %% -> {ok, {Module, Chunks}} | throw(Error)
  423. read_significant_chunks(File, ChunkList) ->
  424. case read_chunk_data(File, ChunkList, [allow_missing_chunks]) of
  425. {ok, {Module, Chunks0}} ->
  426. Mandatory = mandatory_chunks(),
  427. Chunks = filter_significant_chunks(Chunks0, Mandatory, File, Module),
  428. {ok, {Module, Chunks}}
  429. end.
  430. filter_significant_chunks([{_, Data}=Pair|Cs], Mandatory, File, Mod)
  431. when is_binary(Data) ->
  432. [Pair|filter_significant_chunks(Cs, Mandatory, File, Mod)];
  433. filter_significant_chunks([{Id, missing_chunk}|Cs], Mandatory, File, Mod) ->
  434. case member(Id, Mandatory) of
  435. false ->
  436. filter_significant_chunks(Cs, Mandatory, File, Mod);
  437. true ->
  438. error({missing_chunk, File, Id})
  439. end;
  440. filter_significant_chunks([], _, _, _) -> [].
  441. filter_funtab([{"FunT"=Tag, <<L:4/binary, Data0/binary>>}|Cs]) ->
  442. Data = filter_funtab_1(Data0, <<0:32>>),
  443. Funtab = <<L/binary, (iolist_to_binary(Data))/binary>>,
  444. [{Tag, Funtab}|filter_funtab(Cs)];
  445. filter_funtab([H|T]) ->
  446. [H|filter_funtab(T)];
  447. filter_funtab([]) -> [].
  448. filter_funtab_1(<<Important:20/binary,_OldUniq:4/binary,T/binary>>, Zero) ->
  449. [Important,Zero|filter_funtab_1(T, Zero)];
  450. filter_funtab_1(Tail, _) when is_binary(Tail) -> [Tail].
  451. read_all_chunks(File0) when is_atom(File0);
  452. is_list(File0);
  453. is_binary(File0) ->
  454. try
  455. File = beam_filename(File0),
  456. {ok, Module, ChunkIds0} = scan_beam(File, info),
  457. ChunkIds = [Name || {Name,_,_} <- ChunkIds0],
  458. {ok, Module, Chunks} = scan_beam(File, ChunkIds),
  459. {ok, Module, lists:reverse(Chunks)}
  460. catch Error -> Error end.
  461. read_chunk_data(File0, ChunkNames) ->
  462. try read_chunk_data(File0, ChunkNames, [])
  463. catch Error -> Error end.
  464. %% -> {ok, {Module, Symbols}} | throw(Error)
  465. read_chunk_data(File0, ChunkNames0, Options)
  466. when is_atom(File0); is_list(File0); is_binary(File0) ->
  467. File = beam_filename(File0),
  468. {ChunkIds, Names, Optional} = check_chunks(ChunkNames0, File, [], [], []),
  469. AllowMissingChunks = member(allow_missing_chunks, Options),
  470. {ok, Module, Chunks} = scan_beam(File, ChunkIds, AllowMissingChunks, Optional),
  471. AT = ets:new(beam_symbols, []),
  472. T = {empty, AT},
  473. try chunks_to_data(Names, Chunks, File, Chunks, Module, T, [])
  474. after ets:delete(AT)
  475. end.
  476. %% -> {ok, list()} | throw(Error)
  477. check_chunks([atoms | Ids], File, IL, L, O) ->
  478. check_chunks(Ids, File, ["Atom", "AtU8" | IL],
  479. [{atom_chunk, atoms} | L], ["Atom", "AtU8" | O]);
  480. check_chunks([abstract_code | Ids], File, IL, L, O) ->
  481. check_chunks(Ids, File, ["Abst", "Dbgi" | IL],
  482. [{abst_chunk, abstract_code} | L], ["Abst", "Dbgi" | O]);
  483. check_chunks([ChunkName | Ids], File, IL, L, O) when is_atom(ChunkName) ->
  484. ChunkId = chunk_name_to_id(ChunkName, File),
  485. check_chunks(Ids, File, [ChunkId | IL], [{ChunkId, ChunkName} | L], O);
  486. check_chunks([ChunkId | Ids], File, IL, L, O) -> % when is_list(ChunkId)
  487. check_chunks(Ids, File, [ChunkId | IL], [{ChunkId, ChunkId} | L], O);
  488. check_chunks([], _File, IL, L, O) ->
  489. {lists:usort(IL), reverse(L), O}.
  490. %% -> {ok, Module, Data} | throw(Error)
  491. scan_beam(File, What) ->
  492. scan_beam(File, What, false, []).
  493. %% -> {ok, Module, Data} | throw(Error)
  494. scan_beam(File, What0, AllowMissingChunks, OptionalChunks) ->
  495. case scan_beam1(File, What0) of
  496. {missing, _FD, Mod, Data, What} when AllowMissingChunks ->
  497. {ok, Mod, [{Id, missing_chunk} || Id <- What] ++ Data};
  498. {missing, FD, Mod, Data, What} ->
  499. case What -- OptionalChunks of
  500. [] -> {ok, Mod, Data};
  501. [Missing | _] -> error({missing_chunk, filename(FD), Missing})
  502. end;
  503. R ->
  504. R
  505. end.
  506. %% -> {ok, Module, Data} | throw(Error)
  507. scan_beam1(File, What) ->
  508. FD = open_file(File),
  509. case catch scan_beam2(FD, What) of
  510. Error when error =:= element(1, Error) ->
  511. throw(Error);
  512. R ->
  513. R
  514. end.
  515. scan_beam2(FD, What) ->
  516. case pread(FD, 0, 12) of
  517. {NFD, {ok, <<"FOR1", _Size:32, "BEAM">>}} ->
  518. Start = 12,
  519. scan_beam(NFD, Start, What, 17, []);
  520. _Error ->
  521. error({not_a_beam_file, filename(FD)})
  522. end.
  523. scan_beam(_FD, _Pos, [], Mod, Data) when Mod =/= 17 ->
  524. {ok, Mod, Data};
  525. scan_beam(FD, Pos, What, Mod, Data) ->
  526. case pread(FD, Pos, 8) of
  527. {_NFD, eof} when Mod =:= 17 ->
  528. error({missing_chunk, filename(FD), "Atom"});
  529. {_NFD, eof} when What =:= info ->
  530. {ok, Mod, reverse(Data)};
  531. {NFD, eof} ->
  532. {missing, NFD, Mod, Data, What};
  533. {NFD, {ok, <<IdL:4/binary, Sz:32>>}} ->
  534. Id = binary_to_list(IdL),
  535. Pos1 = Pos + 8,
  536. Pos2 = (4 * trunc((Sz+3) / 4)) + Pos1,
  537. get_data(What, Id, NFD, Sz, Pos1, Pos2, Mod, Data);
  538. {_NFD, {ok, _ChunkHead}} ->
  539. error({invalid_beam_file, filename(FD), Pos})
  540. end.
  541. get_atom_data(Cs, Id, FD, Size, Pos, Pos2, Data, Encoding) ->
  542. NewCs = del_chunk(Id, Cs),
  543. {NFD, Chunk} = get_chunk(Id, Pos, Size, FD),
  544. <<_Num:32, Chunk2/binary>> = Chunk,
  545. {Module, _} = extract_atom(Chunk2, Encoding),
  546. C = case Cs of
  547. info ->
  548. {Id, Pos, Size};
  549. _ ->
  550. {Id, Chunk}
  551. end,
  552. scan_beam(NFD, Pos2, NewCs, Module, [C | Data]).
  553. get_data(Cs, "Atom" = Id, FD, Size, Pos, Pos2, _Mod, Data) ->
  554. get_atom_data(Cs, Id, FD, Size, Pos, Pos2, Data, latin1);
  555. get_data(Cs, "AtU8" = Id, FD, Size, Pos, Pos2, _Mod, Data) ->
  556. get_atom_data(Cs, Id, FD, Size, Pos, Pos2, Data, utf8);
  557. get_data(info, Id, FD, Size, Pos, Pos2, Mod, Data) ->
  558. scan_beam(FD, Pos2, info, Mod, [{Id, Pos, Size} | Data]);
  559. get_data(Chunks, Id, FD, Size, Pos, Pos2, Mod, Data) ->
  560. {NFD, NewData} = case member(Id, Chunks) of
  561. true ->
  562. {FD1, Chunk} = get_chunk(Id, Pos, Size, FD),
  563. {FD1, [{Id, Chunk} | Data]};
  564. false ->
  565. {FD, Data}
  566. end,
  567. NewChunks = del_chunk(Id, Chunks),
  568. scan_beam(NFD, Pos2, NewChunks, Mod, NewData).
  569. del_chunk(_Id, info) ->
  570. info;
  571. del_chunk(Id, Chunks) ->
  572. delete(Id, Chunks).
  573. %% -> {NFD, binary()} | throw(Error)
  574. get_chunk(Id, Pos, Size, FD) ->
  575. case pread(FD, Pos, Size) of
  576. {NFD, eof} when Size =:= 0 -> % cannot happen
  577. {NFD, <<>>};
  578. {_NFD, eof} when Size > 0 ->
  579. error({chunk_too_big, filename(FD), Id, Size, 0});
  580. {_NFD, {ok, Chunk}} when Size > byte_size(Chunk) ->
  581. error({chunk_too_big, filename(FD), Id, Size, byte_size(Chunk)});
  582. {NFD, {ok, Chunk}} -> % when Size =:= size(Chunk)
  583. {NFD, Chunk}
  584. end.
  585. chunks_to_data([{atom_chunk, Name} | CNs], Chunks, File, Cs, Module, Atoms, L) ->
  586. {NewAtoms, Ret} = chunk_to_data(Name, <<"">>, File, Cs, Atoms, Module),
  587. chunks_to_data(CNs, Chunks, File, Cs, Module, NewAtoms, [Ret | L]);
  588. chunks_to_data([{abst_chunk, Name} | CNs], Chunks, File, Cs, Module, Atoms, L) ->
  589. DbgiChunk = proplists:get_value("Dbgi", Chunks, <<"">>),
  590. {NewAtoms, Ret} =
  591. case catch chunk_to_data(debug_info, DbgiChunk, File, Cs, Atoms, Module) of
  592. {DbgiAtoms, {debug_info, {debug_info_v1, Backend, Metadata}}} ->
  593. case Backend:debug_info(erlang_v1, Module, Metadata, []) of
  594. {ok, Code} -> {DbgiAtoms, {abstract_code, {raw_abstract_v1, Code}}};
  595. {error, _} -> {DbgiAtoms, {abstract_code, no_abstract_code}}
  596. end;
  597. {error,beam_lib,{key_missing_or_invalid,Path,debug_info}} ->
  598. error({key_missing_or_invalid,Path,abstract_code});
  599. _ ->
  600. AbstChunk = proplists:get_value("Abst", Chunks, <<"">>),
  601. chunk_to_data(Name, AbstChunk, File, Cs, Atoms, Module)
  602. end,
  603. chunks_to_data(CNs, Chunks, File, Cs, Module, NewAtoms, [Ret | L]);
  604. chunks_to_data([{Id, Name} | CNs], Chunks, File, Cs, Module, Atoms, L) ->
  605. {_Id, Chunk} = lists:keyfind(Id, 1, Chunks),
  606. {NewAtoms, Ret} = chunk_to_data(Name, Chunk, File, Cs, Atoms, Module),
  607. chunks_to_data(CNs, Chunks, File, Cs, Module, NewAtoms, [Ret | L]);
  608. chunks_to_data([], _Chunks, _File, _Cs, Module, _Atoms, L) ->
  609. {ok, {Module, reverse(L)}}.
  610. chunk_to_data(attributes=Id, Chunk, File, _Cs, AtomTable, _Mod) ->
  611. try
  612. Term = binary_to_term(Chunk),
  613. {AtomTable, {Id, attributes(Term)}}
  614. catch
  615. error:badarg ->
  616. error({invalid_chunk, File, chunk_name_to_id(Id, File)})
  617. end;
  618. chunk_to_data(compile_info=Id, Chunk, File, _Cs, AtomTable, _Mod) ->
  619. try
  620. {AtomTable, {Id, binary_to_term(Chunk)}}
  621. catch
  622. error:badarg ->
  623. error({invalid_chunk, File, chunk_name_to_id(Id, File)})
  624. end;
  625. chunk_to_data(debug_info=Id, Chunk, File, _Cs, AtomTable, Mod) ->
  626. case Chunk of
  627. <<>> ->
  628. {AtomTable, {Id, no_debug_info}};
  629. <<0:8,N:8,Mode0:N/binary,Rest/binary>> ->
  630. Mode = binary_to_atom(Mode0, utf8),
  631. Term = decrypt_chunk(Mode, Mod, File, Id, Rest),
  632. {AtomTable, {Id, anno_from_term(Term)}};
  633. _ ->
  634. case catch binary_to_term(Chunk) of
  635. {'EXIT', _} ->
  636. error({invalid_chunk, File, chunk_name_to_id(Id, File)});
  637. Term ->
  638. {AtomTable, {Id, anno_from_term(Term)}}
  639. end
  640. end;
  641. chunk_to_data(abstract_code=Id, Chunk, File, _Cs, AtomTable, Mod) ->
  642. %% Before Erlang/OTP 20.0.
  643. case Chunk of
  644. <<>> ->
  645. {AtomTable, {Id, no_abstract_code}};
  646. <<0:8,N:8,Mode0:N/binary,Rest/binary>> ->
  647. Mode = binary_to_atom(Mode0, utf8),
  648. Term = decrypt_chunk(Mode, Mod, File, Id, Rest),
  649. {AtomTable, {Id, old_anno_from_term(Term)}};
  650. _ ->
  651. case catch binary_to_term(Chunk) of
  652. {'EXIT', _} ->
  653. error({invalid_chunk, File, chunk_name_to_id(Id, File)});
  654. Term ->
  655. try
  656. {AtomTable, {Id, old_anno_from_term(Term)}}
  657. catch
  658. _:_ ->
  659. error({invalid_chunk, File,
  660. chunk_name_to_id(Id, File)})
  661. end
  662. end
  663. end;
  664. chunk_to_data(atoms=Id, _Chunk, _File, Cs, AtomTable0, _Mod) ->
  665. AtomTable = ensure_atoms(AtomTable0, Cs),
  666. Atoms = ets:tab2list(AtomTable),
  667. {AtomTable, {Id, lists:sort(Atoms)}};
  668. chunk_to_data(ChunkName, Chunk, File,
  669. Cs, AtomTable, _Mod) when is_atom(ChunkName) ->
  670. case catch symbols(Chunk, AtomTable, Cs, ChunkName) of
  671. {ok, NewAtomTable, S} ->
  672. {NewAtomTable, {ChunkName, S}};
  673. {'EXIT', _} ->
  674. error({invalid_chunk, File, chunk_name_to_id(ChunkName, File)})
  675. end;
  676. chunk_to_data(ChunkId, Chunk, _File,
  677. _Cs, AtomTable, _Module) when is_list(ChunkId) ->
  678. {AtomTable, {ChunkId, Chunk}}. % Chunk is a binary
  679. chunk_name_to_id(indexed_imports, _) -> "ImpT";
  680. chunk_name_to_id(imports, _) -> "ImpT";
  681. chunk_name_to_id(exports, _) -> "ExpT";
  682. chunk_name_to_id(labeled_exports, _) -> "ExpT";
  683. chunk_name_to_id(locals, _) -> "LocT";
  684. chunk_name_to_id(labeled_locals, _) -> "LocT";
  685. chunk_name_to_id(attributes, _) -> "Attr";
  686. chunk_name_to_id(abstract_code, _) -> "Abst";
  687. chunk_name_to_id(debug_info, _) -> "Dbgi";
  688. chunk_name_to_id(compile_info, _) -> "CInf";
  689. chunk_name_to_id(Other, File) ->
  690. error({unknown_chunk, File, Other}).
  691. %% Extract attributes
  692. attributes(Attrs) ->
  693. attributes(keysort(1, Attrs), []).
  694. attributes([], R) ->
  695. reverse(R);
  696. attributes(L, R) ->
  697. K = element(1, hd(L)),
  698. {L1, L2} = splitwith(fun(T) -> element(1, T) =:= K end, L),
  699. V = append([A || {_, A} <- L1]),
  700. attributes(L2, [{K, V} | R]).
  701. %% Extract symbols
  702. symbols(<<_Num:32, B/binary>>, AT0, Cs, Name) ->
  703. AT = ensure_atoms(AT0, Cs),
  704. symbols1(B, AT, Name, [], 1).
  705. symbols1(<<I1:32, I2:32, I3:32, B/binary>>, AT, Name, S, Cnt) ->
  706. Symbol = symbol(Name, AT, I1, I2, I3, Cnt),
  707. symbols1(B, AT, Name, [Symbol|S], Cnt+1);
  708. symbols1(<<>>, AT, _Name, S, _Cnt) ->
  709. {ok, AT, sort(S)}.
  710. symbol(indexed_imports, AT, I1, I2, I3, Cnt) ->
  711. {Cnt, atm(AT, I1), atm(AT, I2), I3};
  712. symbol(imports, AT, I1, I2, I3, _Cnt) ->
  713. {atm(AT, I1), atm(AT, I2), I3};
  714. symbol(labeled_exports, AT, I1, I2, I3, _Cnt) ->
  715. {atm(AT, I1), I2, I3};
  716. symbol(labeled_locals, AT, I1, I2, I3, _Cnt) ->
  717. {atm(AT, I1), I2, I3};
  718. symbol(_, AT, I1, I2, _I3, _Cnt) ->
  719. {atm(AT, I1), I2}.
  720. atm(AT, N) ->
  721. [{_N, S}] = ets:lookup(AT, N),
  722. S.
  723. %% AT is updated.
  724. ensure_atoms({empty, AT}, Cs) ->
  725. case lists:keyfind("AtU8", 1, Cs) of
  726. {_Id, AtomChunk} when is_binary(AtomChunk) ->
  727. extract_atoms(AtomChunk, AT, utf8);
  728. _ ->
  729. {_Id, AtomChunk} = lists:keyfind("Atom", 1, Cs),
  730. extract_atoms(AtomChunk, AT, latin1)
  731. end,
  732. AT;
  733. ensure_atoms(AT, _Cs) ->
  734. AT.
  735. extract_atoms(<<_Num:32, B/binary>>, AT, Encoding) ->
  736. extract_atoms(B, 1, AT, Encoding).
  737. extract_atoms(<<>>, _I, _AT, _Encoding) ->
  738. true;
  739. extract_atoms(B, I, AT, Encoding) ->
  740. {Atom, B1} = extract_atom(B, Encoding),
  741. true = ets:insert(AT, {I, Atom}),
  742. extract_atoms(B1, I+1, AT, Encoding).
  743. extract_atom(<<Len, B/binary>>, Encoding) ->
  744. <<SB:Len/binary, Tail/binary>> = B,
  745. {binary_to_atom(SB, Encoding), Tail}.
  746. %%% Utils.
  747. -record(bb, {pos = 0 :: integer(),
  748. bin :: binary(),
  749. source :: binary() | string()}).
  750. open_file(<<"FOR1",_/binary>>=Binary) ->
  751. #bb{bin = Binary, source = Binary};
  752. open_file(Binary0) when is_binary(Binary0) ->
  753. Binary = uncompress(Binary0),
  754. #bb{bin = Binary, source = Binary};
  755. open_file(FileName) ->
  756. case file:open(FileName, [read, raw, binary]) of
  757. {ok, Fd} ->
  758. read_all(Fd, FileName, []);
  759. Error ->
  760. file_error(FileName, Error)
  761. end.
  762. read_all(Fd, FileName, Bins) ->
  763. case file:read(Fd, 1 bsl 18) of
  764. {ok, Bin} ->
  765. read_all(Fd, FileName, [Bin | Bins]);
  766. eof ->
  767. ok = file:close(Fd),
  768. #bb{bin = uncompress(reverse(Bins)), source = FileName};
  769. Error ->
  770. ok = file:close(Fd),
  771. file_error(FileName, Error)
  772. end.
  773. pread(FD, AtPos, Size) ->
  774. #bb{pos = Pos, bin = Binary} = FD,
  775. Skip = AtPos-Pos,
  776. case Binary of
  777. <<_:Skip/binary, B:Size/binary, Bin/binary>> ->
  778. NFD = FD#bb{pos = AtPos+Size, bin = Bin},
  779. {NFD, {ok, B}};
  780. <<_:Skip/binary, Bin/binary>> when byte_size(Bin) > 0 ->
  781. NFD = FD#bb{pos = AtPos+byte_size(Bin), bin = <<>>},
  782. {NFD, {ok, Bin}};
  783. _ ->
  784. {FD, eof}
  785. end.
  786. filename(BB) when is_binary(BB#bb.source) ->
  787. BB#bb.source;
  788. filename(BB) ->
  789. list_to_atom(BB#bb.source).
  790. beam_filename(Bin) when is_binary(Bin) ->
  791. Bin;
  792. beam_filename(File) ->
  793. filename:rootname(File, ".beam") ++ ".beam".
  794. uncompress(Binary0) ->
  795. {ok, Fd} = ram_file:open(Binary0, [write, binary]),
  796. {ok, _} = ram_file:uncompress(Fd),
  797. {ok, Binary} = ram_file:get_file(Fd),
  798. ok = ram_file:close(Fd),
  799. Binary.
  800. compress(Binary0) ->
  801. {ok, Fd} = ram_file:open(Binary0, [write, binary]),
  802. {ok, _} = ram_file:compress(Fd),
  803. {ok, Binary} = ram_file:get_file(Fd),
  804. ok = ram_file:close(Fd),
  805. Binary.
  806. %% -> ok | throw(Error)
  807. assert_directory(FileName) ->
  808. case filelib:is_dir(FileName) of
  809. true ->
  810. ok;
  811. false ->
  812. error({not_a_directory, FileName})
  813. end.
  814. -spec file_error(file:filename(), {'error',atom()}) -> no_return().
  815. file_error(FileName, {error, Reason}) ->
  816. error({file_error, FileName, Reason}).
  817. -spec error(term()) -> no_return().
  818. error(Reason) ->
  819. throw({error, ?MODULE, Reason}).
  820. %% The following chunks must be kept when stripping a BEAM file.
  821. significant_chunks() ->
  822. ["Line" | md5_chunks()].
  823. %% The following chunks are significant when calculating the MD5
  824. %% for a module. They are listed in the order that they should be MD5:ed.
  825. md5_chunks() ->
  826. ["Atom", "AtU8", "Code", "StrT", "ImpT", "ExpT", "FunT", "LitT"].
  827. %% The following chunks are mandatory in every Beam file.
  828. mandatory_chunks() ->
  829. ["Code", "ExpT", "ImpT", "StrT"].
  830. %%% ====================================================================
  831. %%% The rest of the file handles encrypted debug info.
  832. %%%
  833. %%% Encrypting the debug info is only useful if you want to
  834. %%% have the debug info available all the time (maybe even in a live
  835. %%% system), but don't want to risk that anyone else but yourself
  836. %%% can use it.
  837. %%% ====================================================================
  838. -record(state, {crypto_key_f :: crypto_fun() | 'undefined'}).
  839. -define(CRYPTO_KEY_SERVER, beam_lib__crypto_key_server).
  840. decrypt_chunk(Type, Module, File, Id, Bin) ->
  841. try
  842. KeyString = get_crypto_key({debug_info, Type, Module, File}),
  843. {Type,Key,IVec,_BlockSize} = make_crypto_key(Type, KeyString),
  844. ok = start_crypto(),
  845. NewBin = crypto:block_decrypt(Type, Key, IVec, Bin),
  846. binary_to_term(NewBin)
  847. catch
  848. _:_ ->
  849. error({key_missing_or_invalid, File, Id})
  850. end.
  851. old_anno_from_term({raw_abstract_v1, Forms}) ->
  852. {raw_abstract_v1, anno_from_forms(Forms)};
  853. old_anno_from_term({Tag, Forms}) when Tag =:= abstract_v1;
  854. Tag =:= abstract_v2 ->
  855. try {Tag, anno_from_forms(Forms)}
  856. catch
  857. _:_ ->
  858. {Tag, Forms}
  859. end;
  860. old_anno_from_term(T) ->
  861. T.
  862. anno_from_term({debug_info_v1=Tag1, erl_abstract_code=Tag2, {Forms, Opts}}) ->
  863. try {Tag1, Tag2, {anno_from_forms(Forms), Opts}}
  864. catch
  865. _:_ ->
  866. {Tag1, Tag2, {Forms, Opts}}
  867. end;
  868. anno_from_term(T) ->
  869. T.
  870. anno_from_forms(Forms0) ->
  871. %% Forms with record field types created before OTP 19.0 are
  872. %% replaced by well-formed record forms holding the type
  873. %% information.
  874. Forms = epp:restore_typed_record_fields(Forms0),
  875. [erl_parse:anno_from_term(Form) || Form <- Forms].
  876. start_crypto() ->
  877. case crypto:start() of
  878. {error, {already_started, _}} ->
  879. ok;
  880. ok ->
  881. ok
  882. end.
  883. get_crypto_key(What) ->
  884. call_crypto_server({get_crypto_key, What}).
  885. call_crypto_server(Req) ->
  886. try
  887. gen_server:call(?CRYPTO_KEY_SERVER, Req, infinity)
  888. catch
  889. exit:{noproc,_} ->
  890. %% Not started.
  891. call_crypto_server_1(Req);
  892. exit:{normal,_} ->
  893. %% The process finished just as we called it.
  894. call_crypto_server_1(Req)
  895. end.
  896. call_crypto_server_1(Req) ->
  897. case gen_server:start({local,?CRYPTO_KEY_SERVER}, ?MODULE, [], []) of
  898. {ok, _} -> ok;
  899. {error, {already_started, _}} -> ok
  900. end,
  901. erlang:yield(),
  902. call_crypto_server(Req).
  903. -spec init([]) -> {'ok', #state{}}.
  904. init([]) ->
  905. {ok, #state{}}.
  906. -type calls() :: 'clear_crypto_key_fun'
  907. | {'crypto_key_fun', _}
  908. | {'get_crypto_key', _}.
  909. -spec handle_call(calls(), {pid(), term()}, #state{}) ->
  910. {'noreply', #state{}} |
  911. {'reply', 'error' | {'error','badfun' | 'exists'}, #state{}} |
  912. {'stop', 'normal', 'undefined' | {'ok', term()}, #state{}}.
  913. handle_call({get_crypto_key, _}=R, From, #state{crypto_key_f=undefined}=S) ->
  914. case crypto_key_fun_from_file() of
  915. error ->
  916. {reply, error, S};
  917. F when is_function(F) ->
  918. %% The init function for the fun has already been called.
  919. handle_call(R, From, S#state{crypto_key_f=F})
  920. end;
  921. handle_call({get_crypto_key, What}, From, #state{crypto_key_f=F}=S) ->
  922. try
  923. Result = F(What),
  924. %% The result may hold information that we don't want
  925. %% lying around. Reply first, then GC, then noreply.
  926. gen_server:reply(From, Result),
  927. erlang:garbage_collect(),
  928. {noreply, S}
  929. catch
  930. _:_ ->
  931. {reply, error, S}
  932. end;
  933. handle_call({crypto_key_fun, F}, {_,_} = From, S) ->
  934. case S#state.crypto_key_f of
  935. undefined ->
  936. if is_function(F, 1) ->
  937. {Result, Fun, Reply} =
  938. case catch F(init) of
  939. ok ->
  940. {true, F, ok};
  941. {ok, F1} when is_function(F1) ->
  942. if
  943. is_function(F1, 1) ->
  944. {true, F1, ok};
  945. true ->
  946. {false, undefined,
  947. {error, badfun}}
  948. end;
  949. {error, Reason} ->
  950. {false, undefined, {error, Reason}};
  951. {'EXIT', Reason} ->
  952. {false, undefined, {error, Reason}}
  953. end,
  954. gen_server:reply(From, Reply),
  955. erlang:garbage_collect(),
  956. NewS = case Result of
  957. true ->
  958. S#state{crypto_key_f = Fun};
  959. false ->
  960. S
  961. end,
  962. {noreply, NewS};
  963. true ->
  964. {reply, {error, badfun}, S}
  965. end;
  966. OtherF when is_function(OtherF) ->
  967. {reply, {error, exists}, S}
  968. end;
  969. handle_call(clear_crypto_key_fun, _From, S) ->
  970. case S#state.crypto_key_f of
  971. undefined ->
  972. {stop,normal,undefined,S};
  973. F ->
  974. Result = (catch F(clear)),
  975. {stop,normal,{ok,Result},S}
  976. end.
  977. -spec handle_cast(term(), #state{}) -> {'noreply', #state{}}.
  978. handle_cast(_, State) ->
  979. {noreply, State}.
  980. -spec handle_info(term(), #state{}) -> {'noreply', #state{}}.
  981. handle_info(_, State) ->
  982. {noreply, State}.
  983. -spec code_change(term(), #state{}, term()) -> {'ok', #state{}}.
  984. code_change(_OldVsn, State, _Extra) ->
  985. {ok, State}.
  986. -spec terminate(term(), #state{}) -> 'ok'.
  987. terminate(_Reason, _State) ->
  988. ok.
  989. crypto_key_fun_from_file() ->
  990. case init:get_argument(home) of
  991. {ok,[[Home]]} ->
  992. crypto_key_fun_from_file_1([".",Home]);
  993. _ ->
  994. crypto_key_fun_from_file_1(["."])
  995. end.
  996. crypto_key_fun_from_file_1(Path) ->
  997. case f_p_s(Path, ".erlang.crypt") of
  998. {ok, KeyInfo, _} ->
  999. try_load_crypto_fun(KeyInfo);
  1000. _ ->
  1001. error
  1002. end.
  1003. f_p_s(P, F) ->
  1004. case file:path_script(P, F) of
  1005. {error, enoent} ->
  1006. {error, enoent};
  1007. {error, {Line, _Mod, _Term}=E} ->
  1008. error("file:path_script(~tp,~tp): error on line ~p: ~ts~n",
  1009. [P, F, Line, file:format_error(E)]),
  1010. ok;
  1011. {error, E} when is_atom(E) ->
  1012. error("file:path_script(~tp,~tp): ~ts~n",
  1013. [P, F, file:format_error(E)]),
  1014. ok;
  1015. Other ->
  1016. Other
  1017. end.
  1018. try_load_crypto_fun(KeyInfo) when is_list(KeyInfo) ->
  1019. T = ets:new(keys, [private, set]),
  1020. foreach(
  1021. fun({debug_info, Mode, M, Key}) when is_atom(M) ->
  1022. ets:insert(T, {{debug_info,Mode,M,[]}, Key});
  1023. ({debug_info, Mode, [], Key}) ->
  1024. ets:insert(T, {{debug_info, Mode, [], []}, Key});
  1025. (Other) ->
  1026. error("unknown key: ~p~n", [Other])
  1027. end, KeyInfo),
  1028. fun({debug_info, Mode, M, F}) ->
  1029. alt_lookup_key(
  1030. [{debug_info,Mode,M,F},
  1031. {debug_info,Mode,M,[]},
  1032. {debug_info,Mode,[],[]}], T);
  1033. (clear) ->
  1034. ets:delete(T);
  1035. (_) ->
  1036. error
  1037. end;
  1038. try_load_crypto_fun(KeyInfo) ->
  1039. error("unrecognized crypto key info: ~p\n", [KeyInfo]).
  1040. alt_lookup_key([H|T], Tab) ->
  1041. case ets:lookup(Tab, H) of
  1042. [] ->
  1043. alt_lookup_key(T, Tab);
  1044. [{_, Val}] ->
  1045. Val
  1046. end;
  1047. alt_lookup_key([], _) ->
  1048. error.
  1049. error(Fmt, Args) ->
  1050. error_logger:error_msg(Fmt, Args),
  1051. error.