Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Binary BIN File Merging Utility for Flash Layout Composition

Tech May 14 1

Overview

This utility merges multiple binary (.bin) files into a single output file based on their designated flash memory offsets. It is particularly useful in embedded systems where firmware components—such as bootloadres, SPLs, or differential updates—are stored at specific addresses in flash memory. The tool reads a list of .bin files with embedded hexadecimal addresses in their names, places each file at its specified offset in the output image, fills gaps with 0xFF, and aligns the final size to 4KB boundaries.

Usage Requirements

  • File naming convention: <name>*0x<hex_address>.bin
    Example: bootloader*0x000000.bin

    • The asterisk (*) separates the descriptive name from the hex address.
    • Underscores (_) are not allowed in the name part; hyphens (-) are acceptable.
  • Input file list: A text file named prj.txt containing one .bin filename per line.

  • Tool executable: bin_merge_tool.exe (or equivalent compiled binary).

All files—.bin, prj.txt, and the executable—must reside in the same directory.

Generating the File List

Use the Windows Command Prompt to generate prj.txt:

dir *.bin /b > prj.txt
  • dir *.bin: Lists all .bin files.
  • /b: Uses bare format (filenames only, no metadata).
  • >: Overwrites prj.txt with the output.

To append enstead of overwrite, use >>:

dir *.bin /b >> prj.txt

Core Logic Implementation

The following C program implements the merging logic:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define BUFFER_SIZE      1024
#define PADDING_BYTE     0xFF
#define ALIGNMENT_SIZE   4096
#define MAX_FILES        100

int parse_addresses(const char* list_file, unsigned long long* addrs, int* count) {
    FILE* fp = fopen(list_file, "r");
    if (!fp) return -1;

    char line[BUFFER_SIZE];
    *count = 0;

    while (fgets(line, sizeof(line), fp) && *count < MAX_FILES) {
        line[strcspn(line, "\n\r")] = 0;

        char* sep = strchr(line, '*');
        if (!sep) continue;

        char* addr_start = sep + 1;
        char* ext = strstr(addr_start, ".bin");
        if (!ext) continue;
        *ext = '\0';

        errno = 0;
        unsigned long long addr = strtoull(addr_start, NULL, 16);
        if (errno) continue;

        addrs[(*count)++] = addr;
    }
    fclose(fp);
    return 0;
}

int main() {
    const char* list_name = "prj.txt";
    const char* out_name = "SPL-2MB_0x0.bin";

    unsigned long long addresses[MAX_FILES];
    int file_count = 0;

    if (parse_addresses(list_name, addresses, &file_count) != 0) {
        fprintf(stderr, "Failed to parse addresses from %s\n", list_name);
        return EXIT_FAILURE;
    }

    // Sort addresses and corresponding filenames if needed (omitted here for brevity)

    FILE* list_fp = fopen(list_name, "r");
    FILE* out_fp = fopen(out_name, "wb");
    if (!list_fp || !out_fp) {
        perror("File open error");
        return EXIT_FAILURE;
    }

    char line[BUFFER_SIZE];
    int next_idx = 1;

    while (fgets(line, sizeof(line), list_fp)) {
        line[strcspn(line, "\n\r")] = 0;

        char* sep = strchr(line, '*');
        if (!sep) continue;

        char bin_path[BUFFER_SIZE];
        strncpy(bin_path, line, sizeof(bin_path) - 1);
        bin_path[sizeof(bin_path) - 1] = '\0';

        char* addr_str = sep + 1;
        char* ext = strstr(addr_str, ".bin");
        if (!ext) continue;
        *ext = '\0';

        unsigned long long offset = strtoull(addr_str, NULL, 16);

        printf("Placing %s at 0x%llx\n", bin_path, offset);

        if (fseek(out_fp, offset, SEEK_SET) != 0) {
            perror("Seek failed");
            break;
        }

        FILE* bin_fp = fopen(bin_path, "rb");
        if (!bin_fp) {
            fprintf(stderr, "Cannot open %s\n", bin_path);
            continue;
        }

        size_t n;
        unsigned char buf[BUFFER_SIZE];
        while ((n = fread(buf, 1, sizeof(buf), bin_fp)) > 0) {
            fwrite(buf, 1, n, out_fp);
        }
        fclose(bin_fp);

        // Fill gap to next file (if any)
        if (next_idx < file_count) {
            long long curr_pos = ftell(out_fp);
            long long gap = (long long)addresses[next_idx] - curr_pos;
            if (gap > 0) {
                printf("Padding %lld bytes with 0xFF\n", gap);
                memset(buf, PADDING_BYTE, sizeof(buf));
                while (gap > 0) {
                    size_t write_size = (gap > BUFFER_SIZE) ? BUFFER_SIZE : (size_t)gap;
                    fwrite(buf, 1, write_size, out_fp);
                    gap -= write_size;
                }
            }
            next_idx++;
        }
    }

    fclose(list_fp);
    fclose(out_fp);

    // Final alignment padding
    out_fp = fopen(out_name, "rb+");
    if (!out_fp) return EXIT_FAILURE;

    long long size = ftell(out_fp);
    fseek(out_fp, 0, SEEK_END);
    size = ftell(out_fp);

    long long pad = (size % ALIGNMENT_SIZE) ? (ALIGNMENT_SIZE - (size % ALIGNMENT_SIZE)) : 0;
    if (pad > 0) {
        printf("Appending %lld bytes for 4KB alignment\n", pad);
        unsigned char buf[ALIGNMENT_SIZE];
        memset(buf, PADDING_BYTE, sizeof(buf));
        while (pad > 0) {
            size_t w = (pad > ALIGNMENT_SIZE) ? ALIGNMENT_SIZE : (size_t)pad;
            fwrite(buf, 1, w, out_fp);
            pad -= w;
        }
    }

    fclose(out_fp);
    return EXIT_SUCCESS;
}

Compilation

To build an executible:

  1. Use a C compiler such as Microsoft Visual Studio (e.g., VS 2015 or newer).
  2. Set the build configuration to Release.
  3. Compile the source file—this generates a standalone .exe that can be distributed.

Note: The exact behavior depends on project-specific flash layout rules. Adjust parsing logic or padding strategy as needed.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.