fwtool package

Submodules

fwtool.cli module

Firmware header utility.

A command-line interface for working with a custom firmware header placed at the beginning of a binary image.

Header format

The first 16 bytes of the header contain metadata fields. The remaining bytes up to the configured header size are filled with 0xFF padding.

0x00..0x03 magic: b”XLAB” 0x04..0x07 version: little-endian bytes [0x00, patch, minor, major] 0x08..0x0B size: payload size in bytes, little-endian uint32 0x0C..0x0F crc32: CRC-32/MPEG-2 of payload, little-endian uint32 0x10..end padding: 0xFF

The total header size is configurable via --header-size and must be a power of two so that the application vector table that follows it is correctly aligned. The default is 512 bytes.

Supported operations

  • attach: prepend a new header to a raw firmware binary

  • edit: replace the header of an already packaged binary

  • inspect: print header contents

  • verify: verify header magic, payload size, and payload CRC

Examples

Attach a header (default 512 bytes):

fwtool attach firmware.bin 1.2.3 out.bin

Attach with custom header size:

fwtool attach firmware.bin 1.2.3 out.bin –header-size 1024

Edit an existing header:

fwtool edit packaged.bin 1.2.4 out.bin

Edit in place:

fwtool edit packaged.bin 1.2.4 –in-place

Inspect header:

fwtool inspect packaged.bin

Inspect as JSON:

fwtool inspect packaged.bin –json

Verify header:

fwtool verify packaged.bin

Verify quietly:

fwtool verify packaged.bin –quiet

class fwtool.cli.HeaderInfo(magic, version, size, crc)[source]

Bases: object

Parsed header fields extracted from a packaged firmware binary.

crc: int
classmethod from_bytes(data)[source]

Parse header info fields from a binary blob.

Only the first HEADER_INFO_SIZE bytes are read.

Parameters:

data (bytes) – Byte buffer containing at least HEADER_INFO_SIZE bytes.

Return type:

HeaderInfo

Returns:

Parsed HeaderInfo object.

Raises:

ValueError – If fewer than HEADER_INFO_SIZE bytes are provided.

magic: bytes
size: int
to_dict()[source]

Convert the header to a JSON-friendly dictionary.

Return type:

dict

Returns:

Dictionary representation of the header.

version: VersionInfo
class fwtool.cli.VerificationResult(magic_ok, size_ok, crc_ok, header, header_size, payload_size, payload_crc)[source]

Bases: object

Verification outcome for an existing packaged firmware binary.

crc_ok: bool
header: HeaderInfo
header_size: int
magic_ok: bool
property ok: bool

Return whether all verification checks passed.

Returns:

True if magic, size, and CRC checks all passed.

payload_crc: int
payload_size: int
size_ok: bool
to_dict()[source]

Convert the verification result to a JSON-friendly dictionary.

Return type:

dict

Returns:

Dictionary representation of the verification result.

class fwtool.cli.VersionInfo(major=0, minor=0, patch=0)[source]

Bases: object

Semantic firmware version stored as major.minor.patch.

classmethod from_bytes(data)[source]

Parse the 4-byte packed version field from the binary header.

The byte layout is:

[0x00, patch, minor, major]

Parameters:

data (bytes) – 4-byte version field.

Return type:

VersionInfo

Returns:

Parsed VersionInfo object.

Raises:

ValueError – If the input length is not 4 bytes.

classmethod from_string(value)[source]

Parse a version string into a VersionInfo instance.

Accepted formats include:

“1” “1.2” “1.2.3”

Missing components are padded with zero. Extra components are ignored.

Parameters:

value (str) – Version string.

Return type:

VersionInfo

Returns:

Parsed VersionInfo object.

Raises:
  • ValueError – If version components are out of range or not integers.

  • ValueError – If version string is empty

major: int = 0
minor: int = 0
patch: int = 0
to_bytes()[source]

Convert the version into the 4-byte packed header representation.

Return type:

bytes

Returns:

Packed version bytes as [0x00, patch, minor, major].

to_dict()[source]

Convert the version into a JSON-friendly dictionary.

Return type:

dict

Returns:

Dictionary representation of the version.

validate()[source]

Validate that all version components fit in one byte.

Raises:

ValueError – If major, minor, or patch is outside 0..255.

Return type:

None

fwtool.cli.add_binary_arg(parser)[source]

Add the binary positional argument to a subparser.

Parameters:

parser (ArgumentParser) – Subparser to add the argument to.

Return type:

None

fwtool.cli.add_header_size_arg(parser, default=None, help_suffix='')[source]

Add the --header-size option to a subparser.

Parameters:
  • parser (ArgumentParser) – Subparser to add the argument to.

  • default (int | None) – Default value; None means “infer from file”.

  • help_suffix (str) – Extra text appended to the help string.

Return type:

None

fwtool.cli.add_json_arg(parser)[source]

Add the --json flag to a subparser.

Parameters:

parser (ArgumentParser) – Subparser to add the argument to.

Return type:

None

fwtool.cli.add_output_args(parser)[source]

Add mutually exclusive output / --in-place arguments to a subparser.

Parameters:

parser (ArgumentParser) – Subparser to add the arguments to.

Return type:

None

fwtool.cli.add_version_arg(parser)[source]

Add the version positional argument to a subparser.

Parameters:

parser (ArgumentParser) – Subparser to add the argument to.

Return type:

None

fwtool.cli.build_header(payload, version, header_size)[source]

Build a new firmware header for the given payload and version.

The size and CRC are computed from the payload only. The header is padded with 0xFF to header_size bytes.

Parameters:
  • payload (bytes) – Firmware payload bytes.

  • version (VersionInfo) – Firmware version to store in the header.

  • header_size (int) – Total header size in bytes.

Return type:

bytes

Returns:

Complete header of exactly header_size bytes.

Raises:

RuntimeError – If the generated header size does not match.

fwtool.cli.build_parser()[source]

Build the top-level argument parser with subcommands.

Subcommands:

attach: attach a new header to a raw firmware binary edit: replace the header of a packaged binary inspect: print parsed header fields verify: verify header against the payload

Return type:

ArgumentParser

Returns:

Configured ArgumentParser instance.

fwtool.cli.handle_attach(args)[source]

Handle the attach subcommand.

Reads a raw firmware binary, builds a new header, and writes the combined packaged binary.

Parameters:

args (Namespace) – Parsed CLI arguments for the attach subcommand.

Return type:

None

fwtool.cli.handle_edit(args)[source]

Handle the edit subcommand.

Reads a packaged firmware binary, strips its existing header, builds a new header with the given version, and writes the result.

Parameters:

args (Namespace) – Parsed CLI arguments for the edit subcommand.

Return type:

None

fwtool.cli.handle_inspect(args)[source]

Handle the inspect subcommand.

Reads a packaged firmware binary and prints parsed header information.

Parameters:

args (Namespace) – Parsed CLI arguments for the inspect subcommand.

Return type:

None

fwtool.cli.handle_verify(args)[source]

Handle the verify subcommand.

Reads a packaged firmware binary, verifies its header against the payload, and exits with 0 on success or 1 on failure.

Parameters:

args (Namespace) – Parsed CLI arguments for the verify subcommand.

Return type:

None

fwtool.cli.infer_header_size(data)[source]

Infer the header size of a packaged binary from its stored payload size field.

The header size is calculated as:

len(data) - stored_payload_size

Parameters:

data (bytes) – Packaged binary data.

Return type:

int

Returns:

Inferred header size in bytes.

Raises:

ValueError – If the file is too small, the stored payload size is invalid, or the inferred header size is unreasonable.

fwtool.cli.main()[source]

Parse arguments and dispatch to the appropriate subcommand handler.

Return type:

None

fwtool.cli.parse_header(data)[source]

Parse and validate the header from a packaged firmware binary.

Parameters:

data (bytes) – Input binary data that should start with a valid header.

Return type:

HeaderInfo

Returns:

Parsed HeaderInfo.

Raises:

ValueError – If the input is too small or does not begin with the expected magic.

fwtool.cli.print_header(info, as_json=False)[source]

Print parsed header information in text or JSON format.

Parameters:
  • info (HeaderInfo) – Parsed header information.

  • as_json (bool) – If True, print JSON instead of human-readable text.

Return type:

None

fwtool.cli.print_verification_result(result, as_json=False)[source]

Print a verification result in text or JSON format.

Parameters:
  • result (VerificationResult) – Verification result to print.

  • as_json (bool) – If True, print JSON instead of human-readable text.

Return type:

None

fwtool.cli.read_binary(path)[source]

Read the full contents of a binary file.

Parameters:

path (Path) – Input file path.

Return type:

bytes

Returns:

File contents as bytes.

fwtool.cli.resolve_header_size_for_build(args, data=None)[source]

Determine and validate the header size for attach/edit operations.

For attach mode the default is DEFAULT_HEADER_SIZE. For edit mode the size is inferred from the existing file when --header-size is not provided.

Parameters:
  • args (Namespace) – Parsed CLI arguments (must contain header_size).

  • data (bytes | None) – Existing packaged binary data used for inference, or None.

Return type:

int

Returns:

Validated header size.

Raises:

ValueError – If the resolved size is invalid.

fwtool.cli.resolve_header_size_for_verify(args, data)[source]

Determine the header size for verification.

If --header-size is provided it is used directly; otherwise the size is inferred from the stored payload size.

Parameters:
  • args (Namespace) – Parsed CLI arguments (must contain header_size).

  • data (bytes) – Packaged binary data.

Return type:

int

Returns:

Header size in bytes.

Raises:

ValueError – If inference fails and no explicit size was given.

fwtool.cli.split_payload_from_packaged(data, header_size)[source]

Split a packaged binary into header and payload.

Parameters:
  • data (bytes) – Packaged binary data.

  • header_size (int) – Expected header size in bytes.

Return type:

tuple[bytes, bytes]

Returns:

A tuple of (existing_header, payload).

Raises:

ValueError – If the data is smaller than header_size.

fwtool.cli.validate_header_size(size)[source]

Validate that a header size is acceptable.

The size must be at least HEADER_INFO_SIZE bytes and must be a power of two so that the vector table following it in flash is correctly aligned.

Parameters:

size (int) – Proposed header size in bytes.

Raises:

ValueError – If the size is too small or not a power of two.

Return type:

None

fwtool.cli.validate_input_file(path)[source]

Validate that the input path exists and refers to a regular file.

Parameters:

path (Path) – Input file path.

Raises:
  • FileNotFoundError – If the file does not exist.

  • ValueError – If the path exists but is not a regular file.

Return type:

None

fwtool.cli.verify_header(data, header_size)[source]

Verify the header of a packaged firmware binary against its payload.

The following checks are performed:
  • magic matches HEADER_MAGIC

  • stored size matches actual payload size

  • stored CRC matches CRC-32/MPEG-2 of payload

Parameters:
  • data (bytes) – Packaged firmware binary data.

  • header_size (int) – Header size in bytes used to locate the payload.

Return type:

VerificationResult

Returns:

VerificationResult with parsed header fields and check results.

Raises:

ValueError – If the input is too small to contain a header.

fwtool.cli.write_binary(path, data)[source]

Write binary data to the given path, creating parent directories if needed.

Parameters:
  • path (Path) – Output file path.

  • data (bytes) – Data to write.

Return type:

None

fwtool.cli.write_binary_in_place(path, data)[source]

Safely replace an existing file with new binary data.

The replacement is done by writing to a temporary file in the same directory, flushing it to disk, and then atomically replacing the target file.

Parameters:
  • path (Path) – File to replace.

  • data (bytes) – New file contents.

Raises:

OSError – If writing or replacement fails.

Return type:

None

fwtool.cli.write_output(path, data, in_place)[source]

Write output data to the appropriate destination.

Parameters:
  • path (Path | None) – Output file path, or None if in-place is used.

  • data (bytes) – Data to write.

  • in_place (Path | None) – Original input path for in-place replacement, or None.

Return type:

None

Module contents