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