在线文档教程
Erlang 20

beam_lib

beam_lib

模块

beam_lib

模块摘要

beam文件格式的接口。

描述

该模块为由BEAM编译器(“BEAM文件”)创建的文件提供接口。所使用的格式,“EA IFF 1985”交换格式文件标准的变体,将数据划分为块。

块数据可以作为二进制文件或复合词返回。当块由名称(原子)而不是标识符(字符串)引用时,将返回复合项。识别的名称和相应的标识符如下:

  • atoms ("Atom")

  • attributes ("Attr")

  • compile_info ("CInf")

  • debug_info ("Dbgi")

  • exports ("ExpT")

  • imports ("ImpT")

  • indexed_imports ("ImpT")

  • labeled_exports ("ExpT")

  • labeled_locals ("LocT")

  • locals ("LocT")

调试信息/摘要代码

选项debug_info可以指定给编译器(请参阅参考资料compile(3))以获取调试信息,例如Erlang Abstract Format存储在debug_info块中。诸如Debugger和Xref之类的工具需要包含调试信息。

警告

源代码可以从调试信息重建。为了防止这种情况,请使用加密的调试信息(请参见下文)

调试信息,也可以从文件的梁使用去除strip/1strip_files/1和/或strip_release/1

重构源代码

下面的示例演示如何从BEAM文件中的调试信息中重构Erlang源代码Beam*

{ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(Beam,[abstract_code]). io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).

加密调试信息

可以对调试信息进行加密,以保持源代码的机密性,但仍然可以使用诸如Debugger或Xref之类的工具。

若要使用加密的调试信息,必须向编译器和beam_lib键指定为字符串。建议该字符串至少包含32个字符,并使用大小写字母以及数字和特殊字符。

加密算法的默认类型(目前唯一的类型)是des3_cbc,三轮DES。 使用erlang:md5 / 1对密钥字符串进行加密,以生成用于des3_cbc的密钥。

就我们所知道的写作时间而言,在不知道des3_cbc密钥的情况下破解加密是不可行的。因此,只要密钥保持安全并且是不可猜测的,加密的调试信息对于入侵者应该是安全的。

密钥可以通过以下两种方式提供:

  • 使用Compiler选项{debug_info_key,Key},查看compile(3)并运行crypto_key_fun/1注册一个fun 函数,该函数在beam_lib解密调试信息时必须返回密钥。如果没有这样的乐趣注册,beam_lib而是搜索.erlang.crypt文件,请参阅下一节。

  • 将密钥存储在名为.erlang.crypt...

在这种情况下,encrypt_debug_info可以使用Compiler选项,请参阅compile(3)

.erlang.crypt

beam_lib在当前目录中搜索.erlang.crypt,然后搜索当前用户的主目录。 如果找到文件并包含密钥,则beam_lib会隐式创建加密密钥函数并注册它。

档案.erlang.crypt包含一个元组列表:

{debug_info, Mode, Module, Key}

Mode是加密算法的类型; 目前,唯一允许的值是des3_cbcModule是原子,在这种情况下Key仅用于模块Module,或者[]Key所有模块中都使用该原子。Key是非空密钥字符串。

Key在使用两者ModeModule匹配的第一个元组中。

下面是.erlang.crypt为所有模块返回相同密钥的文件:

[{debug_info, des3_cbc, [], "%>7}|pc/DM6Cga*68$Mw]L#&_Gejr]G^"}].

下面是一个稍微复杂一些的.erlang.crypt为模块提供一个密钥t所有其他模块的另一个关键是:

[{debug_info, des3_cbc, t, "My KEY"}, {debug_info, des3_cbc, [], "%>7}|pc/DM6Cga*68$Mw]L#&_Gejr]G^"}].

不要使用这些示例中的任何键。用你自己的键。

数据类型

beam() = module() | file:filename() | binary()

下面描述的每个函数都接受模块名、文件名或包含波束模块的二进制文件。

chunkdata() =

{chunkid(),dataB()} |

{abstract_code,abst_code()} |

{debug_info,debug_info()} |

{attributes, [attrib_entry()]} |

{compile_info, [compinfo_entry()]} |

{exports, [{atom(), arity()}]} |

{labeled_exports, [labeled_entry()]} |

{imports, [mfa()]} |

{indexed_imports,

[{index(), module(), Function :: atom(), arity()}]} |

{locals, [{atom(), arity()}]} |

{labeled_locals, [labeled_entry()]} |

{atoms, [{integer(), atom()}]}

属性列表按Attributeattrib_entry())排序,每个属性名称在列表中出现一次。属性值的出现顺序与文件中的相同。功能列表也被排序。

chunkid() = nonempty_string()

“ATTR”“CINF”“Dbgi”“ExpT”“Imt”“Loct”“AtU 8”

dataB() = binary()

debug_info() =

{DbgiVersion :: atom(), Backend :: module(), Data :: term()} |

no_debug_info

存储在debug_info块中的格式。 要从后端检索特定的代码表示,必须调用后端:debug_info(格式,模块,数据,选项)。 格式是一个原子,例如Erlang抽象格式的erlang_v1或Core Erlang的core_v1。 模块是由波束文件表示的模块,Data是存储在调试信息块中的值。 Opts是后端支持的任何值列表。 后端:debug_info / 4必须返回{ok,Code}或{error,Term}。

开发人员必须始终调用debug_info / 4函数,并且永远不要依赖存储在debug_info块中的数据,因为它是不透明的并可能在任何时候改变。 no_debug_info表示块“Dbgi”存在,但是为空。

abst_code() =

{AbstVersion :: atom(),forms()} | no_abstract_code

不检查表格是否符合由表示的抽象格式AbstVersionno_abstract_code意味着块"Abst"存在,但是空的。

对于使用OTP 20编译的模块,abst_code块将自动从debug_info块中计算出来。

forms() = [erl_parse:abstract_form()|erl_parse:form_info()]

compinfo_entry() = {InfoKey :: atom(), term()}

attrib_entry() =

{Attribute :: atom(), [AttributeValue :: term()]}

labeled_entry() = {Function :: atom(), arity(),label()}

index() = integer() >= 0

label() = integer()

chunkref() =chunkname()|chunkid()

chunkname() =

abstract_code |

debug_info |

attributes |

compile_info |

exports |

labeled_exports |

imports |

indexed_imports |

locals |

labeled_locals |

atoms

chnk_rsn() =

{unknown_chunk, file:filename(), atom()} |

{key_missing_or_invalid,

file:filename(),

abstract_code | debug_info} |

info_rsn()

info_rsn() =

{chunk_too_big,

file:filename(),

chunkid(),

ChunkSize :: integer() >= 0,

FileSize :: integer() >= 0} |

{invalid_beam_file,

file:filename(),

Position :: integer() >= 0} |

{invalid_chunk, file:filename(),chunkid()} |

{missing_chunk, file:filename(),chunkid()} |

{not_a_beam_file, file:filename()} |

{file_error, file:filename(), file:posix()}

输出

all_chunks(File ::)beam()- >

{ok,beam_lib,[{ chunkid()dataB()}]}

读取所有块的块数据。

build_module(块) - > {ok,Binary}

类型

从块列表构建BEAM模块(作为二进制文件)。

chunks(Beam,ChunkRefs) - >

{ok,{module(),[ chunkdata()]}}

{error,beam_lib,chnk_rsn()}

类型

读取所选块参考的块数据。返回的块数据列表的顺序由块参考列表的顺序决定。

chunks(Beam, ChunkRefs, Options) ->

{ok, {module(), ChunkResult}} |

{error, beam_lib, chnk_rsn()}

类型

读取选定块引用的块数据。块数据返回列表的顺序由块引用列表的顺序决定。

默认情况下,如果Beam中缺少任何请求的块,则返回错误元组。 但是,如果指定了选项allow_missing_chunks,则即使缺少块,也会返回结果。 在结果列表中,任何丢失的块都表示为{ChunkRef,missing_chunk}。 但是请注意,如果块“Atom”丢失,则认为这是致命错误,并且返回值是错误元组。

clear_crypto_key_fun() - > undefined | {ok,Result}

类型

取消注册加密密钥的乐趣并终止持有它的进程,由crypto_key_fun/1开始。

如果未注册加密密钥函数,则返回{ok,undefined}或{ok,Term},其中Term是来自CryptoKeyFun(clear)的返回值,请参阅crypto_key_fun / 1。

cmp(Beam1, Beam2) -> ok | {error, beam_lib, cmp_rsn()}

类型

比较两个BEAM文件的内容。如果模块名称相同,"CInf"则返回除chunk (包含由其返回的编译信息的块Module:module_info(compile))在这两个文件中具有相同内容的所有块ok。否则会返回错误消息。

cmp_dirs(Dir1, Dir2) ->

{Only1, Only2, Different} | {error, beam_lib, Reason}

类型

比较两个目录中的BEAM文件。 只有扩展名为“.beam”的文件才会被比较。 仅存在于目录Dir1(Dir2)中的BEAM文件将在Only1(Only2)中返回。 两个目录中存在但被cmp / 2视为不同的BEAM文件将作为对{Filename1,Filename2},其中Filename1(Filename2)存在于目录Dir1(Dir2)中返回。

crypto_key_fun(CryptoKeyFun) -> ok | {error, Reason}

类型

如果beam_lib必须读取debug_info已加密的块,则会注册所调用的一元乐趣。函数是在由函数启动的过程中进行的。

如果在尝试注册函数时已经注册了函数,{error, exists}会被归还。

函数必须处理以下参数:

CryptoKeyFun(init) -> ok | {ok, NewCryptoKeyFun} | {error, Term}

当函数被注册时被调用,在拥有函数的过程中。 这里的加密密钥函数可以做任何必要的初始化。 如果返回{ok,NewCryptoKeyFun},则注册NewCryptoKeyFun而不是CryptoKeyFun。 如果{error,Term}被返回,注册将被中止,并且crypto_key_fun / 1也会返回{error,Term}。

CryptoKeyFun{debug_info, Mode, Module, Filename}) -> Key

Module在名为的文件中的模块需要密钥时调用FilenameMode是加密算法的类型; 目前,唯一可能的价值是des3_cbc。如果没有密钥可用,该调用将失败(引发异常)。

CryptoKeyFun(clear) -> term()

在函数未注册之前调用。这里可以完成任何清理工作。返回值并不重要,但clear_crypto_key_fun/0作为其返回值的一部分传回给调用者。

diff_dirs(Dir1, Dir2) -> ok | {error, beam_lib, Reason}

类型

将两个目录中的束文件比较为cmp_dirs/2,但仅存在于一个目录中或不同的文件的名称在标准输出中显示。

format_error(Reason) -> io_lib:chars()

类型

对于此模块中任何函数返回的指定错误,此函数返回英文错误的描述性字符串。对于文件错误,函数file:format_error(Posix)将被调用。

info(Beam) -> InfoPair | {error, beam_lib, info_rsn()}

类型

返回一个列表,其中包含一些关于BEAM文件的信息{Item, Info}

{file, Filename} | {binary, Binary}

BEAM文件的名称(字符串)或从中提取信息的二进制文件。

{module, Module}

模块的名称(原子)。

{chunks, [{ChunkId, Pos, Size}]}

对于每个块,标识符(字符串)以及块数据的位置和大小(以字节为单位)。

md5(Beam) -> {ok, {module(), MD5}} | {error, beam_lib, chnk_rsn()}

类型

计算模块代码的MD5冗余检查(不包括编译日期和其他属性)。

strip(Beam1) ->

{ok, {module(), Beam2}} | {error, beam_lib, info_rsn()}

类型

从BEAM文件中除去加载器所需的所有块。特别是,调试信息(块debug_infoabstract_code)被删除。

strip_files(Files) ->

{ok, {module(), Beam}} |

{error, beam_lib, info_rsn()}

类型

从BEAM文件中除去加载器所需的所有块。 特别是调试信息(chunk debug_info和abstract_code)被删除。 返回的列表包含每个指定文件名的一个元素,其顺序与文件中的顺序相同。

strip_release(Dir) ->

{ok, {module(), file:filename()}} |

{error, beam_lib, Reason}

类型

从发行版的BEAM文件中除去加载器所需的所有块。Dir将成为安装根目录。例如,当前的OTP版本可以随调用beam_lib:strip_release(code:root_dir())一起被剥离。

version(Beam) ->

{ok, {module(), Version :: term()}} |

{error, beam_lib, chnk_rsn()}

类型

返回模块版本。 版本由模块属性-vsn(Vsn)定义。 如果未指定此属性,则版本默认为模块的校验和。 请注意,如果版本Vsn不是列表,则将它合并为一个,即{ok,{Module,[Vsn]}}被返回。 如果有许多-vsn模块属性,则结果是连接的版本列表。

例子:

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]}}