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/1
,strip_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_cbc
。Module
是原子,在这种情况下Key
仅用于模块Module
,或者[]
在Key
所有模块中都使用该原子。Key
是非空密钥字符串。
Key
在使用两者Mode
和Module
匹配的第一个元组中。
下面是.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()}]}
属性列表按Attribute
( attrib_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
不检查表格是否符合由表示的抽象格式AbstVersion
。no_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
在名为的文件中的模块需要密钥时调用Filename
。Mode
是加密算法的类型; 目前,唯一可能的价值是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_info
和abstract_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]}}