Application to generate a patch between two binary files without compression. The differential algorithm is based on the bash command DIFF. It is available a Python application to encode (generate the patch) and decode (rebuild the new firmware) options. The decode is based on a C code.
The application works only on Linux operating system
It is available a script to build and configure the Python script
sh configure.sh
-
encode
python3 patch.py encode OLD_FW NEW_FW PATCH [OPTIONS]options:
-d : use diff with option minimal to reduce the patch size -e : use encode dict opcodes to reduce the patch size -v : verify che correctness of the patch -V : verify che correctness of the txt patch -t FILENAME: write the patch in txt format -b FILENAME: write the patch in binary format -r FILENAME: write the report in txt format -R FILENAME: write the report in csv format (if the file exists with currect header, append the report) -
decode
python3 patch.py decode OLD_FW PATCH NEW_FW [OPTIONS]options:
-v : enable verbose mode -r SIZE: set the read buffer size -p SIZE: set the patch buffer size
Are present two instructions: CPY to copy bytes from the old firmware, ADD to add new bytes. To save the bit to indicate the command in final patch it is assumed that the first instruction is a CPY then there is an alternance between ADD and CPY. To further reduce the patch size it is possible enable encoding of the most used opcodes, see section encoded opcodes for details. The patch is generated in three steps:
- textual patch
- binary patch in textual format
- final binary patch
For debugging purposes it is possible to generate the patch in the first two formats.
-
Patch txt
Textual patch generated where commands are explicitly written, fields are expressed in decimal format while bytes in hexadecimal. At the beginning are present two dictionaries to encode the most used opcodes, the first for ADD instructions, the second for CPY instructions. If the option -e is not used the dictionaries are empty.
CPY format:
0, <nbd>, <nbc>- 0: indicate CPY instruction
- nbd: number of bytes to jump in old firmware
- nbc: number of bytes to copy from old firmware in new firmware
ADD format:
1, <nba>, <hex[B1]>, <hex[B2]>, ... <hex[Bnba]>- 1: indicate ADD instruction
- nba: number of bytes to add in new firmware
- Bn: new bytes in hex format
Dictionaries format (at the beginning of the patch):
{"1,<neba1>,<hex[B1_1]>,<hex[B1_2]>,...<hex[B1_neba1]>": "<EA1>", "1,<neba2>,<hex[B2_1]>,<hex[B2_2]>,...<hex[B2_neba2]>": "<EA2>", ...} {"0,<nebd1>,<nebc1>": "<EC1>", "0,<nebd2>,<nebc2>": "<EC2>", ...}EADD format:
2, <EAn>- 2: indicate EADD instruction
- code: indicate which encode the instruction
ECPY format:
3, <ECn>- 3: indicate ECPY instruction
- code: indicate which encode the instruction
example of Patch txt
{"1,2,49,A7": "15", "1,1,91": "14", "1,2,6C,46": "13", [...]} {"0,2,2": "31", "0,1,3": "30", "0,1,5": "29", [..]"} 0,0,8 1,2,20,13 2,23 1,2,20,13 1,9,2A,76,A3,BC,87,9F,74,78,96 2,31 1,8,85,7C,AE,77,B7,39,B4,74 0,9,1 [...] -
Patch bin txt
It is the previous patch converted in binary and saved in textual format.
Header format:
<bin[endpatch]>,<bin[WNBD]>,<bin[WNBC]>,<bin[WNBA]>- endpatch: number of bits of the patch
- WNBD: define the number of bit reserved to <wnbd> field
- WNBC: define the number of bit reserved to <wnbc> field
- WNBA: define the number of bit reserved to <wnba> field
Dictionary format:
<bin[ND]> <bin[neba1-1]> <bin[B1_1]>,<bin[B1_2]>,...<bin[B1_neba1]>, ... <bin[nebd1]> <bin[nebd1]>, ...- ND: 4 LSB define the number of EADD, 4 MSB define the number of ECPY
- EADD:
- neba: fixed bit length to 2 (enba cannot be 0, so is written as enba-1 to gain one opcode)
- codes are implicitly defined by the order: first is WNBA-1, second is WNBA-2, ...
- ECOPY:
- enbd, enbc: fixed bit length to 4
- codes are implicitly defined by the order: first is WNBD-1, second is WNBD-2, ...
[Optional custom fw header]
It is possible to define a methodology to build the firmware header
CPY format:
0, <bin[wnbd]> <bin[nbd]>, <bin[wnbc]> <bin[nbc]>- 0: indicate CPY instruction
- wnbd: number of bit reserved to next <nbd> field
- nbd: number of bytes to jump in old firmware
- wnbc: number of bit reserved to next <nbc> field
- nbc: number of bytes to copy from old firmware in new firmware
ADD format:
1, <bin[wnba]> <bin[nba]>, <bin[B1]>, <bin[B2]>, ... <bin[Bnba]>- 1: indicate ADD instruction
- wnba: number of bit reserved to next <nba> field
- nba: number of bytes to add in new firmware
- Bn: new bytes in bin format
EADD format:
2, <bin[EADDn]>- 2: indicate EADD instruction
- EADDn: code in the dictionaries
ECPY format:
3, <bin[ECPYn]>- 3: indicate ECPY instruction
- ECPYn: code in the dictionaries
example of Patch bin txt:
100000111100010000001101,00000011,00000100,00000100 01011111 01 01001001 10100111,00 10010001, ... 0010 0010,0001 0011,0001 0101, ... [Custom fw header] 0,00000,0100 1000 1,0010 10,00100000,00010011 2,10111 1,0010 10,00100000,00010011 2,11111 1,0100 1000,10000101,01111100,10101110, ... 0,00100 1001,0001 1 1,0100 1001,00101010,01110110,10100011, ... [...]Notes
In
<bin[nbd]>,<bin[nbd]>and<bin[nbd]>fields the most significant bit is removed since is always 1. -
Final patch
The final patch is generated by the previous patch with the first bit of each instruction line eliminated (instruction bit). The patch is written in binary format
Due to the nature of two firmware versions, it is possible that some ADD or CPY instructions are repeated several times in the patch. To reduce the size it is possible to encode the most used opcodes in a dictionary. To make the encoding effective some limitation are applied:
- maximum 15 codes for each dictionary
- for EADD the maximum number of new bytes is 4
- for ECPY the maximum number of nbd and nbc is 15
The encoding exploits not used representation of the fields <wnba> anb <wnbd>. For example if WNBA=4 can be represented fields from 0 to 15, but if the maximum wnba used is 4, the values from 5 to 15 are not used. These values can be exploited to represent the codes in the dictionary, without using additional bits to indicate the encoding. If WNBA or WNBD does not have free values it is assessed the possibility to increase the value of WNBA or WNBD. The increasing is done only if there are advantages in terms of patch size.
It is possible to define a methodology to generate a header for the firmware.
In bpatch.py:
- set header_fw_size: size of the custom header in the firmware in bytes
- set header_patch_size: size of the custom header in the patch in bits
- set header_lines: number of lines of the custom header in the patch
- define write_header_custom: function that read the header of the firmware and produce the lines required to re-built the header in Patch bin txt
In bpatch.h
- enable MACRO HEADER_PATCH
In bpatch.c
- define write_header: function to re-built header for correspondent bytes from the patch
In the repository is present as example a custum header for SBSFU expansion for STM32, detailed here
Fields of CSV report:
- fw_size: size of new firmware in bytes
- patch_sz: size of patch in bytes
- CPY: number of CPY instructions
- ADD: number of ADD instructions
- NB: number of new bytes added
- ECPY: number of encoded CPY instructions
- EADD: number of encoded ADD instructions
- WNBD
- WNBC
- WNBA
It is present a test folder when the application can be tested and assessed on firmwares
Since bpatch is the result of an academic effort, we kindly ask that you acknowledge it by citing the following publication:
@article{bpatch,
title = {Incremental firmware update over-the-air for low-power IoT devices over LoRaWAN},
journal = {Internet of Things},
volume = {34},
pages = {101772},
year = {2025},
issn = {2542-6605},
doi = {https://doi.org/10.1016/j.iot.2025.101772},
url = {https://www.sciencedirect.com/science/article/pii/S2542660525002860},
author = {Andrea {De Simone} and Giovanna Turvani and Fabrizio Riente},
keywords = {Internet of Things, LoRaWAN, FUOTA, Incremental update, Low-power},
abstract = {Remote firmware updates in Internet of Things (IoT) devices remain a major challenge due to the constraints of many IoT communication protocols. In particular, transmitting full firmware images over low-bandwidth links such as Long Range Wide Area Network (LoRaWAN) is often impractical. Existing techniques, such as firmware partitioning, can alleviate the problem but are often insufficient, especially for battery-powered devices where time and energy are critical constraints. Consequently, physical maintenance is still frequently required, which is costly and impractical in large-scale deployments. In this work, we introduce bpatch, a lightweight method for generating highly compact delta patches that enable on-device firmware reconstruction. The algorithm is explicitly designed for low-power devices, minimizing memory requirements and computational overhead during the update process. We evaluate bpatch on 173 firmware images across three architectures. Results show that it reduces update payloads by up to 39,000×for near-identical updates and by 9–18×for typical minor revisions, eliminating the need to transmit full firmware images. Experimental results further demonstrate significant time and energy savings, with performance comparable to more complex alternatives. bpatch is released as open-source and, although demonstrated on LoRaWAN, the approach is flexible and can be adapted to other IoT communication technologies.}
}We use M.m.p as a verdioning system.
- M = Major
- m = minor
- p = patch
There is always back compatibility among minor version and patches only fix possible bugs