Converting from *.hex to *.bin for ARM on Linux

34k views Asked by At

I want to upload program to my STM32F4 Discovery board using st-flash command. Problem is when I try to upload *.hex or *.elf file it is just not working. I tried many ways ( like using xxd ) of converting from *.elf or *.hex to *.bin but it is still not working when I upload it. And yes, I tried uploading hex file from other Windows computer and it works.

Sample ( first three lines, just to show you how it looks inside ) of hex file:

:020000040800F2
:100000000000022099020008A1020008A5020008D1
:10001000A9020008AD020008B102000800000000BB

My OS is Ubuntu 14.04 LTS.

Thanks for help!

5

There are 5 answers

3
Adashi On

Have you considered using arm-none-linux-gnueabi-objcopy (or similar) instead of xxd? This can be found in any ARM toolchain.

7
Luis Colorado On

.hex file format is documented on the web. You need a loader program capable to understand it, as it has several kinds of registers to control the loading process. Some of the registers control entry point address. Others are data to be loaded at some fixed address.

You can get information at the wikipedia (I have found it there) for Intel Hex format (that's how it is called). If all the data is on only one segment and no entry point is specified, theoretically you can convert it to binary data to be loaded, but that's improbable.

It is a text file made of lines beginning with ':' character, then comes a two field hex number representing the number of bytes of data this record has, then the address this data is to be loaded on, then the type of file, it can be one of:

  • 00 This value is for a bunch of data, normally 16 bytes (0x10)
  • 01 End of file. It has no data, so always is codified as :00000001FF
  • 02 Extended segment address, to allow addresses with more than 16bit.
  • 03 Start Entry point address, to register the initial CS:IP address in 0x86 architecture.
  • 04 Extended Linear Address, to specify 32bit addresses. This specifies the upper 16bit address part of 00 registers.
  • 05 Start Entry point Linear Address. This is the 32 bit linear entry point address.

Then comes n bytes (n is the value of the first field) of data (hex coded) to be loaded and finally a checksum byte (the sum in two's complement of all the record bytes from the colon up).

1
Grzegorz Wegner On
arm-none-eabi-objcopy.exe -I ihex file.hex -O binary file.bin
4
A. Genchev On

I assume you have linux and you have installed binutils, so you just do:

objcopy --input-target=ihex --output-target=binary code00.hex code00.bin
0
Gabriel Staples On

I present a function below to allow this:

hex2bin path/to/myfirmware1.hex
hex2bin path/to/myfirmware1.hex path/to/myfirmware2.hex
hex2bin myfirmware1.hex myfirmware2.hex myfirmware3.hex myfirmware4.hex
# etc.

Note: get the latest versions of my hex2bin and hex2xxdhex functions in my eRCaGuy_dotfiles repo here: .bash_useful_functions.

Bash function to mass-convert Intel *.hex firmware files to *.bin firmware files, and to *.xxd.hex files for comparison in meld

To add onto @A. Genchev's answer: I find it tedious to have to have such a long command when I want to convert many hex files at once, so I wrote this helper function.

Copy and paste this into the bottom of your ~/.bashrc file, then run . ~/.bashrc to re-source your ~/.basrhc file and make this function available to you:

# Function to convert .hex firmware files to .bin
#
# Example usage:
#
#       # this produces "path/to/myfile.bin" from "path/to/myfile.hex"
#       hex2bin path/to/myfile.hex
#
#       # you can pass multiple paths at once too
#       hex2bin path/to/myfile1.hex path/to/myfile2.hex path/to/myfile3.hex
#
hex2bin() {
    # treat all input args as file paths
    for filepath_hex in "$@"; do
        # See: https://stackoverflow.com/a/965072/4561887
        filepath_hex_no_extension="${filepath_hex%.*}"
        filepath_bin="${filepath_hex_no_extension}.bin"
        # debugging
        # echo "filepath_hex_no_extension = $filepath_hex_no_extension"
        # echo "filepath_bin = $filepath_bin"
        echo "Converting \"$filepath_hex\" to \"$filepath_bin\"."

        objcopy --input-target=ihex --output-target=binary \
            "$filepath_hex" "$filepath_bin"
    done
}

# (Optional) add an alias prefixed with your initials so you can find all your
# custom aliases and functions easily by typing your initials followed by an
# underscore and hitting Tab Tab.
alias gs_hex2bin="hex2bin"

If you have a different toolchain, just replace objcopy with the version of objcopy from your toolchain. Ex: for the Microchip MPLAB XC32 compiler toolchain for PIC microcontrollers, use xc32-objcopy instead of objcopy.

Now, instead of this:

objcopy --input-target=ihex --output-target=binary \
    path/to/myfirmware1.hex path/to/myfirmware1.bin

objcopy --input-target=ihex --output-target=binary \
    path/to/myfirmware2.hex path/to/myfirmware2.bin

...you can do this:

hex2bin path/to/myfirmware1.hex path/to/myfirmware2.hex

You can pass in as many pathnames as you want all at once.

Going further: comparing and analyzing hex/binary file differences using objcopy, xxd, and meld

What if you want to compare two Intel hex firmware files to look for differences? Perhaps two hex firmware images are nearly identical, but differ only in some strings, IP addresses, or timestamps stored inside of them. That would be nice to know.

Here's how:

# - for "path/to/myfirmware1.hex", produce both "path/to/myfirmware1.bin"
#   and "path/to/myfirmware1.xxd.hex"
# - for "path/to/myfirmware2.hex", produce both "path/to/myfirmware2.bin"
#   and "path/to/myfirmware2.xxd.hex"
hex2xxdhex "path/to/myfirmware1.hex" "path/to/myfirmware2.hex"

# now compare the two `.xxd.hex` output files in `meld`
meld "path/to/myfirmware1.xxd.hex" "path/to/myfirmware2.xxd.hex"

Example output in meld:

enter image description here

enter image description here

Here is the definition of my hex2xxdhex function:

# Function to convert .hex firmware files to .bin and then to a
# human-compare-friendly .xxd.hex, so you can easily compare two files with
# `diff` or `meld`.
# - See my answer here: https://superuser.com/a/1790518/425838
#
# Example usage:
#
#       # this produces both "path/to/myfile.bin" and "path/to/myfile.xxd.hex"
#       # from "path/to/myfile.hex"
#       hex2xxdhex path/to/myfile.hex
#
#       # you can pass multiple paths at once too
#       hex2xxdhex path/to/myfile1.hex path/to/myfile2.hex
#       # then compare the two output ".xxd.hex" files with `meld`
#       meld path/to/myfile1.xxd.hex path/to/myfile2.xxd.hex
#
hex2xxdhex() {
    # treat all input args as file paths
    for filepath_hex in "$@"; do
        # See: https://stackoverflow.com/a/965072/4561887
        filepath_hex_no_extension="${filepath_hex%.*}"
        filepath_bin="${filepath_hex_no_extension}.bin"
        filepath_xxdhex="${filepath_hex_no_extension}.xxd.hex"
        echo "Converting \"$filepath_hex\" to \"$filepath_bin\" and to"\
            "\"$filepath_xxdhex\"."

        objcopy --input-target=ihex --output-target=binary \
            "$filepath_hex" "$filepath_bin"
        xxd "$filepath_bin" "$filepath_xxdhex"
    done
}
alias gs_hex2xxdhex="hex2xxdhex"

For a lot more detail, see my answer here: Super User: How to compare binary files, hex files, and Intel hex firmware files with meld