檢視原始碼 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/1strip_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 加密是不可行的。因此,只要金鑰保持安全且無法猜測,加密的除錯資訊應該對入侵者是安全的。

可以透過以下兩種方式提供金鑰:

  1. 使用編譯器選項 {debug_info_key,Key},請參閱 compile 和函式 crypto_key_fun/1,以註冊一個 fun,該 fun 會在 beam_lib 必須解密除錯資訊時返回金鑰。

如果未註冊此類 fun,則 beam_lib 會改為搜尋 .erlang.crypt 檔案,請參閱下一節。

  1. 將金鑰儲存在名為 .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_cbcModule 是原子,在這種情況下,Key 僅用於模組 Module,或是 [],在這種情況下,Key 用於所有模組。Key 是非空白的金鑰字串。

使用第一個 ModeModule 皆匹配的 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) 定義。

類型

連結到此類型

abst_code()

檢視原始碼 (未匯出)
-type abst_code() :: {AbstVersion :: atom(), forms()} | no_abstract_code.

不會檢查表單是否符合 AbstVersion 所指示的抽象格式。no_abstract_code 表示 "Abst" 區塊存在,但為空白。

對於使用 OTP 20 或更高版本編譯的模組,abst_code 區塊會自動從 debug_info 區塊計算得出。

-type attrib_entry() :: {Attribute :: atom(), [AttributeValue :: term()]}.
-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().
連結到此類型

chunkdata()

檢視原始碼 (未匯出)
-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"

連結到此類型

chunkname()

檢視原始碼 (未匯出)
-type chunkname() ::
          abstract_code | debug_info | attributes | compile_info | exports | labeled_exports | imports |
          indexed_imports | locals | labeled_locals | atoms | documentation.
連結到此類型

chunkref()

檢視原始碼 (未匯出)
-type chunkref() :: chunkname() | chunkid().
連結到此類型

cmp_rsn()

檢視原始碼 (未匯出)
-type cmp_rsn() ::
          {modules_different, module(), module()} |
          {chunks_different, chunkid()} |
          different_chunks |
          info_rsn().
-type compinfo_entry() :: {InfoKey :: atom(), term()}.
連結到此類型

crypto_fun()

檢視原始碼 (未匯出)
-type crypto_fun() :: fun((crypto_fun_arg()) -> term()).
連結到此類型

crypto_fun_arg()

檢視原始碼 (未匯出)
-type crypto_fun_arg() :: init | clear | {debug_info, mode(), module(), file:filename()}.
-type dataB() :: binary().
連結到此類型

debug_info()

檢視原始碼 (未匯出)
-type debug_info() :: {DbgiVersion :: atom(), Backend :: module(), Data :: term()} | no_debug_info.

儲存在 debug_info 區塊中的格式。

要從後端檢索特定的程式碼表示法,必須調用 Backend:debug_info(Format, Module, Data, Opts)Format 是一個原子,例如用於 Erlang 抽象格式的 erlang_v1 或用於 Core Erlang 的 core_v1Module 是由 beam 檔案表示的模組,而 Data 是儲存在除錯資訊區塊中的值。OptsBackend 支援的任何值列表。Backend:debug_info/4 必須返回 {ok, Code}{error, Term}

開發人員必須始終調用 debug_info/4 函數,絕不依賴儲存在 debug_info 區塊中的 Data,因為它是不可見的,並且可能隨時變更。no_debug_info 表示區塊 "Dbgi" 存在,但為空。

-type docs() ::
          #docs_v1{anno :: term(),
                   beam_language :: term(),
                   format :: term(),
                   module_doc :: term(),
                   metadata :: term(),
                   docs :: term()}.

EEP-48 文件格式

-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 labeled_entry() :: {Function :: atom(), arity(), label()}.
-type mode() :: des3_cbc.

函式

連結到此函式

all_chunks(File)

檢視原始碼 (自 OTP 18.2 起)
-spec all_chunks(beam()) -> {ok, module(), [{chunkid(), dataB()}]} | {error, beam_lib, info_rsn()}.

讀取所有區塊的區塊資料。

連結到此函式

build_module(Chunks)

檢視原始碼 (自 OTP 18.2 起)
-spec build_module(Chunks) -> {ok, Binary} when Chunks :: [{chunkid(), dataB()}], Binary :: binary().

從區塊清單建立 BEAM 模組(以二進制檔案形式)。

連結到此函式

chunks(Beam, ChunkRefs)

檢視原始碼
-spec chunks(Beam, ChunkRefs) -> {ok, {module(), [chunkdata()]}} | {error, beam_lib, chnk_rsn()}
                when Beam :: beam(), ChunkRefs :: [chunkref()].

讀取所選區塊參考的區塊資料。返回的區塊資料清單的順序由區塊參考清單的順序決定。

連結到此函式

chunks(Beam, ChunkRefs, Options)

檢視原始碼
-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 元組。

連結到此函式

clear_crypto_key_fun()

檢視原始碼
-spec clear_crypto_key_fun() -> undefined | {ok, Result} when Result :: undefined | term().

取消註冊加密金鑰 fun,並終止由 crypto_key_fun/1 啟動的、持有它的程序。

如果沒有註冊任何加密金鑰函式,則返回 {ok, undefined},或者返回 {ok, Term},其中 TermCryptoKeyFun(clear) 的返回值,請參閱 crypto_key_fun/1

-spec cmp(Beam1, Beam2) -> ok | {error, beam_lib, cmp_rsn()} when Beam1 :: beam(), Beam2 :: beam().

比較兩個 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) 中。

連結到此函式

crypto_key_fun(CryptoKeyFun)

檢視原始碼
-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_infoabstract_code)。

連結到此函式

strip(Beam1, AdditionalChunks)

檢視原始碼 (自 OTP 22.0 起)
-spec strip(Beam1, AdditionalChunks) -> {ok, {module(), Beam2}} | {error, beam_lib, info_rsn()}
               when Beam1 :: beam(), AdditionalChunks :: [chunkid()], Beam2 :: beam().

從 BEAM 檔案中移除所有區塊,但載入器使用或在 AdditionalChunks 中提及的區塊除外。

特別是,會移除除錯資訊 (區塊 debug_infoabstract_code)。

-spec strip_files(Files) -> {ok, [{module(), Beam}]} | {error, beam_lib, info_rsn()}
                     when Files :: [beam()], Beam :: beam().

Files 中移除所有區塊,但載入器使用的區塊除外。

特別是,會移除除錯資訊 (區塊 debug_infoabstract_code)。返回的列表針對每個指定的檔案名包含一個元素,其順序與 Files 中的順序相同。

連結到此函式

strip_files(Files, AdditionalChunks)

檢視原始碼 (自 OTP 22.0 起)
-spec strip_files(Files, AdditionalChunks) -> {ok, [{module(), Beam}]} | {error, beam_lib, info_rsn()}
                     when Files :: [beam()], AdditionalChunks :: [chunkid()], Beam :: beam().

Files 中移除所有區塊,但載入器使用或在 AdditionalChunks 中提及的區塊除外。

特別是,會移除除錯資訊 (區塊 debug_infoabstract_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 版本。

連結到此函式

strip_release(Dir, AdditionalChunks)

檢視原始碼 (自 OTP 22.0 起)
-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]}}