.TH beam_lib 3erl "stdlib 5.2.2" "Ericsson AB" "Erlang Module Definition" .SH NAME beam_lib \- An interface to the BEAM file format. .SH DESCRIPTION .LP This module provides an interface to files created by the BEAM Compiler ("BEAM files")\&. The format used, a variant of "EA IFF 1985" Standard for Interchange Format Files, divides data into chunks\&. .LP Chunk data can be returned as binaries or as compound terms\&. Compound terms are returned when chunks are referenced by names (atoms) rather than identifiers (strings)\&. The recognized names and the corresponding identifiers are as follows: .RS 2 .TP 2 * \fIatoms ("Atom")\fR\& .LP .TP 2 * \fIattributes ("Attr")\fR\& .LP .TP 2 * \fIcompile_info ("CInf")\fR\& .LP .TP 2 * \fIdebug_info ("Dbgi")\fR\& .LP .TP 2 * \fIexports ("ExpT")\fR\& .LP .TP 2 * \fIimports ("ImpT")\fR\& .LP .TP 2 * \fIindexed_imports ("ImpT")\fR\& .LP .TP 2 * \fIlabeled_exports ("ExpT")\fR\& .LP .TP 2 * \fIlabeled_locals ("LocT")\fR\& .LP .TP 2 * \fIlocals ("LocT")\fR\& .LP .RE .SH "DEBUG INFORMATION/ABSTRACT CODE" .LP Option \fIdebug_info\fR\& can be specified to the Compiler (see \fIcompile(3erl)\fR\&) to have debug information, such as Erlang Abstract Format, stored in the \fIdebug_info\fR\& chunk\&. Tools such as Debugger and Xref require the debug information to be included\&. .LP .RS -4 .B Warning: .RE Source code can be reconstructed from the debug information\&. To prevent this, use encrypted debug information (see below)\&. .LP The debug information can also be removed from BEAM files using \fIstrip/1\fR\&, \fIstrip_files/1\fR\&, and/or \fIstrip_release/1\fR\&\&. .SH "RECONSTRUCT SOURCE CODE" .LP The following example shows how to reconstruct Erlang source code from the debug information in a BEAM file \fIBeam\fR\&: .LP .nf {ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(Beam,[abstract_code]). io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]). .fi .SH "ENCRYPTED DEBUG INFORMATION" .LP The debug information can be encrypted to keep the source code secret, but still be able to use tools such as Debugger or Xref\&. .LP To use encrypted debug information, a key must be provided to the compiler and \fIbeam_lib\fR\&\&. The key is specified as a string\&. It is recommended that the string contains at least 32 characters and that both upper and lower case letters as well as digits and special characters are used\&. .LP The default type (and currently the only type) of crypto algorithm is \fIdes3_cbc\fR\&, three rounds of DES\&. The key string is scrambled using \fIerlang:md5/1\fR\& to generate the keys used for \fIdes3_cbc\fR\&\&. .LP .RS -4 .B Note: .RE As far as we know by the time of writing, it is infeasible to break \fIdes3_cbc\fR\& encryption without any knowledge of the key\&. Therefore, as long as the key is kept safe and is unguessable, the encrypted debug information \fIshould\fR\& be safe from intruders\&. .LP The key can be provided in the following two ways: .RS 2 .TP 2 * Use Compiler option \fI{debug_info_key,Key}\fR\&, see \fIcompile(3erl)\fR\& and function \fIcrypto_key_fun/1\fR\& to register a fun that returns the key whenever \fIbeam_lib\fR\& must decrypt the debug information\&. .RS 2 .LP If no such fun is registered, \fIbeam_lib\fR\& instead searches for an \fI\&.erlang\&.crypt\fR\& file, see the next section\&. .RE .LP .TP 2 * Store the key in a text file named \fI\&.erlang\&.crypt\fR\&\&. .RS 2 .LP In this case, Compiler option \fIencrypt_debug_info\fR\& can be used, see \fIcompile(3erl)\fR\&\&. .RE .LP .RE .SH ".ERLANG.CRYPT" .LP \fIbeam_lib\fR\& searches for \fI\&.erlang\&.crypt\fR\& in the current directory, then the user\&'s home directory and then \fIfilename:basedir(user_config, "erlang")\fR\&\&. If the file is found and contains a key, \fIbeam_lib\fR\& implicitly creates a crypto key fun and registers it\&. .LP File \fI\&.erlang\&.crypt\fR\& is to contain a single list of tuples: .LP .nf {debug_info, Mode, Module, Key} .fi .LP \fIMode\fR\& is the type of crypto algorithm; currently, the only allowed value is \fIdes3_cbc\fR\&\&. \fIModule\fR\& is either an atom, in which case \fIKey\fR\& is only used for the module \fIModule\fR\&, or \fI[]\fR\&, in which case \fIKey\fR\& is used for all modules\&. \fIKey\fR\& is the non-empty key string\&. .LP \fIKey\fR\& in the first tuple where both \fIMode\fR\& and \fIModule\fR\& match is used\&. .LP The following is an example of an \fI\&.erlang\&.crypt\fR\& file that returns the same key for all modules: .LP .nf [{debug_info, des3_cbc, [], "%>7}|pc/DM6Cga*68$Mw]L#&_Gejr]G^"}]. .fi .LP The following is a slightly more complicated example of an \fI\&.erlang\&.crypt\fR\& providing one key for module \fIt\fR\& and another key for all other modules: .LP .nf [{debug_info, des3_cbc, t, "My KEY"}, {debug_info, des3_cbc, [], "%>7}|pc/DM6Cga*68$Mw]L#&_Gejr]G^"}]. .fi .LP .RS -4 .B Note: .RE Do not use any of the keys in these examples\&. Use your own keys\&. .SH DATA TYPES .nf \fBbeam()\fR\& = file:filename() | binary() .br .fi .RS .LP Each of the functions described below accept either the filename (as a string) or a binary containing the BEAM module\&. .RE .nf \fBchunkdata()\fR\& = .br {chunkid(), dataB()} | .br {abstract_code, abst_code()} | .br {debug_info, debug_info()} | .br {attributes, [attrib_entry()]} | .br {compile_info, [compinfo_entry()]} | .br {exports, [{atom(), arity()}]} | .br {labeled_exports, [labeled_entry()]} | .br {imports, [mfa()]} | .br {indexed_imports, .br [{index(), module(), Function :: atom(), arity()}]} | .br {locals, [{atom(), arity()}]} | .br {labeled_locals, [labeled_entry()]} | .br {atoms, [{integer(), atom()}]} .br .fi .RS .LP The list of attributes is sorted on \fIAttribute\fR\& (in \fIattrib_entry()\fR\&) and each attribute name occurs once in the list\&. The attribute values occur in the same order as in the file\&. The lists of functions are also sorted\&. .RE .nf \fBchunkid()\fR\& = nonempty_string() .br .fi .RS .LP "Attr" | "CInf" | "Dbgi" | "ExpT" | "ImpT" | "LocT" | "AtU8" .RE .nf \fBdataB()\fR\& = binary() .br .fi .nf \fBdebug_info()\fR\& = .br {DbgiVersion :: atom(), Backend :: module(), Data :: term()} | .br no_debug_info .br .fi .RS .LP The format stored in the \fIdebug_info\fR\& chunk\&. To retrieve particular code representation from the backend, \fIBackend:debug_info(Format, Module, Data, Opts)\fR\& must be invoked\&. \fIFormat\fR\& is an atom, such as \fIerlang_v1\fR\& for the Erlang Abstract Format or \fIcore_v1\fR\& for Core Erlang\&. \fIModule\fR\& is the module represented by the beam file and \fIData\fR\& is the value stored in the debug info chunk\&. \fIOpts\fR\& is any list of values supported by the \fIBackend\fR\&\&. \fIBackend:debug_info/4\fR\& must return \fI{ok, Code}\fR\& or \fI{error, Term}\fR\&\&. .LP Developers must always invoke the \fIdebug_info/4\fR\& function and never rely on the \fIData\fR\& stored in the \fIdebug_info\fR\& chunk, as it is opaque and may change at any moment\&. \fIno_debug_info\fR\& means that chunk \fI"Dbgi"\fR\& is present, but empty\&. .RE .nf \fBabst_code()\fR\& = .br {AbstVersion :: atom(), forms()} | no_abstract_code .br .fi .RS .LP It is not checked that the forms conform to the abstract format indicated by \fIAbstVersion\fR\&\&. \fIno_abstract_code\fR\& means that chunk \fI"Abst"\fR\& is present, but empty\&. .LP For modules compiled with OTP 20 onwards, the \fIabst_code\fR\& chunk is automatically computed from the \fIdebug_info\fR\& chunk\&. .RE .nf \fBforms()\fR\& = [erl_parse:abstract_form() | erl_parse:form_info()] .br .fi .nf \fBcompinfo_entry()\fR\& = {InfoKey :: atom(), term()} .br .fi .nf \fBattrib_entry()\fR\& = .br {Attribute :: atom(), [AttributeValue :: term()]} .br .fi .nf \fBlabeled_entry()\fR\& = {Function :: atom(), arity(), label()} .br .fi .nf \fBindex()\fR\& = integer() >= 0 .br .fi .nf \fBlabel()\fR\& = integer() .br .fi .nf \fBchunkref()\fR\& = chunkname() | chunkid() .br .fi .nf \fBchunkname()\fR\& = .br abstract_code | debug_info | attributes | compile_info | .br exports | labeled_exports | imports | indexed_imports | .br locals | labeled_locals | atoms .br .fi .nf \fBchnk_rsn()\fR\& = .br {unknown_chunk, file:filename(), atom()} | .br {key_missing_or_invalid, .br file:filename(), .br abstract_code | debug_info} | .br {missing_backend, file:filename(), module()} | .br info_rsn() .br .fi .nf \fBinfo_rsn()\fR\& = .br {chunk_too_big, .br file:filename(), .br chunkid(), .br ChunkSize :: integer() >= 0, .br FileSize :: integer() >= 0} | .br {invalid_beam_file, .br file:filename(), .br Position :: integer() >= 0} | .br {invalid_chunk, file:filename(), chunkid()} | .br {missing_chunk, file:filename(), chunkid()} | .br {not_a_beam_file, file:filename()} | .br {file_error, file:filename(), file:posix()} .br .fi .SH EXPORTS .LP .nf .B all_chunks(File :: beam()) -> .B {ok, module(), [{chunkid(), dataB()}]} | .B {error, beam_lib, info_rsn()} .br .fi .br .RS .LP Reads chunk data for all chunks\&. .RE .LP .nf .B build_module(Chunks) -> {ok, Binary} .br .fi .br .RS .LP Types: .RS 3 Chunks = [{chunkid(), dataB()}] .br Binary = binary() .br .RE .RE .RS .LP Builds a BEAM module (as a binary) from a list of chunks\&. .RE .LP .nf .B chunks(Beam, ChunkRefs) -> .B {ok, {module(), [chunkdata()]}} | .B {error, beam_lib, chnk_rsn()} .br .fi .br .RS .LP Types: .RS 3 Beam = beam() .br ChunkRefs = [chunkref()] .br .RE .RE .RS .LP Reads chunk data for selected chunks references\&. The order of the returned list of chunk data is determined by the order of the list of chunks references\&. .RE .LP .nf .B chunks(Beam, ChunkRefs, Options) -> .B {ok, {module(), [ChunkResult]}} | .B {error, beam_lib, chnk_rsn()} .br .fi .br .RS .LP Types: .RS 3 Beam = beam() .br ChunkRefs = [chunkref()] .br Options = [allow_missing_chunks] .br ChunkResult = .br chunkdata() | {ChunkRef :: chunkref(), missing_chunk} .br .RE .RE .RS .LP Reads chunk data for selected chunks references\&. The order of the returned list of chunk data is determined by the order of the list of chunks references\&. .LP By default, if any requested chunk is missing in \fIBeam\fR\&, an \fIerror\fR\& tuple is returned\&. However, if option \fIallow_missing_chunks\fR\& is specified, a result is returned even if chunks are missing\&. In the result list, any missing chunks are represented as \fI{ChunkRef,missing_chunk}\fR\&\&. Notice however that if chunk \fI"Atom"\fR\& is missing, that is considered a fatal error and the return value is an \fIerror\fR\& tuple\&. .RE .LP .nf .B clear_crypto_key_fun() -> undefined | {ok, Result} .br .fi .br .RS .LP Types: .RS 3 Result = undefined | term() .br .RE .RE .RS .LP Unregisters the crypto key fun and terminates the process holding it, started by \fIcrypto_key_fun/1\fR\&\&. .LP Returns either \fI{ok, undefined}\fR\& if no crypto key fun is registered, or \fI{ok, Term}\fR\&, where \fITerm\fR\& is the return value from \fICryptoKeyFun(clear)\fR\&, see \fIcrypto_key_fun/1\fR\&\&. .RE .LP .nf .B cmp(Beam1, Beam2) -> ok | {error, beam_lib, cmp_rsn()} .br .fi .br .RS .LP Types: .RS 3 Beam1 = Beam2 = beam() .br .nf \fBcmp_rsn()\fR\& = .br {modules_different, module(), module()} | .br {chunks_different, chunkid()} | .br different_chunks | .br info_rsn() .fi .br .RE .RE .RS .LP Compares the contents of two BEAM files\&. If the module names are the same, and all chunks except for chunk \fI"CInf"\fR\& (the chunk containing the compilation information that is returned by \fIModule:module_info(compile)\fR\&) have the same contents in both files, \fIok\fR\& is returned\&. Otherwise an error message is returned\&. .RE .LP .nf .B cmp_dirs(Dir1, Dir2) -> .B {Only1, Only2, Different} | {error, beam_lib, Reason} .br .fi .br .RS .LP Types: .RS 3 Dir1 = Dir2 = atom() | file:filename() .br Only1 = Only2 = [file:filename()] .br Different = .br [{Filename1 :: file:filename(), Filename2 :: file:filename()}] .br Reason = {not_a_directory, term()} | info_rsn() .br .RE .RE .RS .LP Compares the BEAM files in two directories\&. Only files with extension \fI"\&.beam"\fR\& are compared\&. BEAM files that exist only in directory \fIDir1\fR\& (\fIDir2\fR\&) are returned in \fIOnly1\fR\& (\fIOnly2\fR\&)\&. BEAM files that exist in both directories but are considered different by \fIcmp/2\fR\& are returned as pairs {\fIFilename1\fR\&, \fIFilename2\fR\&}, where \fIFilename1\fR\& (\fIFilename2\fR\&) exists in directory \fIDir1\fR\& (\fIDir2\fR\&)\&. .RE .LP .nf .B crypto_key_fun(CryptoKeyFun) -> ok | {error, Reason} .br .fi .br .RS .LP Types: .RS 3 CryptoKeyFun = crypto_fun() .br Reason = badfun | exists | term() .br .nf \fBcrypto_fun()\fR\& = fun((crypto_fun_arg()) -> term()) .fi .br .nf \fBcrypto_fun_arg()\fR\& = .br init | clear | {debug_info, mode(), module(), file:filename()} .fi .br .nf \fBmode()\fR\& = des3_cbc .fi .br .RE .RE .RS .LP Registers an unary fun that is called if \fIbeam_lib\fR\& must read an \fIdebug_info\fR\& chunk that has been encrypted\&. The fun is held in a process that is started by the function\&. .LP If a fun is already registered when attempting to register a fun, \fI{error, exists}\fR\& is returned\&. .LP The fun must handle the following arguments: .LP .nf CryptoKeyFun(init) -> ok | {ok, NewCryptoKeyFun} | {error, Term} .fi .LP Called when the fun is registered, in the process that holds the fun\&. Here the crypto key fun can do any necessary initializations\&. If \fI{ok, NewCryptoKeyFun}\fR\& is returned, \fINewCryptoKeyFun\fR\& is registered instead of \fICryptoKeyFun\fR\&\&. If \fI{error, Term}\fR\& is returned, the registration is aborted and \fIcrypto_key_fun/1\fR\& also returns \fI{error, Term}\fR\&\&. .LP .nf CryptoKeyFun({debug_info, Mode, Module, Filename}) -> Key .fi .LP Called when the key is needed for module \fIModule\fR\& in the file named \fIFilename\fR\&\&. \fIMode\fR\& is the type of crypto algorithm; currently, the only possible value is \fIdes3_cbc\fR\&\&. The call is to fail (raise an exception) if no key is available\&. .LP .nf CryptoKeyFun(clear) -> term() .fi .LP Called before the fun is unregistered\&. Here any cleaning up can be done\&. The return value is not important, but is passed back to the caller of \fIclear_crypto_key_fun/0\fR\& as part of its return value\&. .RE .LP .nf .B diff_dirs(Dir1, Dir2) -> ok | {error, beam_lib, Reason} .br .fi .br .RS .LP Types: .RS 3 Dir1 = Dir2 = atom() | file:filename() .br Reason = {not_a_directory, term()} | info_rsn() .br .RE .RE .RS .LP Compares the BEAM files in two directories as \fIcmp_dirs/2\fR\&, but the names of files that exist in only one directory or are different are presented on standard output\&. .RE .LP .nf .B format_error(Reason) -> io_lib:chars() .br .fi .br .RS .LP Types: .RS 3 Reason = term() .br .RE .RE .RS .LP For a specified error returned by any function in this module, this function returns a descriptive string of the error in English\&. For file errors, function \fIfile:format_error(Posix)\fR\& is to be called\&. .RE .LP .nf .B info(Beam) -> [InfoPair] | {error, beam_lib, info_rsn()} .br .fi .br .RS .LP Types: .RS 3 Beam = beam() .br InfoPair = .br {file, Filename :: file:filename()} | .br {binary, Binary :: binary()} | .br {module, Module :: module()} | .br {chunks, .br [{ChunkId :: chunkid(), .br Pos :: integer() >= 0, .br Size :: integer() >= 0}]} .br .RE .RE .RS .LP Returns a list containing some information about a BEAM file as tuples \fI{Item, Info}\fR\&: .RS 2 .TP 2 .B \fI{file, Filename} | {binary, Binary}\fR\&: The name (string) of the BEAM file, or the binary from which the information was extracted\&. .TP 2 .B \fI{module, Module}\fR\&: The name (atom) of the module\&. .TP 2 .B \fI{chunks, [{ChunkId, Pos, Size}]}\fR\&: For each chunk, the identifier (string) and the position and size of the chunk data, in bytes\&. .RE .RE .LP .nf .B md5(Beam) -> {ok, {module(), MD5}} | {error, beam_lib, chnk_rsn()} .br .fi .br .RS .LP Types: .RS 3 Beam = beam() .br MD5 = binary() .br .RE .RE .RS .LP Calculates an MD5 redundancy check for the code of the module (compilation date and other attributes are not included)\&. .RE .LP .nf .B strip(Beam1) -> .B {ok, {module(), Beam2}} | {error, beam_lib, info_rsn()} .br .fi .br .RS .LP Types: .RS 3 Beam1 = Beam2 = beam() .br .RE .RE .RS .LP Removes all chunks from a BEAM file except those used by the loader\&. In particular, the debug information (chunk \fIdebug_info\fR\& and \fIabstract_code\fR\&) is removed\&. .RE .LP .nf .B strip(Beam1, AdditionalChunks) -> .B {ok, {module(), Beam2}} | {error, beam_lib, info_rsn()} .br .fi .br .RS .LP Types: .RS 3 Beam1 = beam() .br AdditionalChunks = [chunkid()] .br Beam2 = beam() .br .RE .RE .RS .LP Removes all chunks from a BEAM file except those used by the loader or mentioned in \fIAdditionalChunks\fR\&\&. In particular, the debug information (chunk \fIdebug_info\fR\& and \fIabstract_code\fR\&) is removed\&. .RE .LP .nf .B strip_files(Files) -> .B {ok, [{module(), Beam}]} | .B {error, beam_lib, info_rsn()} .br .fi .br .RS .LP Types: .RS 3 Files = [beam()] .br Beam = beam() .br .RE .RE .RS .LP Removes all chunks except those used by the loader from BEAM files\&. In particular, the debug information (chunk \fIdebug_info\fR\& and \fIabstract_code\fR\&) is removed\&. The returned list contains one element for each specified filename, in the same order as in \fIFiles\fR\&\&. .RE .LP .nf .B strip_files(Files, AdditionalChunks) -> .B {ok, [{module(), Beam}]} | .B {error, beam_lib, info_rsn()} .br .fi .br .RS .LP Types: .RS 3 Files = [beam()] .br AdditionalChunks = [chunkid()] .br Beam = beam() .br .RE .RE .RS .LP Removes all chunks except those used by the loader or mentioned in \fIAdditionalChunks\fR\&\&. In particular, the debug information (chunk \fIdebug_info\fR\& and \fIabstract_code\fR\&) is removed\&. The returned list contains one element for each specified filename, in the same order as in \fIFiles\fR\&\&. .RE .LP .nf .B strip_release(Dir) -> .B {ok, [{module(), file:filename()}]} | .B {error, beam_lib, Reason} .br .fi .br .RS .LP Types: .RS 3 Dir = atom() | file:filename() .br Reason = {not_a_directory, term()} | info_rsn() .br .RE .RE .RS .LP Removes all chunks except those used by the loader from the BEAM files of a release\&. \fIDir\fR\& is to be the installation root directory\&. For example, the current OTP release can be stripped with the call \fIbeam_lib:strip_release(code:root_dir())\fR\&\&. .RE .LP .nf .B strip_release(Dir, AdditionalChunks) -> .B {ok, [{module(), file:filename()}]} | .B {error, beam_lib, Reason} .br .fi .br .RS .LP Types: .RS 3 Dir = atom() | file:filename() .br AdditionalChunks = [chunkid()] .br Reason = {not_a_directory, term()} | info_rsn() .br .RE .RE .RS .LP Removes all chunks except those used by the loader or mentioned in \fIAdditionalChunks\fR\&\&. \fIDir\fR\& is to be the installation root directory\&. For example, the current OTP release can be stripped with the call \fIbeam_lib:strip_release(code:root_dir())\fR\&\&. .RE .LP .nf .B version(Beam) -> .B {ok, {module(), [Version :: term()]}} | .B {error, beam_lib, chnk_rsn()} .br .fi .br .RS .LP Types: .RS 3 Beam = beam() .br .RE .RE .RS .LP Returns the module version or versions\&. A version is defined by module attribute \fI-vsn(Vsn)\fR\&\&. If this attribute is not specified, the version defaults to the checksum of the module\&. Notice that if version \fIVsn\fR\& is not a list, it is made into one, that is \fI{ok,{Module,[Vsn]}}\fR\& is returned\&. If there are many \fI-vsn\fR\& module attributes, the result is the concatenated list of versions\&. .LP \fIExamples:\fR\& .LP .nf 1> beam_lib:version(a)\&. % -vsn(1). {ok,{a,[1]}} 2> beam_lib:version(b)\&. % -vsn([1]). {ok,{b,[1]}} 3> beam_lib:version(c)\&. % -vsn([1]). -vsn(2). {ok,{c,[1,2]}} 4> beam_lib:version(d)\&. % no -vsn attribute {ok,{d,[275613208176997377698094100858909383631]}} .fi .RE