Skip to content

vlsi-nanocomputing/bpatch

Repository files navigation

bpatch

GitHub release (latest by date) DOI DOI

Overview

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.

Prerequisite

The application works only on Linux operating system

How to configure

It is available a script to build and configure the Python script

sh configure.sh

how to use

  • 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
    

bpatch structure

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:

  1. textual patch
  2. binary patch in textual format
  3. 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

Encoded opcodes

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.

Custom header

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

CSV report format

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

Test environment

It is present a test folder when the application can be tested and assessed on firmwares

Reference

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.}
}

Versioning

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

About

Application to generate a delta-patch between two binary files without compression

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages