A command-line interface for working with a custom firmware header placed at the beginning of a binary image.
Project description
fwtool
fwtool is a command-line utility for creating, updating, inspecting, and verifying
a custom firmware metadata header prepended to a binary image.
It is intended for firmware packaging workflows where a raw application binary is post-processed into a single flashable image:
- a fixed-size header is placed at the beginning that contains version, firmware size, and CRC information
- the firmware firmware follows immediately after
This is useful for bootloaders or firmware update logic that need to validate and identify an application image before booting or programming it.
What it does
fwtool supports four subcommands:
- attach — prepend a new header to a raw firmware binary
- edit — replace the header of an already packaged binary
- inspect — print the parsed contents of an existing header
- verify — verify that an existing header matches the firmware
Header format
The tool generates a header with the following layout:
| Offset | Size | Field | Description |
|---|---|---|---|
0x00..0x03 |
4 | magic | ASCII string XLAB |
0x04..0x07 |
4 | version | Little-endian bytes: [0x00, patch, minor, major] |
0x08..0x0B |
4 | size | firmware size in bytes, little-endian uint32 |
0x0C..0x0F |
4 | crc32 | CRC-32/MPEG-2 of firmware, little-endian uint32 |
0x10..end |
— | padding | Filled with 0xFF |
The total header size defaults to 512 bytes and is configurable via
--header-size. It must be a power of two (minimum 16 bytes) so that the
application vector table following it in flash is correctly aligned.
With the default 512-byte header, the padding region spans 0x10..0x1FF
(496 bytes of 0xFF).
Notes
- The CRC is calculated over the firmware only, not over the header.
- The size stored in the header is the firmware size only.
- The version is stored as:
- byte 0:
0x00 - byte 1: patch
- byte 2: minor
- byte 3: major
- byte 0:
For example, version 1.2.3 is stored as:
0x00030201
Typical use case
A common workflow looks like this:
- Link the firmware application so that it expects to execute after the reserved header space.
- Build the raw application binary.
- Use
fwtoolto prepend the metadata header. - Program the resulting combined binary into flash.
For example, with the default 512-byte header:
Flash address 0x08004000:
[512-byte metadata header]
[firmware payload]
Installation
Install using pip
pip install fwtool
Install using pipx
pipx install fwtool
Install using uv
uv tool install fwtool
Check installation
fwtool --version
Usage
fwtool <command> [arguments] [options]
Subcommands
Command Description
attach Attach a new header to a raw firmware binary
edit Replace the header of a packaged binary
inspect Print header fields from a packaged binary
verify Verify header of a packaged binary
Common options
--version Show program version and exit
--help Show help message and exit
attach:
fwtool attach <binary> <version> <output> [options]
fwtool attach <binary> <version> --in-place [options]
Argument / Option Description
binary Path to raw input binary file
version Firmware version string (e.g. 1, 1.2, 1.2.3)
output Path to output file
--in-place Modify the input file directly instead of writing output
--header-size N Total header size in bytes; must be a power of 2 (default: 512)
edit:
fwtool edit <binary> <version> <output> [options]
fwtool edit <binary> <version> --in-place [options]
Argument / Option Description
binary Path to packaged binary file with existing header
version New firmware version string
output Path to output file
--in-place Modify the input file directly instead of writing output
--header-size N Total header size in bytes; must be a power of 2 (default: inferred from file)
inspect:
fwtool inspect <binary> [options]
Argument / Option Description
binary Path to packaged binary file
--json Emit machine-readable JSON output
verify:
fwtool verify <binary> [options]
Argument / Option Description
binary Path to packaged binary file
--json Emit machine-readable JSON output
--quiet Suppress output; use exit code only
--header-size N Total header size in bytes; must be a power of 2 (default: inferred from file)
Examples
Attach a new header to a raw binary
fwtool attach firmware.bin 1.2.3 packaged.bin
This creates:
packaged.bin = [512-byte header][firmware.bin firmware]
Attach with a custom header size
fwtool attach firmware.bin 1.2.3 packaged.bin --header-size 1024
Replace the header of an existing packaged binary
fwtool edit packaged.bin 1.2.4 updated.bin
This keeps the firmware but replaces the header with updated metadata.
Replace the header in place
fwtool edit packaged.bin 1.2.4 --in-place
This modifies packaged.bin directly.
Attach a header in place
fwtool attach firmware.bin 1.2.3 --in-place
This replaces the raw input file with a packaged binary containing the header.
Print header contents
fwtool inspect packaged.bin
Example output:
magic: b'XLAB'
version: 1.2.3
size: 123456 bytes
crc32: 0x1a2b3c4d
Print header contents as JSON
fwtool inspect packaged.bin --json
Example output:
{
"magic_ascii": "XLAB",
"magic_hex": "584c4142",
"version": {
"major": 1,
"minor": 2,
"patch": 3,
"string": "1.2.3"
},
"size": 123456,
"crc": {
"int": 439041101,
"hex": "0x1a2b3c4d"
}
}
Verify a packaged binary
fwtool verify packaged.bin
Example output:
magic: OK
version: 1.2.3
header size: 512 bytes
size: OK (header=123456, actual=123456)
crc32: OK (header=0x1a2b3c4d, actual=0x1a2b3c4d)
verification: OK
Verify quietly using only the exit code
fwtool verify packaged.bin --quiet
echo $?
Exit code meanings:
0: verification passed
1: verification failed
Verify with JSON output
fwtool verify packaged.bin --json
Example output:
{
"ok": true,
"magic_ok": true,
"size_ok": true,
"crc_ok": true,
"header_size": 512,
"header": {
"magic_ascii": "XLAB",
"magic_hex": "584c4142",
"version": {
"major": 1,
"minor": 2,
"patch": 3,
"string": "1.2.3"
},
"size": 123456,
"crc": {
"int": 439041101,
"hex": "0x1a2b3c4d"
}
},
"firmware": {
"size": 123456,
"crc": {
"int": 439041101,
"hex": "0x1a2b3c4d"
}
}
}
Header size
The header size is configurable and defaults to 512 bytes.
Rules:
- must be a power of two (16, 32, 64, 128, 256, 512, 1024, …)
- must be at least 16 bytes (the minimum to hold the metadata fields)
- ensures the application vector table following the header is correctly aligned
- For edit and verify, if --header-size is not specified, the tool infers the header size from the stored firmware size field:
header_size = file_size - stored_firmware_size
If inference fails (e.g. corrupted size field), use --header-size to specify the size explicitly.
Version handling
Accepted version formats:
1->1.0.01.2->1.2.01.2.3->1.2.3
Rules:
- missing components are filled with zero
- extra components are ignored
- each component must be in the range 0..255
Verification behavior
When fwtool verify is used, the tool checks:
- the magic field is
XLAB - the firmware size matches the size stored in the header
- the firmware CRC matches the CRC stored in the header
- The header itself is not included in the size or CRC calculation.
Exit codes
General operations
0on success- non-zero on failure
verify
0if verification succeeds1if verification fails
Common workflow example
- Build a raw firmware image:
arm-none-eabi-objcopy -O binary app.elf app.bin
- Attach metadata header:
fwtool attach app.bin 1.2.3 app_packed.bin
- Verify the packaged image:
fwtool verify app_packed.bin
- Program the combined image to flash:
st-flash write app_packed.bin 0x08004000
Notes for embedded use
If your firmware image is packaged with a prepended header, the application must typically be linked to execute after the reserved header region.
Example with the default 512-byte (0x200) header:
- metadata region starts at
0x08004000 - header size is
0x200(512 bytes) - application is linked to start at
0x08004200 - Then the combined image can be programmed at
0x08004000, and the application - firmware will land at the correct runtime address.
Development
Run tests
pytest -q
License
MIT License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fwtool-3.0.0.tar.gz.
File metadata
- Download URL: fwtool-3.0.0.tar.gz
- Upload date:
- Size: 82.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4d539920c2e47714e8b7229ee8404cc8c14eb5abb6845527341a4400ebfdded
|
|
| MD5 |
bb7853eb21323e6572fca864c660fbce
|
|
| BLAKE2b-256 |
7b3c21b5db44560e9663294d2856f76e12d5179f24858bca2705c5a6627b5438
|
Provenance
The following attestation bundles were made for fwtool-3.0.0.tar.gz:
Publisher:
publish_package.yml on ece-mohammad/fwtool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fwtool-3.0.0.tar.gz -
Subject digest:
f4d539920c2e47714e8b7229ee8404cc8c14eb5abb6845527341a4400ebfdded - Sigstore transparency entry: 1293804387
- Sigstore integration time:
-
Permalink:
ece-mohammad/fwtool@aaba620236c86e3a9bd5ec5778d91ab66a00d044 -
Branch / Tag:
refs/tags/v3.0.0 - Owner: https://github.com/ece-mohammad
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_package.yml@aaba620236c86e3a9bd5ec5778d91ab66a00d044 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fwtool-3.0.0-py3-none-any.whl.
File metadata
- Download URL: fwtool-3.0.0-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c44da9ac7d5bee2af101e62b7a81b4e80d478a53a2b8a274ad14c3c84da5cba1
|
|
| MD5 |
8fd024c2a815b99ac7b86cbe4c3fd98c
|
|
| BLAKE2b-256 |
cba94a845f3878a23e67e805de15fdab8b31c60ce6afffbc486c84a8b0ce45ad
|
Provenance
The following attestation bundles were made for fwtool-3.0.0-py3-none-any.whl:
Publisher:
publish_package.yml on ece-mohammad/fwtool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fwtool-3.0.0-py3-none-any.whl -
Subject digest:
c44da9ac7d5bee2af101e62b7a81b4e80d478a53a2b8a274ad14c3c84da5cba1 - Sigstore transparency entry: 1293804438
- Sigstore integration time:
-
Permalink:
ece-mohammad/fwtool@aaba620236c86e3a9bd5ec5778d91ab66a00d044 -
Branch / Tag:
refs/tags/v3.0.0 - Owner: https://github.com/ece-mohammad
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_package.yml@aaba620236c86e3a9bd5ec5778d91ab66a00d044 -
Trigger Event:
push
-
Statement type: