Binary Analysis 06 · Intel HEX と Motorola SREC — アドレス付きテキスト形式

Chapter 06

Intel HEX と Motorola SREC — アドレス付きテキスト形式

この章のゴール.

Intel HEX と S-record の行の構造を手で読めるようになり、 アドレスとデータの対応、チェックサムの検算、.bin との相互変換ができるようになること。 「どのアドレスに何が書かれるか」を取り違えないこと。

この章で使う既出の用語(定義は各リンク先). メモリマップ(01 章 3 節)、オフセット(03 章 1 節)、チェックサム(03 章 8 節)、ブートローダ(04 章 5 節)

1. なぜテキスト形式があるのか

.bin は「アドレス 0 から順にバイトを並べただけ」なので、飛び飛びの領域(ブートローダは 0x08000000、設定は 0x08078000、その間は空き)を表せない。空きを 0xFF で埋めれば巨大になる。

Intel HEX と Motorola S-record(SREC) は、この問題を解くアドレス付きのテキスト形式である。 「このアドレスに、この数バイトを書け」というレコードの列で、飛び地を素直に表せる。書き込みツール・ブートローダ・製造装置が広く扱う。 中身は ASCII なので、テキストエディタでも開ける。

なぜアドレス付きテキスト形式か — 飛び地を表す.bin(アドレス 0 から詰めるだけ)Boot0xFF…0xFF…App0xFF…Cfg空きを 0xFF で埋めるので巨大になるHEX / SREC(レコードの列):10 08000000 … CC Boot:10 08008000 … CC App:10 08078000 … CC Cfg:00000001FF 終端「このアドレスに、この数バイトを書け」の列で飛び地を素直に表す書き込みツール・ブートローダ・製造装置が扱う。中身は ASCII でエディタでも開ける
.bin は飛び地を 0xFF で埋めるしかない。HEX / SREC はレコードの列で飛び地を表す

2. Intel HEX の 1 行

各行は : で始まり、すべて 16 進の文字である。

:10 8000 00 0004002 0C581000 8...  CC
 │  │    │  └ データ(Byte count 個のバイト)      └ チェックサム
 │  │    └ レコード種別(00=データ)
 │  └ アドレス(下位 16 ビット)
 └ Byte count(このレコードのデータ長)

実例:

:108000000004002 0C58100088180008838000 08 7A

を分解すると:

部分値意味
:—行の始まり
1016データが 16 バイト
80000x8000書き込み先アドレス(下位 16 ビット)
00—種別 = データレコード
0004002 0C5810008...—16 バイトのデータ
末尾 2 桁7Aチェックサム

レコード種別が重要である。

種別名前役割
00Dataデータ本体
01End Of Fileファイルの終わり(:00000001FF)
02Extended Segment Address以降のアドレス上位(×16)。古い
04Extended Linear Address以降のアドレスの上位 16 ビット。これで 32 ビット空間を表す
05Start Linear Address実行開始アドレス(エントリポイント)

アドレスの落とし穴: データレコードのアドレス欄は下位 16 ビットだけである。 上位は直前の 04 レコードで決まる。:020000040800F2 は「以降の上位 16 ビットは 0x0800」の意味で、続く :10 8000 ... は実アドレス 0x08008000 を指す。 04 レコードを見落とすと、アドレスを 0x8000 と誤読する。

Intel HEX の 1 行:10Byte count=168000アドレス下位16bit00種別=データ00 04 00 20 …(データ 16 バイト)7Achecksumレコード種別00 Data / 01 End Of Fileデータ本体・終端04 Extended Linear Address以降のアドレスの上位 16 ビットを決める05 Start Linear Address実行開始アドレスデータ行のアドレスは下位 16 ビットだけ。上位は直前の 04 レコードで決まる(見落とすと誤読)
「:」+ 長さ + アドレス下位 + 種別 + データ + チェックサム。上位アドレスは 04 レコード

3. チェックサムの検算

Intel HEX のチェックサムは、: を除く全バイトの和の下位 8 ビットの 2 の補数(和 + チェックサム = 0 mod 256)。

line = "108000000004002 0C58100088180008838000087A".replace(" ","")
b = bytes.fromhex(line)
assert (sum(b) & 0xFF) == 0        # データ + チェックサムを全部足すと 0

手で確かめるより、変換ツールが検算してくれる。壊れた行を探すときだけ手計算する。

4. Motorola S-record(SREC)

同じ発想の別形式。各行は S + 種別数字で始まる。

S3 15 08008000 0004002 0C5810008... CC
 │  │  │        │                    └ チェックサム
 │  │  │        └ データ
 │  │  └ アドレス(種別で幅が変わる)
 │  └ Byte count(アドレス+データ+チェックサムのバイト数)
 └ 種別
種別アドレス幅用途
S0—ヘッダ(ファイル名などのコメント)
S116 ビットデータ(小さいアドレス空間)
S224 ビットデータ
S332 ビットデータ(Cortex-M で普通)
S5 / S6—データレコードの個数
S7 / S8 / S932/24/16実行開始アドレス(終端)

S3 はアドレスがフル 32 ビットなので、Intel HEX のような「上位を別レコードで持つ」仕組みが要らず、1 行で実アドレスが読める。 チェックサムは「Byte count 以降の全バイトの和の 1 の補数(反転)」。

Motorola S-recordS3種別15Byte count0800800032bit アドレス…データ…CCchecksumS1 / S2 / S3 = 16 / 24 / 32 ビットアドレスのデータS3 が Cortex-M で普通。1 行で実アドレスが読めるS0 ヘッダ / S5・S6 個数 / S7・S8・S9 実行開始S3 はフル 32 ビットアドレスを 1 行に持つ(04 レコードのような上位管理が不要)
S + 種別で始まる。S3 は 32 ビットアドレスを 1 行に持つ

5. 変換 — srec_cat と objcopy

srecord パッケージの srec_cat が最強の変換・加工ツールである。

# HEX → BIN(空き region は指定した値で埋める)
srec_cat fw.hex -intel -o fw.bin -binary
# BIN → HEX(この BIN が置かれる先頭アドレスを指定するのが肝心)
srec_cat fw.bin -binary -offset 0x08000000 -o fw.hex -intel
# HEX → SREC
srec_cat fw.hex -intel -o fw.srec -motorola
# 情報表示(どのアドレス範囲が埋まっているか)
srec_info fw.hex -intel

srec_info の出力("Data: 08000000 - 0807FFFF" のような範囲一覧)は、メモリマップ作り(01 章)にそのまま使える。

objcopy でも変換できる。

arm-none-eabi-objcopy -I ihex -O binary fw.hex fw.bin
arm-none-eabi-objcopy -I binary -O ihex --change-addresses 0x08000000 fw.bin fw.hex

BIN → HEX で最も多い間違いは、オフセット(この BIN が焼かれる先頭アドレス)を指定し忘れて、アドレス 0 のイメージができてしまうことである。必ず -offset / --change-addresses を付ける。

6. Python で読む

from intelhex import IntelHex
ih = IntelHex("fw.hex")
print(hex(ih.minaddr()), hex(ih.maxaddr()))     # 埋まっている範囲
print(ih.segments())                             # 飛び地の一覧 [(start,end), ...]
data = ih.tobinarray(start=0x08008000, size=16)  # 特定アドレスのバイトを取り出す
ih.tobinfile("fw.bin")                           # BIN に落とす(空きは 0xFF 相当で詰まる)

bincopy ライブラリは HEX / SREC / TI-TXT を統一的に扱え、bincopy info fw.hex で範囲を表示できる。

7. UF2 — もう 1 つの配布形式

Raspberry Pi Pico や Adafruit のボードで使う UF2(USB Flashing Format)は、512 バイト固定のブロックの列で、各ブロックが書き込み先アドレスとデータを持つ点は HEX / SREC と同じ発想である。 マジックは 0x0A324655(UF2\n)で始まる。uf2conv.py で .bin と相互変換できる。08 章で触れる。

8. 手を動かす

HEX の 1 行を解剖する


この章のポイント