Binary BIN File Merging Utility for Flash Layout Composition
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.
- The asterisk (
-
Input file list: A text file named
prj.txtcontaining one.binfilename 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.binfiles./b: Uses bare format (filenames only, no metadata).>: Overwritesprj.txtwith 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:
- Use a C compiler such as Microsoft Visual Studio (e.g., VS 2015 or newer).
- Set the build configuration to Release.
- Compile the source file—this generates a standalone
.exethat can be distributed.
Note: The exact behavior depends on project-specific flash layout rules. Adjust parsing logic or padding strategy as needed.