pygbl
A parser for the EBL and GBL (v3 and v4) firmware image formats used by the Silicon Labs Gecko bootloader.
Images are parsed into structured tags, and round-trip byte-for-byte: anything the library reads it can write back unchanged, including the trailing data that some vendors append after the end tag.
Installation
pip install pygbl
The base install is pure Python and covers parsing, serializing, building images from ELFs, and LZMA. Signing, encryption and LZ4 need optional dependencies:
pip install pygbl[crypto] # signing and encryption
pip install pygbl[lz4] # LZ4 compression
pip install pygbl[all]
Calling those features without their dependency raises MissingDependencyError.
Usage
Parse an image and inspect its tags:
import pathlib
from pygbl import GBL3ApplicationInfo, parse_firmware_image
data = pathlib.Path("ncp-uart-hw.gbl").read_bytes()
image = parse_firmware_image(data)
for tag in image.tags:
print(tag)
print(image.get_first_tag(GBL3ApplicationInfo).version)
print(image.get_metadata()) # opaque bytes, the schema is vendor defined
assert image.serialize() == data
Modify an image. Tags are frozen dataclasses, so dataclasses.replace works, and
regenerate_crc fixes up the end tag afterwards:
import dataclasses
from pygbl import GBL3End, GBL3Metadata
modified = dataclasses.replace(
image,
tags=[t for t in image.tags if not isinstance(t, GBL3End)]
+ [GBL3Metadata(metadata=b'{"fw_type": "zigbee_ncp"}')],
).regenerate_crc()
Compress, encrypt and sign, in the order the bootloader expects:
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from pygbl import GBL3Compression
private_key = load_pem_private_key(
pathlib.Path("vendor_sign.key").read_bytes(), password=None
)
key = bytes.fromhex("7F8FE53979B31BC556FCB131AFF42414")
sealed = image.compress(GBL3Compression.LZMA).encrypt(key).sign(private_key)
pathlib.Path("signed.gbl").write_bytes(sealed.serialize(block_size=4))
And unwrap it again:
assert sealed.verify_signature(private_key.public_key())
assert sealed.decrypt(key).decompress().serialize() == image.serialize()
Building images from an ELF
Images can be built straight from a linked ELF, without commander. Program data comes
from the loadable segments (keyed by physical address, since initialized data is
stored in flash but linked at its RAM address) and the application info tag is read
from the SDK's application_properties_t struct:
from pygbl import build_application_gbl3, build_bootloader_gbl3
with open("zigbee_ncp.out", "rb") as f:
image = build_application_gbl3(f, metadata=b'{"fw_type": "zigbee_ncp"}')
with open("bootloader.out", "rb") as f:
bootloader = build_bootloader_gbl3(f)
Both reproduce commander gbl create byte-for-byte. Bootloader payloads carry a CRC32
of themselves, which build_bootloader_gbl3 appends for you.
GBLv4
Series 3 parts use GBLv4, a different format that nests tags inside a signed manifest
and can bundle several updates in one file. parse_firmware_image recognizes it by its
magic and returns a GBL4Image, which round-trips byte-for-byte like the rest:
from pygbl import GBL4Image, GBL4MemorySectionInfo, GBL4UpdateMemorySection
image = parse_firmware_image(pathlib.Path("light-simg301.gbl4").read_bytes())
assert isinstance(image, GBL4Image)
assert image.serialize() == data
# `get_tags` searches the whole tree, at any depth
for update in image.get_tags(GBL4UpdateMemorySection):
print(f"{update.target_address:#010x} {update.plain_image_size} bytes")
for info in image.get_tags(GBL4MemorySectionInfo):
print(info.compression_scheme, info.encryption_scheme, info.nonce.hex())
Reading and writing are supported; building a v4 image from an ELF is not, the ELF helpers are GBLv3 only.
EBL
EBL images, used by older EM3xx parts, work the same way:
from pygbl import EBLEraseProgram, parse_firmware_image
image = parse_firmware_image(pathlib.Path("ncp-uart-sw.ebl").read_bytes())
for tag in image.get_tags(EBLEraseProgram):
print(f"{tag.address:#010x} {len(tag.data)} bytes")
Bootloader and application images
A GBL can contain a bootloader, an application, or both. Combined images can be split apart and recombined, which is useful because some bootloaders cannot flash a combined image in one pass:
bootloader, application = combined.split_bootloader_app()
recombined = application.combine_bootloader_app(bootloader)
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 pygbl-1.0.0.tar.gz.
File metadata
- Download URL: pygbl-1.0.0.tar.gz
- Upload date:
- Size: 7.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1d85932d583519d1b311796048a2b1b27264b881ab773a156dae5f2800e1e92
|
|
| MD5 |
502149a982b76da29a1e2964490a2b35
|
|
| BLAKE2b-256 |
d25092a582eedf3a825fd4ac3ad7de7130b44e1fcad2ee94039ec3e34225c48f
|
Provenance
The following attestation bundles were made for pygbl-1.0.0.tar.gz:
Publisher:
publish-to-pypi.yml on zigpy/pygbl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygbl-1.0.0.tar.gz -
Subject digest:
d1d85932d583519d1b311796048a2b1b27264b881ab773a156dae5f2800e1e92 - Sigstore transparency entry: 2362918464
- Sigstore integration time:
-
Permalink:
zigpy/pygbl@f69642228c591770d5bf69c24b96f7d195d5ee4f -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/zigpy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@f69642228c591770d5bf69c24b96f7d195d5ee4f -
Trigger Event:
release
-
Statement type:
File details
Details for the file pygbl-1.0.0-py3-none-any.whl.
File metadata
- Download URL: pygbl-1.0.0-py3-none-any.whl
- Upload date:
- Size: 29.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
75590e5ab19240387a80d133a25ae46685ab6eb033d21c1b26e5ccf91a1b3e9e
|
|
| MD5 |
549ba732c82b7211fc39adc615c8ff36
|
|
| BLAKE2b-256 |
abf5e6b8f178ef8c7ff6dc3bdaea499e57cd2f03b01d9665d25e89db33abacc4
|
Provenance
The following attestation bundles were made for pygbl-1.0.0-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on zigpy/pygbl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pygbl-1.0.0-py3-none-any.whl -
Subject digest:
75590e5ab19240387a80d133a25ae46685ab6eb033d21c1b26e5ccf91a1b3e9e - Sigstore transparency entry: 2362918676
- Sigstore integration time:
-
Permalink:
zigpy/pygbl@f69642228c591770d5bf69c24b96f7d195d5ee4f -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/zigpy
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@f69642228c591770d5bf69c24b96f7d195d5ee4f -
Trigger Event:
release
-
Statement type: