1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1997-2011. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: -module(beam_lib_SUITE). 20: 21: %-define(debug, true). 22: 23: -ifdef(debug). 24: -define(format(S, A), io:format(S, A)). 25: -define(line, put(line, ?LINE), ). 26: -define(config(X,Y), "./log_dir/"). 27: -define(t,test_server). 28: -define(privdir, "beam_lib_SUITE_priv"). 29: -else. 30: -include_lib("test_server/include/test_server.hrl"). 31: -define(format(S, A), ok). 32: -define(privdir, ?config(priv_dir, Conf)). 33: -endif. 34: 35: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 36: init_per_group/2,end_per_group/2, 37: normal/1, error/1, cmp/1, cmp_literals/1, strip/1, otp_6711/1, 38: building/1, md5/1, encrypted_abstr/1, encrypted_abstr_file/1]). 39: 40: -export([init_per_testcase/2, end_per_testcase/2]). 41: 42: suite() -> [{ct_hooks,[ts_install_cth]}]. 43: 44: all() -> 45: [error, normal, cmp, cmp_literals, strip, otp_6711, 46: building, md5, encrypted_abstr, encrypted_abstr_file]. 47: 48: groups() -> 49: []. 50: 51: init_per_suite(Config) -> 52: Config. 53: 54: end_per_suite(_Config) -> 55: ok. 56: 57: init_per_group(_GroupName, Config) -> 58: Config. 59: 60: end_per_group(_GroupName, Config) -> 61: Config. 62: 63: 64: init_per_testcase(_Case, Config) -> 65: Dog=?t:timetrap(?t:minutes(2)), 66: [{watchdog, Dog}|Config]. 67: 68: end_per_testcase(_Case, Config) -> 69: Dog=?config(watchdog, Config), 70: test_server:timetrap_cancel(Dog), 71: ok. 72: 73: normal(suite) -> []; 74: normal(doc) -> ["Read correct beam file"]; 75: normal(Conf) when is_list(Conf) -> 76: ?line PrivDir = ?privdir, 77: ?line Simple = filename:join(PrivDir, "simple"), 78: ?line Source = Simple ++ ".erl", 79: ?line BeamFile = Simple ++ ".beam", 80: ?line simple_file(Source), 81: 82: ?line NoOfTables = length(ets:all()), 83: ?line P0 = pps(), 84: 85: CompileFlags = [{outdir,PrivDir}, debug_info], 86: ?line {ok,_} = compile:file(Source, CompileFlags), 87: ?line {ok, Binary} = file:read_file(BeamFile), 88: 89: ?line do_normal(BeamFile), 90: ?line do_normal(Binary), 91: 92: ?line {ok,_} = compile:file(Source, [{outdir,PrivDir}, no_debug_info]), 93: ?line {ok, {simple, [{abstract_code, no_abstract_code}]}} = 94: beam_lib:chunks(BeamFile, [abstract_code]), 95: 96: %% ?line {ok,_} = compile:file(Source, [compressed | CompileFlags]), 97: %% ?line do_normal(BeamFile), 98: 99: ?line file:delete(BeamFile), 100: ?line file:delete(Source), 101: ?line NoOfTables = length(ets:all()), 102: ?line true = (P0 == pps()), 103: ok. 104: 105: do_normal(BeamFile) -> 106: ?line Imports = {imports, [{erlang, get_module_info, 1}, 107: {erlang, get_module_info, 2}, 108: {lists, member, 2}]}, 109: ?line Exports = {exports, [{module_info, 0}, {module_info, 1}, {t, 0}]}, 110: ?line Local = {locals, [{t, 1}]}, 111: ?line {ok, {simple, [Imports]}} = beam_lib:chunks(BeamFile, [imports]), 112: ?line {ok, {simple, [{"ImpT",_Bin}]}} = 113: beam_lib:chunks(BeamFile, ["ImpT"]), 114: ?line {ok, {simple, [Exports]}} = beam_lib:chunks(BeamFile, [exports]), 115: ?line {ok, {simple, [{attributes, [{vsn, [_]}]}]}} = 116: beam_lib:chunks(BeamFile, [attributes]), 117: ?line {ok, {simple, [{compile_info, _}=CompileInfo]}} = 118: beam_lib:chunks(BeamFile, [compile_info]), 119: ?line {ok, {simple, [Local]}} = beam_lib:chunks(BeamFile, [locals]), 120: ?line {ok, {simple, [{attributes, [{vsn, [_]}]}, CompileInfo, 121: Exports, Imports, Local]}} = 122: beam_lib:chunks(BeamFile, [attributes, compile_info, exports, imports, locals]), 123: ?line {ok, {simple, [{atoms, _Atoms}]}} = 124: beam_lib:chunks(BeamFile, [atoms]), 125: ?line {ok, {simple, [{labeled_exports, _LExports}]}} = 126: beam_lib:chunks(BeamFile, [labeled_exports]), 127: ?line {ok, {simple, [{labeled_locals, _LLocals}]}} = 128: beam_lib:chunks(BeamFile, [labeled_locals]), 129: ?line {ok, {simple, [_Vsn]}} = beam_lib:version(BeamFile), 130: ?line {ok, {simple, [{abstract_code, _}]}} = 131: beam_lib:chunks(BeamFile, [abstract_code]), 132: 133: %% Test reading optional chunks. 134: All = ["Atom", "Code", "StrT", "ImpT", "ExpT", "FunT", "LitT"], 135: ?line {ok,{simple,Chunks}} = beam_lib:chunks(BeamFile, All, [allow_missing_chunks]), 136: ?line verify_simple(Chunks). 137: 138: verify_simple([{"Atom", AtomBin}, 139: {"Code", CodeBin}, 140: {"StrT", StrBin}, 141: {"ImpT", ImpBin}, 142: {"ExpT", ExpBin}, 143: {"FunT", missing_chunk}, 144: {"LitT", missing_chunk}]) 145: when is_binary(AtomBin), is_binary(CodeBin), is_binary(StrBin), 146: is_binary(ImpBin), is_binary(ExpBin) -> 147: ok. 148: 149: error(suite) -> []; 150: error(doc) -> ["Read invalid beam files"]; 151: error(Conf) when is_list(Conf) -> 152: ?line PrivDir = ?privdir, 153: ?line Simple = filename:join(PrivDir, "simple"), 154: ?line Source = Simple ++ ".erl", 155: ?line BeamFile = Simple ++ ".beam", 156: ?line WrongFile = Simple ++ "foo.beam", 157: ?line simple_file(Source), 158: 159: ?line NoOfTables = length(ets:all()), 160: ?line P0 = pps(), 161: ?line {ok,_} = compile:file(Source, [{outdir,PrivDir},debug_info]), 162: ?line ACopy = filename:join(PrivDir, "a_copy.beam"), 163: ?line copy_file(BeamFile, ACopy), 164: 165: ?line {ok, Binary} = file:read_file(BeamFile), 166: 167: ?line copy_file(ACopy, WrongFile), 168: ?line verify(file_error, beam_lib:info("./does_simply_not_exist")), 169: 170: ?line do_error(BeamFile, ACopy), 171: ?line do_error(Binary, ACopy), 172: 173: ?line copy_file(ACopy, BeamFile), 174: ?line verify(unknown_chunk, beam_lib:chunks(BeamFile, [not_a_chunk])), 175: 176: ?line ok = file:write_file(BeamFile, <<>>), 177: ?line verify(not_a_beam_file, beam_lib:info(BeamFile)), 178: ?line verify(not_a_beam_file, beam_lib:info(<<>>)), 179: ?line ok = file:write_file(BeamFile, <<"short">>), 180: ?line verify(not_a_beam_file, beam_lib:info(BeamFile)), 181: ?line verify(not_a_beam_file, beam_lib:info(<<"short">>)), 182: 183: ?line {Binary1, _} = split_binary(Binary, byte_size(Binary)-10), 184: LastChunk = last_chunk(Binary), 185: ?line verify(chunk_too_big, beam_lib:chunks(Binary1, [LastChunk])), 186: ?line Chunks = chunk_info(Binary), 187: ?line {value, {_, AbstractStart, _}} = lists:keysearch("Abst", 1, Chunks), 188: ?line {Binary2, _} = split_binary(Binary, AbstractStart), 189: ?line verify(chunk_too_big, beam_lib:chunks(Binary2, ["Abst"])), 190: ?line {Binary3, _} = split_binary(Binary, AbstractStart-4), 191: ?line verify(invalid_beam_file, beam_lib:chunks(Binary3, ["Abst"])), 192: 193: %% Instead of the 5:32 field below, there used to be control characters 194: %% (including zero bytes) directly in the string. Because inferior programs 195: %% such as sed and clearcasediff don't like zero bytes in text files, 196: %% we have eliminated them. 197: ?line ok = file:write_file(BeamFile, <<"FOR1",5:32,"BEAMfel">>), 198: % ?line verify(invalid_beam_file, beam_lib:info(BeamFile)), 199: % ?line verify(invalid_beam_file, beam_lib:info(<<"FOR1",5:32,"BEAMfel">>)), 200: 201: ?line NoOfTables = length(ets:all()), 202: ?line true = (P0 == pps()), 203: ?line file:delete(Source), 204: ?line file:delete(WrongFile), 205: ?line file:delete(BeamFile), 206: ?line file:delete(ACopy), 207: ok. 208: 209: last_chunk(Bin) -> 210: L = beam_lib:info(Bin), 211: {chunks,Chunks} = lists:keyfind(chunks, 1, L), 212: {Last,_,_} = lists:last(Chunks), 213: Last. 214: 215: do_error(BeamFile, ACopy) -> 216: % evil tests 217: ?line Chunks = chunk_info(BeamFile), 218: ?line {value, {_, AtomStart, _}} = lists:keysearch("Atom", 1, Chunks), 219: ?line {value, {_, ImportStart, _}} = lists:keysearch("ImpT", 1, Chunks), 220: ?line {value, {_, AbstractStart, _}} = lists:keysearch("Abst", 1, Chunks), 221: ?line {value, {_, AttributesStart, _}} = 222: lists:keysearch("Attr", 1, Chunks), 223: ?line {value, {_, CompileInfoStart, _}} = 224: lists:keysearch("CInf", 1, Chunks), 225: ?line verify(missing_chunk, beam_lib:chunks(BeamFile, ["__"])), 226: ?line BF2 = set_byte(ACopy, BeamFile, ImportStart+4, 17), 227: ?line verify(invalid_chunk, beam_lib:chunks(BF2, [imports])), 228: ?line BF3 = set_byte(ACopy, BeamFile, AtomStart-6, 17), 229: ?line verify(missing_chunk, beam_lib:chunks(BF3, [imports])), 230: ?line BF4 = set_byte(ACopy, BeamFile, AbstractStart+10, 17), 231: ?line verify(invalid_chunk, beam_lib:chunks(BF4, [abstract_code])), 232: ?line BF5 = set_byte(ACopy, BeamFile, AttributesStart+10, 17), 233: ?line verify(invalid_chunk, beam_lib:chunks(BF5, [attributes])), 234: 235: ?line BF6 = set_byte(ACopy, BeamFile, 1, 17), 236: ?line verify(not_a_beam_file, beam_lib:info(BF6)), 237: ?line BF7 = set_byte(ACopy, BeamFile, 9, 17), 238: ?line verify(not_a_beam_file, beam_lib:info(BF7)), 239: 240: ?line BF8 = set_byte(ACopy, BeamFile, 13, 17), 241: ?line verify(missing_chunk, beam_lib:chunks(BF8, ["Atom"])), 242: 243: ?line BF9 = set_byte(ACopy, BeamFile, CompileInfoStart+10, 17), 244: ?line verify(invalid_chunk, beam_lib:chunks(BF9, [compile_info])). 245: 246: 247: cmp(suite) -> []; 248: cmp(doc) -> ["Compare contents of BEAM files and directories"]; 249: cmp(Conf) when is_list(Conf) -> 250: ?line PrivDir = ?privdir, 251: 252: ?line Dir1 = filename:join(PrivDir, "dir1"), 253: ?line Dir2 = filename:join(PrivDir, "dir2"), 254: 255: ok = file:make_dir(Dir1), 256: ok = file:make_dir(Dir2), 257: 258: ?line {SourceD1, BeamFileD1} = make_beam(Dir1, simple, member), 259: ?line {Source2D1, BeamFile2D1} = make_beam(Dir1, simple2, concat), 260: ?line {SourceD2, BeamFileD2} = make_beam(Dir2, simple, concat), 261: 262: ?line NoOfTables = length(ets:all()), 263: ?line P0 = pps(), 264: 265: %% cmp 266: ?line ok = beam_lib:cmp(BeamFileD1, BeamFileD1), 267: ?line ver(modules_different, beam_lib:cmp(BeamFileD1, BeamFile2D1)), 268: ?line ver(chunks_different, beam_lib:cmp(BeamFileD1, BeamFileD2)), 269: ?line verify(file_error, beam_lib:cmp(foo, bar)), 270: 271: ?line {ok, B1} = file:read_file(BeamFileD1), 272: ?line ok = beam_lib:cmp(B1, BeamFileD1), 273: ?line {ok, B2} = file:read_file(BeamFileD2), 274: ?line ver(chunks_different, beam_lib:cmp(B1, B2)), 275: 276: %% cmp_dirs 277: ?line {[],[],[]} = beam_lib:cmp_dirs(Dir1, Dir1), 278: ?line true = {[BeamFile2D1], [], [{BeamFileD1,BeamFileD2}]} == 279: beam_lib:cmp_dirs(Dir1, Dir2), 280: ?line true = {[], [BeamFile2D1], [{BeamFileD2,BeamFileD1}]} == 281: beam_lib:cmp_dirs(Dir2, Dir1), 282: ?line ver(not_a_directory, beam_lib:cmp_dirs(foo, bar)), 283: 284: %% diff_dirs 285: ?line ok = beam_lib:diff_dirs(Dir1, Dir1), 286: ?line ver(not_a_directory, beam_lib:diff_dirs(foo, bar)), 287: 288: ?line true = (P0 == pps()), 289: ?line NoOfTables = length(ets:all()), 290: ?line delete_files([SourceD1, BeamFileD1, Source2D1, 291: BeamFile2D1, SourceD2, BeamFileD2]), 292: 293: file:del_dir(Dir1), 294: file:del_dir(Dir2), 295: ok. 296: 297: cmp_literals(suite) -> []; 298: cmp_literals(doc) -> ["Compare contents of BEAM files having literals"]; 299: cmp_literals(Conf) when is_list(Conf) -> 300: ?line PrivDir = ?privdir, 301: 302: ?line Dir1 = filename:join(PrivDir, "dir1"), 303: ?line Dir2 = filename:join(PrivDir, "dir2"), 304: 305: ok = file:make_dir(Dir1), 306: ok = file:make_dir(Dir2), 307: 308: ?line {SourceD1, BeamFileD1} = make_beam(Dir1, simple, constant), 309: ?line {SourceD2, BeamFileD2} = make_beam(Dir2, simple, constant2), 310: 311: ?line NoOfTables = length(ets:all()), 312: ?line P0 = pps(), 313: 314: %% cmp 315: ?line ok = beam_lib:cmp(BeamFileD1, BeamFileD1), 316: ?line ver(chunks_different, beam_lib:cmp(BeamFileD1, BeamFileD2)), 317: 318: ?line {ok, B1} = file:read_file(BeamFileD1), 319: ?line ok = beam_lib:cmp(B1, BeamFileD1), 320: ?line {ok, B2} = file:read_file(BeamFileD2), 321: ?line ver(chunks_different, beam_lib:cmp(B1, B2)), 322: 323: ?line true = (P0 == pps()), 324: ?line NoOfTables = length(ets:all()), 325: 326: ?line delete_files([SourceD1, BeamFileD1, SourceD2, BeamFileD2]), 327: 328: file:del_dir(Dir1), 329: file:del_dir(Dir2), 330: ok. 331: 332: strip(suite) -> []; 333: strip(doc) -> ["Strip BEAM files"]; 334: strip(Conf) when is_list(Conf) -> 335: ?line PrivDir = ?privdir, 336: ?line {SourceD1, BeamFileD1} = make_beam(PrivDir, simple, member), 337: ?line {Source2D1, BeamFile2D1} = make_beam(PrivDir, simple2, concat), 338: ?line {Source3D1, BeamFile3D1} = make_beam(PrivDir, make_fun, make_fun), 339: ?line {Source4D1, BeamFile4D1} = make_beam(PrivDir, constant, constant), 340: ?line {Source5D1, BeamFile5D1} = make_beam(PrivDir, lines, lines), 341: 342: ?line NoOfTables = length(ets:all()), 343: ?line P0 = pps(), 344: 345: %% strip binary 346: ?line verify(not_a_beam_file, beam_lib:strip(<<>>)), 347: ?line {ok, B1} = file:read_file(BeamFileD1), 348: ?line {ok, {simple, NB1}} = beam_lib:strip(B1), 349: ?line BId1 = chunk_ids(B1), 350: ?line NBId1 = chunk_ids(NB1), 351: ?line true = length(BId1) > length(NBId1), 352: ?line compare_chunks(B1, NB1, NBId1), 353: 354: %% strip file 355: ?line verify(file_error, beam_lib:strip(foo)), 356: ?line {ok, {simple, _}} = beam_lib:strip(BeamFileD1), 357: ?line compare_chunks(NB1, BeamFileD1, NBId1), 358: 359: %% strip_files 360: ?line {ok, B2} = file:read_file(BeamFile2D1), 361: ?line {ok, [{simple,_},{simple2,_}]} = beam_lib:strip_files([B1, B2]), 362: ?line {ok, [{simple,_},{simple2,_},{make_fun,_},{constant,_}]} = 363: beam_lib:strip_files([BeamFileD1, BeamFile2D1, BeamFile3D1, BeamFile4D1]), 364: 365: %% check that each module can be loaded. 366: ?line {module, simple} = code:load_abs(filename:rootname(BeamFileD1)), 367: ?line {module, simple2} = code:load_abs(filename:rootname(BeamFile2D1)), 368: ?line {module, make_fun} = code:load_abs(filename:rootname(BeamFile3D1)), 369: ?line {module, constant} = code:load_abs(filename:rootname(BeamFile4D1)), 370: 371: %% check that line number information is still present after stripping 372: ?line {module, lines} = code:load_abs(filename:rootname(BeamFile5D1)), 373: ?line {'EXIT',{badarith,[{lines,t,1,Info}|_]}} = 374: (catch lines:t(atom)), 375: ?line true = code:delete(lines), 376: ?line false = code:purge(lines), 377: ?line {ok, {lines,BeamFile5D1}} = beam_lib:strip(BeamFile5D1), 378: ?line {module, lines} = code:load_abs(filename:rootname(BeamFile5D1)), 379: ?line {'EXIT',{badarith,[{lines,t,1,Info}|_]}} = 380: (catch lines:t(atom)), 381: 382: ?line true = (P0 == pps()), 383: ?line NoOfTables = length(ets:all()), 384: 385: ?line delete_files([SourceD1, BeamFileD1, 386: Source2D1, BeamFile2D1, 387: Source3D1, BeamFile3D1, 388: Source4D1, BeamFile4D1, 389: Source5D1, BeamFile5D1]), 390: ok. 391: 392: 393: otp_6711(Conf) when is_list(Conf) -> 394: ?line {'EXIT',{function_clause,_}} = (catch {a, beam_lib:info(3)}), 395: ?line {'EXIT',{function_clause,_}} = (catch {a, beam_lib:chunks(a, b)}), 396: ?line {'EXIT',{function_clause,_}} = (catch {a, beam_lib:chunks(a,b,c)}), 397: ?line {'EXIT',{function_clause,_}} = (catch {a, beam_lib:all_chunks(3)}), 398: ?line {'EXIT',{function_clause,_}} = (catch {a, beam_lib:cmp(3,4)}), 399: ?line {'EXIT',{function_clause,_}} = (catch {a, beam_lib:strip(3)}), 400: ?line {'EXIT',{function_clause,_}} = 401: (catch {a, beam_lib:strip_files([3])}), 402: 403: ?line PrivDir = ?privdir, 404: ?line Dir = filename:join(PrivDir, "dir"), 405: ?line Lib = filename:join(Dir, "lib"), 406: ?line App = filename:join(Lib, "app"), 407: ?line EBin = filename:join(App, "ebin"), 408: 409: ok = file:make_dir(Dir), 410: ok = file:make_dir(Lib), 411: ok = file:make_dir(App), 412: ok = file:make_dir(EBin), 413: 414: ?line {SourceD, BeamFileD} = make_beam(EBin, simple, member), 415: 416: unwritable(BeamFileD), 417: 418: %% There is no way that strip_release can fail with 419: %% function_clause or something like that... 420: ?line {error,_,{file_error,_,_}} = beam_lib:strip_release(Dir), 421: 422: ?line delete_files([SourceD, BeamFileD]), 423: file:del_dir(EBin), 424: file:del_dir(App), 425: file:del_dir(Lib), 426: file:del_dir(Dir), 427: ok. 428: 429: -include_lib("kernel/include/file.hrl"). 430: 431: unwritable(Fname) -> 432: {ok, Info} = file:read_file_info(Fname), 433: Mode = Info#file_info.mode - 8#00200, 434: file:write_file_info(Fname, Info#file_info{mode = Mode}). 435: 436: building(doc) -> "Testing building of BEAM files."; 437: building(Conf) when is_list(Conf) -> 438: ?line PrivDir = ?privdir, 439: 440: ?line Dir1 = filename:join(PrivDir, "b_dir1"), 441: ?line Dir2 = filename:join(PrivDir, "b_dir2"), 442: 443: ok = file:make_dir(Dir1), 444: ok = file:make_dir(Dir2), 445: 446: ?line {SourceD1, BeamFileD1} = make_beam(Dir1, building, member), 447: 448: ?line NoOfTables = length(ets:all()), 449: ?line P0 = pps(), 450: 451: %% read all chunks 452: ?line ChunkIds = chunk_ids(BeamFileD1), 453: ?line {ok, _Mod, Chunks} = beam_lib:all_chunks(BeamFileD1), 454: ?line ChunkIds = lists:map(fun ({Id, Data}) when is_binary(Data) -> Id 455: end, Chunks), 456: 457: %% write a new beam file, with reversed chunk order 458: ?line BeamFileD2 = filename:join(Dir2, "building.beam"), 459: ?line {ok,RevBeam} = beam_lib:build_module(lists:reverse(Chunks)), 460: ?line file:write_file(BeamFileD2, RevBeam), 461: 462: %% compare files 463: ?line compare_chunks(BeamFileD1, BeamFileD2, ChunkIds), 464: 465: %% test that we can retrieve a chunk before the atom table 466: %% (actually, try to retrieve all chunks) 467: 468: ?line lists:foreach(fun(Id) -> 469: {ok, {building, [{Id, _Data}]}} = 470: beam_lib:chunks(BeamFileD1, [Id]) 471: end, ChunkIds), 472: ?line lists:foreach(fun(Id) -> 473: {ok, {building, [{Id, _Data}]}} = 474: beam_lib:chunks(BeamFileD2, [Id]) 475: end, ChunkIds), 476: 477: ?line true = (P0 == pps()), 478: ?line NoOfTables = length(ets:all()), 479: 480: ?line delete_files([SourceD1, BeamFileD1, BeamFileD2]), 481: file:del_dir(Dir1), 482: file:del_dir(Dir2), 483: ok. 484: 485: md5(suite) -> []; 486: md5(doc) -> ["Compare beam_lib:md5/1 and code:module_md5/1."]; 487: md5(Conf) when is_list(Conf) -> 488: ?line Beams = collect_beams(), 489: io:format("Found ~w beam files", [length(Beams)]), 490: md5_1(Beams). 491: 492: md5_1([N|Ns]) -> 493: {ok,Beam0} = file:read_file(N), 494: Beam = maybe_uncompress(Beam0), 495: {ok,{Mod,MD5}} = beam_lib:md5(Beam), 496: {Mod,MD5} = {Mod,code:module_md5(Beam)}, 497: md5_1(Ns); 498: md5_1([]) -> ok. 499: 500: collect_beams() -> 501: SuperDir = filename:dirname(filename:dirname(code:which(?MODULE))), 502: TestDirs = filelib:wildcard(filename:join([SuperDir,"*_test"])), 503: AbsDirs = [filename:absname(X) || X <- code:get_path()], 504: collect_beams_1(AbsDirs ++ TestDirs). 505: 506: collect_beams_1([Dir|Dirs]) -> 507: filelib:wildcard(filename:join(Dir, "*.beam")) ++ collect_beams_1(Dirs); 508: collect_beams_1([]) -> []. 509: 510: maybe_uncompress(<<"FOR1",_/binary>>=Beam) -> Beam; 511: maybe_uncompress(Beam) -> zlib:gunzip(Beam). 512: 513: encrypted_abstr(suite) -> []; 514: encrypted_abstr(doc) -> ["Test encrypted abstract format"]; 515: encrypted_abstr(Conf) when is_list(Conf) -> 516: run_if_crypto_works(fun() -> encrypted_abstr_1(Conf) end). 517: 518: encrypted_abstr_1(Conf) -> 519: ?line PrivDir = ?privdir, 520: ?line Simple = filename:join(PrivDir, "simple"), 521: ?line Source = Simple ++ ".erl", 522: ?line BeamFile = Simple ++ ".beam", 523: ?line simple_file(Source), 524: 525: %% Avoid getting an extra port when crypto starts erl_ddll. 526: ?line erl_ddll:start(), 527: 528: ?line NoOfTables = length(ets:all()), 529: ?line P0 = pps(), 530: 531: Key = "#a_crypto_key", 532: CompileFlags = [{outdir,PrivDir}, debug_info, {debug_info_key,Key}], 533: ?line {ok,_} = compile:file(Source, CompileFlags), 534: ?line {ok, Binary} = file:read_file(BeamFile), 535: 536: ?line do_encrypted_abstr(BeamFile, Key), 537: ?line do_encrypted_abstr(Binary, Key), 538: 539: ?line ok = crypto:stop(), %To get rid of extra ets tables. 540: ?line file:delete(BeamFile), 541: ?line file:delete(Source), 542: ?line NoOfTables = length(ets:all()), 543: ?line true = (P0 == pps()), 544: ok. 545: 546: do_encrypted_abstr(Beam, Key) -> 547: ?line verify(key_missing_or_invalid, beam_lib:chunks(Beam, [abstract_code])), 548: 549: %% The raw chunk "Abst" can still be read even without a key. 550: ?line {ok,{simple,[{"Abst",Abst}]}} = beam_lib:chunks(Beam, ["Abst"]), 551: ?line <<0:8,8:8,"des3_cbc",_/binary>> = Abst, 552: 553: %% Try som invalid funs. 554: ?line bad_fun(badfun, fun() -> ok end), 555: ?line bad_fun(badfun, {a,b}), 556: ?line bad_fun(blurf), 557: ?line {function_clause,_} = bad_fun(fun(glurf) -> ok end), 558: 559: %% Funs that return something strange. 560: ?line bad_fun(badfun, fun(init) -> {ok,fun() -> ok end} end), 561: ?line glurf = bad_fun(fun(init) -> {error,glurf} end), 562: 563: %% Try clearing (non-existing fun). 564: ?line undefined = beam_lib:clear_crypto_key_fun(), 565: 566: %% Install a fun which cannot retrieve a key. 567: ?line ok = beam_lib:crypto_key_fun(fun(init) -> ok end), 568: ?line {error,beam_lib,Error} = beam_lib:chunks(Beam, [abstract_code]), 569: 570: %% Install a fun which returns an incorrect key. 571: ?line {ok,_} = beam_lib:clear_crypto_key_fun(), 572: ?line ok = beam_lib:crypto_key_fun(simple_crypto_fun("wrong key...")), 573: ?line {error,beam_lib,Error} = beam_lib:chunks(Beam, [abstract_code]), 574: 575: %% Installing a new key fun is not possible without clearing the old. 576: ?line verify(exists, beam_lib:crypto_key_fun(simple_crypto_fun(Key))), 577: 578: %% Install the simplest possible working key fun. 579: ?line {ok,_} = beam_lib:clear_crypto_key_fun(), 580: ?line ok = beam_lib:crypto_key_fun(simple_crypto_fun(Key)), 581: ?line verify_abstract(Beam), 582: ?line {ok,{simple,[{"Abst",Abst}]}} = beam_lib:chunks(Beam, ["Abst"]), 583: 584: %% Installing a new key fun is not possible without clearing the old. 585: verify(exists, beam_lib:crypto_key_fun(ets_crypto_fun(Key))), 586: 587: %% Install a key using an ets table. 588: ?line {ok,_} = beam_lib:clear_crypto_key_fun(), 589: ?line ok = beam_lib:crypto_key_fun(ets_crypto_fun(Key)), 590: ?line verify_abstract(Beam), 591: ?line {ok,{simple,[{"Abst",Abst}]}} = beam_lib:chunks(Beam, ["Abst"]), 592: 593: ?line {ok,cleared} = beam_lib:clear_crypto_key_fun(), 594: 595: %% Try to force a stop/start race. 596: ?line start_stop_race(10000), 597: 598: ok. 599: 600: start_stop_race(0) -> 601: ok; 602: start_stop_race(N) -> 603: {error,_} = beam_lib:crypto_key_fun(bad_fun), 604: undefined = beam_lib:clear_crypto_key_fun(), 605: start_stop_race(N-1). 606: 607: bad_fun(F) -> 608: {error,E} = beam_lib:crypto_key_fun(F), 609: E. 610: 611: bad_fun(S, F) -> 612: verify(S, beam_lib:crypto_key_fun(F)). 613: 614: 615: verify_abstract(Beam) -> 616: {ok,{simple,[Chunk]}} = beam_lib:chunks(Beam, [abstract_code]), 617: {abstract_code,{raw_abstract_v1,_}} = Chunk. 618: 619: simple_crypto_fun(Key) -> 620: fun(init) -> ok; 621: ({debug_info, des3_cbc, simple, _}) -> Key 622: end. 623: 624: ets_crypto_fun(Key) -> 625: fun(init) -> 626: T = ets:new(beam_lib_SUITE_keys, [private, set]), 627: true = ets:insert(T, {key,Key}), 628: {ok,fun({debug_info, des3_cbc, simple, _}) -> 629: [{key,Val}] = ets:lookup(T, key), 630: Val; 631: (clear) -> 632: ets:delete(T), 633: cleared 634: end} 635: end. 636: 637: encrypted_abstr_file(suite) -> []; 638: encrypted_abstr_file(doc) -> 639: ["Test encrypted abstract format with the key in .erlang.crypt"]; 640: encrypted_abstr_file(Conf) when is_list(Conf) -> 641: run_if_crypto_works(fun() -> encrypted_abstr_file_1(Conf) end). 642: 643: encrypted_abstr_file_1(Conf) -> 644: ?line PrivDir = ?privdir, 645: ?line Simple = filename:join(PrivDir, "simple"), 646: ?line Source = Simple ++ ".erl", 647: ?line BeamFile = Simple ++ ".beam", 648: ?line simple_file(Source), 649: 650: %% Avoid getting an extra port when crypto starts erl_ddll. 651: ?line erl_ddll:start(), 652: 653: ?line NoOfTables = length(ets:all()), 654: ?line P0 = pps(), 655: 656: Key = "Long And niCe 99Krypto Key", 657: CompileFlags = [{outdir,PrivDir}, debug_info, {debug_info_key,Key}], 658: ?line {ok,_} = compile:file(Source, CompileFlags), 659: ?line {ok, Binary} = file:read_file(BeamFile), 660: 661: ?line {ok,OldCwd} = file:get_cwd(), 662: ?line ok = file:set_cwd(PrivDir), 663: ?line do_encrypted_abstr_file(BeamFile, Key), 664: ?line do_encrypted_abstr_file(Binary, Key), 665: ?line ok = file:set_cwd(OldCwd), 666: 667: ?line ok = crypto:stop(), %To get rid of extra ets tables. 668: ?line file:delete(filename:join(PrivDir, ".erlang.crypt")), 669: ?line file:delete(BeamFile), 670: ?line file:delete(Source), 671: ?line NoOfTables = length(ets:all()), 672: ?line true = (P0 == pps()), 673: ok. 674: 675: do_encrypted_abstr_file(Beam, Key) -> 676: %% No key. 677: ?line write_crypt_file(""), 678: ?line {error,beam_lib,Error} = beam_lib:chunks(Beam, [abstract_code]), 679: 680: %% A wrong key. 681: ?line write_crypt_file(["[{debug_info,des3_cbc,simple,\"A Wrong Key\"}].\n"]), 682: ?line {error,beam_lib,Error} = beam_lib:chunks(Beam, [abstract_code]), 683: 684: %% Write correct key... 685: ?line write_crypt_file(["[{debug_info,des3_cbc,simple,\"",Key,"\"}].\n"]), 686: 687: %% ... but the fun with the wrong key is still there. 688: ?line {error,beam_lib,Error} = beam_lib:chunks(Beam, [abstract_code]), 689: 690: %% Clear the fun. Now it should work. 691: ?line {ok,_} = beam_lib:clear_crypto_key_fun(), 692: ?line verify_abstract(Beam), 693: ?line verify_abstract(Beam), 694: ?line ok = file:delete(".erlang.crypt"), 695: ?line verify_abstract(Beam), 696: 697: %% Clear, otherwise the second pass will fail. 698: ?line {ok,_} = beam_lib:clear_crypto_key_fun(), 699: ?line {error,beam_lib,Error} = beam_lib:chunks(Beam, [abstract_code]), 700: ok. 701: 702: write_crypt_file(Contents0) -> 703: Contents = list_to_binary([Contents0]), 704: io:format("~s\n", [binary_to_list(Contents)]), 705: ok = file:write_file(".erlang.crypt", Contents). 706: 707: compare_chunks(File1, File2, ChunkIds) -> 708: ?line {ok, {_, Chunks1}} = beam_lib:chunks(File1, ChunkIds), 709: ?line {ok, {_, Chunks2}} = beam_lib:chunks(File2, ChunkIds), 710: ?line true = Chunks1 == Chunks2. 711: 712: chunk_ids(File) -> 713: ?line lists:map(fun({Id,_Start,_Size}) -> Id end, chunk_info(File)). 714: 715: chunk_info(File) -> 716: ?line {value, {chunks, Chunks}} = 717: lists:keysearch(chunks, 1, beam_lib:info(File)), 718: Chunks. 719: 720: make_beam(Dir, Module, F) -> 721: ?line FileBase = filename:join(Dir, atom_to_list(Module)), 722: ?line Source = FileBase ++ ".erl", 723: ?line BeamFile = FileBase ++ ".beam", 724: ?line simple_file(Source, Module, F), 725: ?line {ok, _} = compile:file(Source, [{outdir,Dir}, debug_info, report]), 726: {Source, BeamFile}. 727: 728: set_byte(_Backup, Binary, Pos, Byte) when is_binary(Binary) -> 729: ?line <<B1:Pos/binary, _:1/binary, B2/binary>> = Binary, 730: NB = <<B1/binary, Byte:8, B2/binary>>, 731: NB; 732: set_byte(Backup, File, Pos, Byte) -> 733: ?line copy_file(Backup, File), 734: ?line set_byte(File, Pos, Byte), 735: File. 736: 737: set_byte(File, Pos, Byte) -> 738: ?line {ok, Fd} = file:open(File, [read, write]), 739: ?line {ok, _} = file:position(Fd, Pos), 740: ?line ok = file:write(Fd, [Byte]), 741: ?line file:close(Fd). 742: 743: copy_file(Src, Dest) -> 744: % ?t:format("copying from ~p to ~p~n", [Src, Dest]), 745: ?line {ok, _} = file:copy(Src, Dest), 746: ?line ok = file:change_mode(Dest, 8#0666). 747: 748: delete_files(Files) -> 749: lists:foreach(fun(F) -> file:delete(F) end, Files). 750: 751: verify(S, {error, beam_lib, R}) -> 752: verify_error(S, R); 753: verify(S, {error, R}) -> 754: verify_error(S, R). 755: 756: verify_error(S, R) -> 757: if 758: S =:= R -> ok; 759: true -> [S|_] = tuple_to_list(R) 760: end, 761: 762: %% Most formatted messages begin with "./simple.beam:" or "<<...". 763: FM = string:str(lists:flatten(beam_lib:format_error(R)), "simpl") > 0, 764: BM = string:str(lists:flatten(beam_lib:format_error(R)), "<<") > 0, 765: 766: %% Also make sure that formatted message is not just the term printed. 767: Handled = beam_lib:format_error(R) =/= io_lib:format("~p~n", [R]), 768: true = ((FM > 0) or (BM > 0)) and Handled. 769: 770: ver(S, {error, beam_lib, R}) -> 771: [S|_] = tuple_to_list(R), 772: case lists:flatten(beam_lib:format_error(R)) of 773: [${ | _] -> 774: test_server:fail({bad_format_error, R}); 775: _ -> 776: ok 777: end. 778: 779: pps() -> 780: {erlang:ports()}. 781: 782: simple_file(File) -> 783: simple_file(File, simple). 784: 785: simple_file(File, Module) -> 786: simple_file(File, Module, member). 787: 788: simple_file(File, Module, make_fun) -> 789: B = list_to_binary(["-module(", atom_to_list(Module), "). " 790: "-export([t/1]). " 791: "t(A) -> " 792: " fun(X) -> A+X end. "]), 793: ok = file:write_file(File, B); 794: simple_file(File, Module, constant) -> 795: B = list_to_binary(["-module(", atom_to_list(Module), "). " 796: "-export([t/1]). " 797: "t(A) -> " 798: " {a,b,[2,3],c,d}. "]), 799: ok = file:write_file(File, B); 800: simple_file(File, Module, constant2) -> 801: B = list_to_binary(["-module(", atom_to_list(Module), "). " 802: "-export([t/1]). " 803: "t(A) -> " 804: " {a,b,[2,3],x,y}. "]), 805: ok = file:write_file(File, B); 806: simple_file(File, Module, lines) -> 807: B = list_to_binary(["-module(", atom_to_list(Module), ").\n" 808: "-export([t/1]).\n" 809: "t(A) ->\n" 810: " A+1.\n"]), 811: ok = file:write_file(File, B); 812: simple_file(File, Module, F) -> 813: B = list_to_binary(["-module(", atom_to_list(Module), "). " 814: "-export([t/0]). " 815: "t() -> " 816: " t([]). " 817: "t(L) -> " 818: " lists:", 819: atom_to_list(F), "(a, L). "]), 820: ok = file:write_file(File, B). 821: 822: run_if_crypto_works(Test) -> 823: try begin crypto:start(), crypto:info(), crypto:stop(), ok end of 824: ok -> 825: Test() 826: catch 827: error:_ -> 828: {skip,"The crypto application is missing or broken"} 829: end. 830: