檢視原始碼 beam_lib (stdlib v6.2)
此模組提供了一個介面,用於與 BEAM 編譯器所建立的檔案("BEAM 檔案")進行互動。
所使用的格式是 "EA IFF 1985" 檔案交換標準格式的變體,將資料分成數個區塊 (chunk)。
區塊資料可以二進制資料或複合項 (compound term) 的形式返回。當區塊使用名稱(原子)而不是識別符號(字串)引用時,會返回複合項。可識別的名稱和對應的識別符號如下:
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")
documentation ("Docs")
除錯資訊/抽象程式碼
可以將 debug_info
選項指定給編譯器(請參閱 compile
),將除錯資訊(例如 Erlang 抽象格式)儲存在 debug_info
區塊中。諸如 Debugger 和 Xref 等工具需要包含除錯資訊。
警告
可以從除錯資訊重建原始碼。為了防止這種情況,請使用加密的除錯資訊(見下文)。
也可以使用 strip/1
、strip_files/1
和/或 strip_release/1
從 BEAM 檔案中移除除錯資訊。
重建原始碼
以下範例顯示如何從 BEAM 檔案 Beam
中的除錯資訊重建 Erlang 原始碼
{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
加密是不可行的。因此,只要金鑰保持安全且無法猜測,加密的除錯資訊應該對入侵者是安全的。
可以透過以下兩種方式提供金鑰:
- 使用編譯器選項
{debug_info_key,Key}
,請參閱compile
和函式crypto_key_fun/1
,以註冊一個 fun,該 fun 會在beam_lib
必須解密除錯資訊時返回金鑰。
如果未註冊此類 fun,則 beam_lib
會改為搜尋 .erlang.crypt
檔案,請參閱下一節。
- 將金鑰儲存在名為
.erlang.crypt
的文字檔案中。
在這種情況下,可以使用編譯器選項 encrypt_debug_info
,請參閱 compile
。
.erlang.crypt
beam_lib
會在目前目錄、然後是 使用者主目錄 以及 filename:basedir(user_config, "erlang")
中搜尋 .erlang.crypt
。如果找到該檔案且包含金鑰,beam_lib
會隱式建立一個加密金鑰 fun 並註冊它。
檔案 .erlang.crypt
應包含一個 tuple 的單一列表
{debug_info, Mode, Module, Key}
Mode
是加密演算法的類型;目前,唯一允許的值是 des3_cbc
。Module
是原子,在這種情況下,Key
僅用於模組 Module
,或是 []
,在這種情況下,Key
用於所有模組。Key
是非空白的金鑰字串。
使用第一個 Mode
和 Module
皆匹配的 tuple 中的 Key
。
以下是 .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^"}].
注意
請勿使用這些範例中的任何金鑰。請使用您自己的金鑰。
摘要
類型
不會檢查表單是否符合 AbstVersion
所指示的抽象格式。no_abstract_code
表示 "Abst"
區塊存在,但為空白。
下面描述的每個函式都接受檔案名稱(以字串形式)或包含 BEAM 模組的二進制檔案。
屬性列表會根據 Attribute
(在 attrib_entry/0
中)排序,並且每個屬性名稱在列表中只會出現一次。屬性值的順序與檔案中的順序相同。函式列表也會排序。
"Attr" | "CInf" | "Dbgi" | "ExpT" | "ImpT" | "LocT" | "AtU8" | "Docs"
儲存在 debug_info
區塊中的格式。
函式
讀取所有區塊的區塊資料。
從區塊清單建立 BEAM 模組(以二進制檔案形式)。
讀取所選區塊參考的區塊資料。返回的區塊資料清單的順序由區塊參考清單的順序決定。
讀取所選區塊參考的區塊資料。返回的區塊資料清單的順序由區塊參考清單的順序決定。
取消註冊加密金鑰 fun,並終止由 crypto_key_fun/1
啟動的、持有它的程序。
比較兩個 BEAM 檔案的內容。
比較兩個目錄中的 BEAM 檔案。
註冊一個單一 fun,如果 beam_lib
必須讀取已加密的 debug_info
區塊,則會呼叫該 fun。該 fun 保存在此函式啟動的程序中。
將兩個目錄中的 BEAM 檔案與 cmp_dirs/2
比較,但是只在一個目錄中存在或不同的檔案名稱會顯示在標準輸出中。
對於此模組中任何函式返回的指定錯誤,此函式會返回錯誤的英文描述字串。對於檔案錯誤,必須呼叫函式 file:format_error(Posix)
。
返回一個清單,其中包含關於 BEAM 檔案的一些資訊,以 tuple {Item, Info}
的形式呈現
計算模組程式碼的 MD5 冗餘檢查(不包含編譯日期和其他屬性)。
從 BEAM 檔案中移除所有區塊,但載入器使用的區塊除外。
從 BEAM 檔案中移除所有區塊,但載入器使用或在 AdditionalChunks
中提及的區塊除外。
從 Files
中移除所有區塊,但載入器使用的區塊除外。
從 Files
中移除所有區塊,但載入器使用或在 AdditionalChunks
中提及的區塊除外。
從版本的 BEAM 檔案中移除所有區塊,但載入器使用的區塊除外。
移除版本中 BEAM 檔案的所有區塊,但載入器使用或在 AdditionalChunks
中提及的區塊除外。
返回模組版本。版本由模組屬性 -vsn(Vsn)
定義。
類型
不會檢查表單是否符合 AbstVersion
所指示的抽象格式。no_abstract_code
表示 "Abst"
區塊存在,但為空白。
對於使用 OTP 20 或更高版本編譯的模組,abst_code
區塊會自動從 debug_info
區塊計算得出。
-type beam() :: file:filename() | binary().
下面描述的每個函式都接受檔案名稱(以字串形式)或包含 BEAM 模組的二進制檔案。
-type chnk_rsn() :: {unknown_chunk, file:filename(), atom()} | {key_missing_or_invalid, file:filename(), abstract_code | debug_info} | {missing_backend, file:filename(), module()} | info_rsn().
-type 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()}]} | {documentation, docs()}.
屬性列表會根據 Attribute
(在 attrib_entry/0
中)排序,並且每個屬性名稱在列表中只會出現一次。屬性值的順序與檔案中的順序相同。函式列表也會排序。
-type chunkid() :: nonempty_string().
"Attr" | "CInf" | "Dbgi" | "ExpT" | "ImpT" | "LocT" | "AtU8" | "Docs"
-type chunkname() ::
abstract_code | debug_info | attributes | compile_info | exports | labeled_exports | imports |
indexed_imports | locals | labeled_locals | atoms | documentation.
-type crypto_fun() :: fun((crypto_fun_arg()) -> term()).
-type crypto_fun_arg() :: init | clear | {debug_info, mode(), module(), file:filename()}.
-type dataB() :: binary().
儲存在 debug_info
區塊中的格式。
要從後端檢索特定的程式碼表示法,必須調用 Backend:debug_info(Format, Module, Data, Opts)
。Format
是一個原子,例如用於 Erlang 抽象格式的 erlang_v1
或用於 Core Erlang 的 core_v1
。Module
是由 beam 檔案表示的模組,而 Data
是儲存在除錯資訊區塊中的值。Opts
是 Backend
支援的任何值列表。Backend:debug_info/4
必須返回 {ok, Code}
或 {error, Term}
。
開發人員必須始終調用 debug_info/4
函數,絕不依賴儲存在 debug_info
區塊中的 Data
,因為它是不可見的,並且可能隨時變更。no_debug_info
表示區塊 "Dbgi"
存在,但為空。
-type forms() :: [erl_parse:abstract_form() | erl_parse:form_info()].
-type index() :: non_neg_integer().
-type info_rsn() :: {chunk_too_big, file:filename(), chunkid(), ChunkSize :: non_neg_integer(), FileSize :: non_neg_integer()} | {invalid_beam_file, file:filename(), Position :: non_neg_integer()} | {invalid_chunk, file:filename(), chunkid()} | {missing_chunk, file:filename(), chunkid()} | {not_a_beam_file, file:filename()} | {file_error, file:filename(), file:posix()}.
-type label() :: integer().
-type mode() :: des3_cbc.
函式
讀取所有區塊的區塊資料。
-spec build_module(Chunks) -> {ok, Binary} when Chunks :: [{chunkid(), dataB()}], Binary :: binary().
從區塊清單建立 BEAM 模組(以二進制檔案形式)。
-spec chunks(Beam, ChunkRefs) -> {ok, {module(), [chunkdata()]}} | {error, beam_lib, chnk_rsn()} when Beam :: beam(), ChunkRefs :: [chunkref()].
讀取所選區塊參考的區塊資料。返回的區塊資料清單的順序由區塊參考清單的順序決定。
-spec chunks(Beam, ChunkRefs, Options) -> {ok, {module(), [ChunkResult]}} | {error, beam_lib, chnk_rsn()} when Beam :: beam(), ChunkRefs :: [chunkref()], Options :: [allow_missing_chunks], ChunkResult :: chunkdata() | {ChunkRef :: chunkref(), missing_chunk}.
讀取所選區塊參考的區塊資料。返回的區塊資料清單的順序由區塊參考清單的順序決定。
預設情況下,如果 Beam
中缺少任何請求的區塊,則會返回 error
元組。但是,如果指定了選項 allow_missing_chunks
,即使缺少區塊也會返回結果。在結果列表中,任何缺少的區塊都表示為 {ChunkRef,missing_chunk}
。但是請注意,如果缺少區塊 "Atom"
,則會將其視為嚴重錯誤,並且返回值為 error
元組。
-spec clear_crypto_key_fun() -> undefined | {ok, Result} when Result :: undefined | term().
取消註冊加密金鑰 fun,並終止由 crypto_key_fun/1
啟動的、持有它的程序。
如果沒有註冊任何加密金鑰函式,則返回 {ok, undefined}
,或者返回 {ok, Term}
,其中 Term
是 CryptoKeyFun(clear)
的返回值,請參閱 crypto_key_fun/1
。
比較兩個 BEAM 檔案的內容。
如果模組名稱相同,並且除了區塊 "CInf"
(包含由 Module:module_info(compile)
返回的編譯資訊的區塊)之外的所有區塊在兩個檔案中都具有相同的內容,則返回 ok
。否則,將返回錯誤訊息。
-spec cmp_dirs(Dir1, Dir2) -> {Only1, Only2, Different} | {error, beam_lib, Reason} when Dir1 :: atom() | file:filename(), Dir2 :: atom() | file:filename(), Only1 :: [file:filename()], Only2 :: [file:filename()], Different :: [{Filename1 :: file:filename(), Filename2 :: file:filename()}], Reason :: {not_a_directory, term()} | info_rsn().
比較兩個目錄中的 BEAM 檔案。
僅比較副檔名為 ".beam"
的檔案。僅存在於目錄 Dir1
(Dir2
) 中的 BEAM 檔案會返回在 Only1
(Only2
) 中。存在於兩個目錄中,但被 cmp/2
認為不同的 BEAM 檔案會以配對 {Filename1
, Filename2
} 的形式返回,其中 Filename1
(Filename2
) 存在於目錄 Dir1
(Dir2
) 中。
-spec crypto_key_fun(CryptoKeyFun) -> ok | {error, Reason} when CryptoKeyFun :: crypto_fun(), Reason :: badfun | exists | term().
註冊一個單一 fun,如果 beam_lib
必須讀取已加密的 debug_info
區塊,則會呼叫該 fun。該 fun 保存在此函式啟動的程序中。
如果在嘗試註冊函式時已註冊函式,則返回 {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
當需要檔案名稱為 Filename
的模組 Module
的金鑰時呼叫。Mode
是加密演算法的類型;目前,唯一可能的值是 des3_cbc
。如果沒有可用的金鑰,則呼叫將失敗 (引發例外)。
CryptoKeyFun(clear) -> term()
在此函式取消註冊之前呼叫。在此處可以執行任何清理操作。返回值並不重要,但會作為其返回值的一部分傳回給 clear_crypto_key_fun/0
的呼叫者。
-spec diff_dirs(Dir1, Dir2) -> ok | {error, beam_lib, Reason} when Dir1 :: atom() | file:filename(), Dir2 :: atom() | file:filename(), Reason :: {not_a_directory, term()} | info_rsn().
將兩個目錄中的 BEAM 檔案與 cmp_dirs/2
比較,但是只在一個目錄中存在或不同的檔案名稱會顯示在標準輸出中。
-spec format_error(Reason) -> io_lib:chars() when Reason :: term().
對於此模組中任何函式返回的指定錯誤,此函式會返回錯誤的英文描述字串。對於檔案錯誤,必須呼叫函式 file:format_error(Posix)
。
-spec info(Beam) -> [InfoPair] | {error, beam_lib, info_rsn()} when Beam :: beam(), InfoPair :: {file, Filename :: file:filename()} | {binary, Binary :: binary()} | {module, Module :: module()} | {chunks, [{ChunkId :: chunkid(), Pos :: non_neg_integer(), Size :: non_neg_integer()}]}.
返回一個清單,其中包含關於 BEAM 檔案的一些資訊,以 tuple {Item, Info}
的形式呈現
{file, Filename} | {binary, Binary}
- BEAM 檔案的名稱 (字串),或从中提取信息的二进制檔。{module, Module}
- 模組的名稱 (原子)。{chunks, [{ChunkId, Pos, Size}]}
- 對於每個區塊,區塊資料的識別碼 (字串) 以及區塊資料的位置和大小 (以位元組為單位)。
-spec md5(Beam) -> {ok, {module(), MD5}} | {error, beam_lib, chnk_rsn()} when Beam :: beam(), MD5 :: binary().
計算模組程式碼的 MD5 冗餘檢查(不包含編譯日期和其他屬性)。
-spec strip(Beam1) -> {ok, {module(), Beam2}} | {error, beam_lib, info_rsn()} when Beam1 :: beam(), Beam2 :: beam().
從 BEAM 檔案中移除所有區塊,但載入器使用的區塊除外。
特別是,會移除除錯資訊 (區塊 debug_info
和 abstract_code
)。
-spec strip(Beam1, AdditionalChunks) -> {ok, {module(), Beam2}} | {error, beam_lib, info_rsn()} when Beam1 :: beam(), AdditionalChunks :: [chunkid()], Beam2 :: beam().
從 BEAM 檔案中移除所有區塊,但載入器使用或在 AdditionalChunks
中提及的區塊除外。
特別是,會移除除錯資訊 (區塊 debug_info
和 abstract_code
)。
-spec strip_files(Files) -> {ok, [{module(), Beam}]} | {error, beam_lib, info_rsn()} when Files :: [beam()], Beam :: beam().
從 Files
中移除所有區塊,但載入器使用的區塊除外。
特別是,會移除除錯資訊 (區塊 debug_info
和 abstract_code
)。返回的列表針對每個指定的檔案名包含一個元素,其順序與 Files
中的順序相同。
-spec strip_files(Files, AdditionalChunks) -> {ok, [{module(), Beam}]} | {error, beam_lib, info_rsn()} when Files :: [beam()], AdditionalChunks :: [chunkid()], Beam :: beam().
從 Files
中移除所有區塊,但載入器使用或在 AdditionalChunks
中提及的區塊除外。
特別是,會移除除錯資訊 (區塊 debug_info
和 abstract_code
)。返回的列表針對每個指定的檔案名包含一個元素,其順序與 Files
中的順序相同。
-spec strip_release(Dir) -> {ok, [{module(), file:filename()}]} | {error, beam_lib, Reason} when Dir :: atom() | file:filename(), Reason :: {not_a_directory, term()} | info_rsn().
從版本的 BEAM 檔案中移除所有區塊,但載入器使用的區塊除外。
Dir
應為安裝根目錄。例如,可以使用呼叫 beam_lib:strip_release(code:root_dir())
來剝離目前的 OTP 版本。
-spec strip_release(Dir, AdditionalChunks) -> {ok, [{module(), file:filename()}]} | {error, beam_lib, Reason} when Dir :: atom() | file:filename(), AdditionalChunks :: [chunkid()], Reason :: {not_a_directory, term()} | info_rsn().
移除版本中 BEAM 檔案的所有區塊,但載入器使用或在 AdditionalChunks
中提及的區塊除外。
Dir
應為安裝根目錄。例如,可以使用呼叫 beam_lib:strip_release(code:root_dir(),[documentation])
來剝離目前的 OTP 版本。
-spec version(Beam) -> {ok, {module(), [Version :: term()]}} | {error, beam_lib, chnk_rsn()} when Beam :: beam().
返回模組版本。版本由模組屬性 -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]}}