合并HEX文件

在上一篇文章 深入解析Intel HEX文件格式 中,我们详细介绍了Intel HEX文件的格式和记录类型。在嵌入式系统开发中,Intel HEX文件是一种常见的二进制数据表示格式,通常用于存储和传输固件。在某些情况下,我们可能需要将多个HEX文件合并为一个文件,例如将多个模块的代码合并为一个完整的固件。本文将详细介绍如何合并Intel HEX文件,并提供一个基于Rust的简单实现。

合并HEX文件的场景

在某些情况下,我们需要将多个HEX文件合并为一个文件。常见的场景包括:

  • 模块化开发:将多个模块的代码合并为一个完整的固件,比如包含boot loader的工程。
  • 固件升级:将多个更新包合并为一个完整的固件。
  • 硬件仿真:将多个HEX文件加载到硬件仿真器中。

合并HEX文件的挑战

合并HEX文件时,可能会遇到以下挑战:

  1. 地址冲突:多个HEX文件可能包含相同地址的数据,导致冲突。
  2. 记录类型处理:需要正确处理扩展地址记录(如扩展线性地址记录和扩展段地址记录)。
  3. 校验和计算:合并后的文件需要重新计算校验和。

合并HEX文件的步骤

合并HEX文件的基本步骤如下:

  1. 读取所有HEX文件:将每个HEX文件的记录解析为内存中的数据结构。
  2. 处理扩展地址记录:根据扩展地址记录调整数据的加载地址。
  3. 合并数据记录:将多个文件的数据记录合并为一个文件,确保地址不冲突。
  4. 生成新的HEX文件:将合并后的记录写入新的HEX文件,并重新计算校验和。

基于Rust的HEX文件合并工具

以下是一个基于Rust的简单HEX文件合并工具实现:

use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use std::path::Path;

fn main() -> io::Result<()> {
    // 定义要合并的HEX文件路径
    let hex_files = vec![
        "file1.hex",
        "file2.hex",
        // 添加更多的文件路径
    ];

    // 定义输出文件路径
    let output_file = "merged.hex";

    // 打开输出文件
    let mut output = File::create(output_file)?;

    // 遍历每个HEX文件
    for hex_file in hex_files {
        let path = Path::new(hex_file);
        let file = File::open(path)?;
        let reader = BufReader::new(file);

        // 逐行读取HEX文件并写入输出文件
        for line in reader.lines() {
            let line = line?;
            writeln!(output, "{}", line)?;
        }
    }

    println!("HEX files merged into {}", output_file);
    Ok(())
}

代码说明

读取HEX文件:使用BufReader逐行读取每个HEX文件。

写入输出文件:将每行记录写入新的HEX文件。

合并逻辑:该工具假设所有HEX文件的地址范围不重叠,适用于简单的合并场景。

处理复杂场景

如果HEX文件的地址范围可能重叠,或者需要处理扩展地址记录,可以按照以下步骤增强工具:

  • 解析扩展地址记录:在读取HEX文件时,解析扩展线性地址记录和扩展段地址记录。
  • 调整地址范围:根据扩展地址记录调整数据的加载地址。
  • 检查地址冲突:在合并数据记录时,检查地址是否冲突,并处理冲突。

python实现

以下是一个使用Python实现的简单HEX文件合并工具:

def parse_hex_record(line):
    """
    解析HEX文件的一行记录。
    返回一个字典,包含记录的长度、地址、类型、数据和校验和。
    """
    if not line.startswith(":"):
        raise ValueError("Invalid HEX record: record must start with ':'")

    # 解析记录字段
    record_length = int(line[1:3], 16)
    address = int(line[3:7], 16)
    record_type = int(line[7:9], 16)
    data = line[9:9 + 2 * record_length]
    checksum = int(line[9 + 2 * record_length:9 + 2 * record_length + 2], 16)

    return {
        "length": record_length,
        "address": address,
        "type": record_type,
        "data": data,
        "checksum": checksum,
    }


def merge_hex_files(file_paths, output_path):
    """
    合并多个HEX文件。
    :param file_paths: 要合并的HEX文件路径列表
    :param output_path: 合并后的输出文件路径
    """
    merged_records = []
    current_base_address = 0  # 当前基地址(用于处理扩展地址记录)

    for file_path in file_paths:
        with open(file_path, "r") as file:
            for line in file:
                line = line.strip()
                if not line:
                    continue

                record = parse_hex_record(line)

                # 处理扩展地址记录
                if record["type"] == 0x04:  # 扩展线性地址记录
                    current_base_address = int(record["data"], 16) << 16
                    continue
                elif record["type"] == 0x02:  # 扩展段地址记录
                    current_base_address = int(record["data"], 16) << 4
                    continue

                # 跳过文件结束记录(除了最后一个文件)
                if record["type"] == 0x01:  # 文件结束记录
                    if file_path != file_paths[-1]:
                        continue

                # 计算绝对地址
                absolute_address = current_base_address + record["address"]

                # 检查地址冲突
                for existing_record in merged_records:
                    if (
                        existing_record["address"] <= absolute_address < existing_record["address"] + existing_record["length"]
                        or absolute_address <= existing_record["address"] < absolute_address + record["length"]
                    ):
                        raise ValueError(f"Address conflict detected at address {hex(absolute_address)}")

                # 添加记录到合并列表
                merged_records.append({
                    "address": absolute_address,
                    "length": record["length"],
                    "data": record["data"],
                    "type": record["type"],
                })

    # 按地址排序记录
    merged_records.sort(key=lambda x: x["address"])

    # 写入合并后的HEX文件
    with open(output_path, "w") as output_file:
        for record in merged_records:
            # 计算校验和
            data_bytes = bytes.fromhex(record["data"])
            checksum = sum(data_bytes) & 0xFF
            checksum = (~checksum + 1) & 0xFF  # 取补码

            # 写入记录
            output_file.write(
                f":{record['length']:02X}{record['address']:04X}{record['type']:02X}{record['data']}{checksum:02X}\n"
            )

        # 写入文件结束记录
        output_file.write(":00000001FF\n")

    print(f"HEX files merged into {output_path}")


# 示例用法
if __name__ == "__main__":
    hex_files = ["file1.hex", "file2.hex"]  # 要合并的HEX文件路径
    output_file = "merged.hex"  # 合并后的输出文件路径
    merge_hex_files(hex_files, output_file)

代码说明

  1. parse_hex_record 函数

    • 解析HEX文件的一行记录,返回记录的长度、地址、类型、数据和校验和。
  2. merge_hex_files 函数

    • 合并多个HEX文件,处理扩展地址记录和文件结束记录。
    • 检查地址冲突,确保合并后的文件地址范围不重叠。
    • 将合并后的记录按地址排序,并写入输出文件。
  3. 地址冲突检查

    • 在合并过程中,检查每条记录的地址范围是否与已合并的记录冲突。
    • 如果发现冲突,抛出错误。
  4. 校验和计算

    • 每条记录的校验和通过计算数据字节的和的补码得到。
  5. 文件结束记录

    • 跳过除最后一个文件之外的所有文件结束记录。
    • 在合并后的文件末尾写入一个文件结束记录。

示例HEX文件

file1.hex
:10010000214601360121470136007EFE09D2190140
:00000001FF
file2.hex
:10020000314602360231470236008EFE09D3190240
:00000001FF
合并后的 merged.hex
:10010000214601360121470136007EFE09D2190140
:10020000314602360231470236008EFE09D3190240
:00000001FF

运行结果

运行上述代码后,merged.hex 文件将包含 file1.hexfile2.hex 的内容,且地址范围不冲突。

总结

合并Intel HEX文件是嵌入式系统开发中的常见任务。通过理解HEX文件格式和合并逻辑,可以轻松实现HEX文件的合并。本文提供了一个基于Rust的简单实现,并讨论了处理复杂场景的方法。希望本文能帮助你更好地理解和处理HEX文件!

rust库中有一个crate,用于开发中的合并情形基本够用,ihex-merge。这是个命令行工具,编译后只有 ~700KB,可以直接使用。注意,这个工具不支持段冲突的合并。

使用方法也非常简单:

ihex-merge output.hex in1.hex in2.hex in3.hex

后台回复 ihex 可以获取Windows二进制文件。

公众号 | FunIO
微信搜一搜 “funio”,发现更多精彩内容。
个人博客 | blog.boringhex.top

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值