Packbeam Format

AtomVM makes use of the packbeam format for aggregating beam and other file types into a single file that is used as the code base for an AtomVM application. Typically, on an embedded device, packbeam files are uploaded (e.g., via serial connection) to a specific location on flash media. The AtomVM runtime will locate the entrypoint into the application, and use the beam and other files flashed to the local media to run the uploaded application.

AtomVM provides a simple tool for generating packbeam files, but other tools have emerged for manipulation packbeam files using standard Erlang and Elixir tool chains, notably Mix and rebar3.

This document describes the packbeam format, so that both AtomVM and upstream/downstream tooling have a reference document on which to base implementations.

Overview

Packbeam files are binary-encoded aggregations of BEAM and plain data files. At a high level, a packbeam file consists of a packbeam header, followed by a sequence of files (beam or otherwise), each of which is prefixed with a header, including data about the file (name, size, flags, etc).

All binary integer values are 32-bit, in network order (big-endian). Headers and encoded files are padded when necessary and aligned on 4-byte boundaries.

At present, the AtomVM runtime treats data in packbeam files as read-only data. There is no support for modifying the contents on an AtomVM file by the runtime.

Packbeam Header

All AtomVM files begin with the packbeam header, a fixed 24-byte sequence of octets:

0x23, 0x21, 0x2f, 0x75,
0x73, 0x72, 0x2f, 0x62,
0x69, 0x6e, 0x2f, 0x65,
0x6e, 0x76, 0x20, 0x41,
0x74, 0x6f, 0x6d, 0x56,
0x4d, 0x0a, 0x00, 0x00

The ASCII encoding of this sequence is

#!/usr/bin/env AtomVM\n

followed by two nil (0x00) bytes.

The packbeam header is followed by a sequence of 0 or more encoded files. The number of files in a packbeam file is not indicated in the packbeam header; however, packbeam files do contain a special end file header, marking the end of the sequence of encoded files.

File encodings

Each embedded file in a packbeam file contains a file header, followed by the file contents.

File Header

The file header consists of the following 4 fields:

  • size (32 bit, big-endian)

  • flags (32-bit, big endian)

  • reserved (32-bit, big-endian, currently unused)

  • module_name (null-terminated sequence of bytes)

The size field indicates the size (in bytes) of the encoded file following the header. This size includes the file content length, in addition to any padding that may have been added to the file, in order for it to align on a 4-byte boundary.

Currently, the two low-order bits of the flags field are used. 0x02 indicates the file is a BEAM file, and 0x01 indicates that the file contains a start/0 function, and is therefore suitable as an entrypoint to start code execution.

When AtomVM starts, it will scan the BEAM files in the AtomVM file, from start to finish, with which it is initialized to find the entrypoint to start code execution. It will start execution on the first BEAM file with a start/0 function, i.e., whose flags mask against 0x03. It is conventional, but not required, for the first file in an AtomVM file to be a BEAM file that has a start/0 entrypoint.

The reserved field is currently unused.

The module_name is variable length, null terminated sequence of characters. Because the module name is variable-length, the header may be padded with null characters (0x00), in order to align the start of the file contents on a 4-byte boundary.

Example

The following BEAM header indicates a BEAM file with a length of 308 bytes (0x00000134), with a start/0 entrypoint (0x00000003), and named mylib.beam (0x6D796C69 622E6265 616D00). The header has a 1-byte padding of null (0x00) characters.

00000134 00000003 00000000 6D796C69 622E6265 616D0000

BEAM files

BEAM files obey IFF encoding as detailed here, but certain information in BEAM files is stripped out in order to minimize the amount of data stored on flash.

The following BEAM chunks are included in BEAM files:

  • AtU8

  • Code

  • ExpT

  • LocT

  • ImpT

  • LitU

  • FunT

  • StrT

  • LitT

Any other chunks are stripped out of the BEAM files before insertion into AVM files.

In addition, data in the literals table (LitT) are uncompressed before insertion into AVM files, as the AtomVM runtime does not include support for zlib decompression.

BEAM files may be padded at the end with a sequence of 1-3 null (0x00) characters, in order to align on 4-byte boundaries.

Note. The module_name field in the file header will only contain the “base” name of the BEAM file, i.e., the file name stripped of any path information.

Normal Files

Normal files (e.g., text files, data files, etc.) can be stored in packbeam AVM files, as well as BEAM files. For example, a normal file might contain static configuration information, or data that is interpreted at runtime.

Normal files contain a 32-bit big-endian size prefix, indicating the size of the file data (without padding). Note that the size field in the file header includes the size of the data with padding, if applicable.

The AtomVM runtime provides access to data files via the atomvm:read_priv/2 NIF. This function will create a path name formed by the App (atom) and Path (string) terms provided by this function, separated by "/priv/". For example, the expression

atomvm:read_priv(mylib, "sample.txt")

yields a binary containing the contents of mylib/priv/sample.txt, if it exists, in the AtomVM packbeam file.

As a consequence, normal files should be included in packbeam files using module names that obey the above patterns.

Note. Normal file names may encode virtual directory names, such as mylib/priv/another/sample/text/file. There is no requirement that the Path component of a normal file be a simple file name.

end file

Packbeam files end with a special end header. The size field of the end header is 0 bytes.

Example

The following sequence of bytes encodes the end header:

00000000 00000000 00000000 656E6400