Erlang/OTP does not publish a specification of the BEAM file. This page is drawn from
the public sources that describe it: the
beam_lib
documentation, the chapter on BEAM files in The BEAM Book, and the compiler’s
own writer, beam_asm.erl, in the Erlang/OTP source (development branch,
November 2026). Details that change between OTP releases are marked. All integers are
32-bit big-endian unless a table says otherwise.
Container
| Offset | Size | Field | Meaning |
|---|---|---|---|
0 | 4 | form ID | FOR1. Standard IFF uses FORM; the different ID marks that BEAM does not follow IFF exactly. |
4 | 4 | size | Bytes that follow this field: the form type and all chunks. |
8 | 4 | form type | BEAM. |
12 | … | chunks | One after another to the end. |
Chunk
| Offset | Size | Field | Meaning |
|---|---|---|---|
0 | 4 | chunk ID | Four ASCII characters such as Code. |
4 | 4 | size | Length of the data, not counting padding. |
8 | size | data | Format depends on the ID. |
| 0–3 | padding | Zero bytes up to a multiple of 4 (IFF pads only to 2). |
The loader needs the atom table and Code, StrT,
ImpT and ExpT, plus FunT and LitT
when the module has funs or literals (the compiler’s “essential” chunks). The other
chunks are optional, and beam_lib:strip/1 removes them.
Chunk IDs
| ID | beam_lib name | Contents |
|---|---|---|
AtU8 | atoms | Atom table, UTF-8. |
Atom | atoms | Atom table, Latin-1. |
Code | — | The bytecode. |
StrT | — | String table. |
ImpT | imports, indexed_imports | Imported functions. |
ExpT | exports, labeled_exports | Exported functions. |
LocT | locals, labeled_locals | Local functions. |
FunT | — | Fun (lambda) table. |
LitT | literals | Literal terms. |
Attr | attributes | Module attributes. |
CInf | compile_info | Compiler information. |
Line | — | Line-number table. |
Type | — | Type information for the loader (OTP 25 and later). |
Meta | — | Metadata given to the compiler as an extra chunk. |
Dbgi | debug_info | Debug information (OTP 20 and later). |
Abst | abstract_code | Abstract code (before OTP 20; now derived from Dbgi). |
Docs | documentation | Documentation, EEP 48. |
ExCk | — | Not written by the Erlang compiler; its layout is not described by any source read here (unknown). |
DbgB | — | Debugger information, only with the beam_debug_info option (development branch). |
Recs | — | Native records (development branch). |
AtU8 and Atom
| Field | Size | Meaning |
|---|---|---|
| count | 4 | Number of atoms. Recent compilers write it negated (as a negative 32-bit number) to say that the lengths below are compact-encoded rather than one byte, allowing longer atoms; the no_long_atoms option keeps the old form. |
| atoms | … | For each atom its length (one byte, or a compact u value when the count is negative) and its bytes. |
Atoms are numbered from 1 in table order. Atom 1 is the module’s name. Operands
refer to atoms by this number; an atom operand of 0 means [].
Code
| Offset | Size | Field | Meaning |
|---|---|---|---|
0 | 4 | header size | Length of the rest of this header in bytes: 16. A loader skips any extra header fields it does not know. |
4 | 4 | instruction set | Format number of the instruction set: 0. The loader rejects other values. |
8 | 4 | opcode max | Highest opcode used in the module. |
12 | 4 | label count | Number of labels. |
16 | 4 | function count | Number of functions. |
20 | … | code | Instructions: an opcode byte followed by its operands in the compact term encoding, ending with int_code_end. |
StrT
Every string used by the code (for bs_match_string and the like),
concatenated with no separators. Instructions give an offset and length. The chunk
may be empty.
ImpT, ExpT and LocT
| Chunk | Header | Entry (12 bytes) |
|---|---|---|
ImpT | count | module atom, function atom, arity |
ExpT | count | function atom, arity, entry label |
LocT | count | function atom, arity, entry label |
Imports are numbered from 0; call_ext and similar instructions refer to
them by that number. LocT lists the functions that are not exported,
and is left out with the compiler’s slim option.
FunT
A count, then one 24-byte entry for each fun the module creates:
| Offset | Size | Field | Meaning |
|---|---|---|---|
0 | 4 | function | Atom of the function that implements the fun. |
4 | 4 | arity | Its arity. |
8 | 4 | label | Its entry label. |
12 | 4 | index | The fun’s index in this table. |
16 | 4 | free | Number of free variables it captures. |
20 | 4 | old uniq | A checksum value: the top 27 bits of the module’s MD5. |
LitT
| Field | Size | Meaning |
|---|---|---|
| uncompressed size | 4 | Size of the table below once inflated, or 0 when it is stored uncompressed (the default from OTP 28). |
| table | … | zlib-compressed (non-zero size), or plain (size 0). |
The table is a count, then for each literal a 4-byte size and the term in the
external term
format, as made by term_to_binary. Literals are numbered from 0.
Floats in code are always literals.
Line
| Field | Size | Meaning |
|---|---|---|
| version | 4 | 0. |
| flags | 4 | Bit value 1: the module has executable_line instructions. Bit value 2: line counters are forced on. Development-branch compilers set these; older ones write 0. |
| line instructions | 4 | Number of line instructions in the code. |
| line items | 4 | Number of line items. |
| file names | 4 | Number of file names (not counting the module’s own source, which is file 0). |
| items | … | Compact-encoded: an a-tagged value switches to that file index, and each i-tagged value is a line number in the current file. |
| names | … | For each file a 16-bit length and the name in UTF-8. |
A line instruction’s operand is an index into the line items; 0 means
no location, and the first real item is 1.
Term chunks
These chunks hold one Erlang term in the external term format, as written by
term_to_binary:
| Chunk | Term |
|---|---|
Attr | The module attributes as a list of {Name, Value}. If the module has no vsn, the compiler adds {vsn, [MD5]} with the module’s MD5 as an integer. |
CInf | A list starting with {version, CompilerVersion}, then the compile information, as returned by Module:module_info(compile). |
Dbgi | {DbgiVersion, Backend, Data}, with the version an atom. Data is opaque and should be read only through Backend:debug_info/4. Debug information can be encrypted with des3_cbc using a key from .erlang.crypt or a key fun. |
Abst | The abstract format of the code, in older files; empty when there is none. |
Docs | An EEP 48 docs_v1 record with the fields anno, beam_language, format, module_doc, metadata and docs. |
Meta | Raw bytes supplied to the compiler as an extra chunk; The BEAM Book reads them with binary_to_term. |
Type
A 4-byte version, a 4-byte count, then the type entries the loader uses to pick
faster instructions; typed register operands refer to them. The entries are encoded
by the compiler’s beam_types module, which is not documented and has
changed with the version number.
Module checksum
The module’s MD5, returned by beam_lib:md5/1 and used for the default
vsn and the fun checksums, is computed over the contents of the
essential chunks (without their IDs, sizes or padding), so it does not change with
compile time, attributes or debug information.
Example
46 4F 52 31 "FOR1"
00 00 02 A4 size: 676 bytes follow
42 45 41 4D "BEAM"
41 74 55 38 "AtU8"
00 00 00 2D chunk size 45
FF FF FF FA atom count −6 (lengths are compact-encoded)
40 length 4 (4 << 4 | tag u = 0)
68 65 6C 6F "helo", atom 1: the module name
…
The sizes are illustrative; each chunk is padded to a multiple of four.
Sources
- beam_lib – chunk names, debug information and its encryption,
Abst,Docs,strip,CInf - The BEAM Book, chapter “Modules and the BEAM file format” – the IFF container, padding and the layouts of the atom, code, string, import, export, local, fun, literal and type chunks
- beam_asm.erl – what the compiler writes: essential chunks, negative atom count, Code header, FunT old uniq, uncompressed LitT, Line, Attr, CInf, DbgB, Recs, Meta, MD5
- beam_dict.erl – FunT fields, atom, import, literal and line numbering
- genop.tab – instruction set format number 0