別件記事でWin11上でサンプルプログラムのビルドをしましたが、そこで複数種類のオブジェクトファイルが生成されてました。実際にフラッシュへ書き込むのはその中の一種類なのですが、この際ROMに書き込めるオブジェクト形式についておさらいしておくべい、と考えました。お世話になっている割に忘れてる存在っす。
※「鳥なき里のマイコン屋」投稿順Indexはこちら
ROM(Flash)に書き込めるオブジェクトフォーマット
立派なOSの上でセルフ開発していても実行用のオブジェクトフォーマットというものがあります。それらとベアメタルで組み込み用のマイコンのROMに書き込むオブジェクトフォーマットには「決定的な違い」がある場合(無い場合もあるけど)があります。
立派なOS上の実行用のオブジェクトファイルは、実行時にOSのローダーがメモリ番地(多分仮想記憶上のですが)を割り当ててオブジェクト内の未解決の番地を決定してくれたり、ライブラリとのダイナミックリンクの最終的な処理をしてくれたりすることが多いと思います。実行ファイルとしてリンク済とは言っても、ここは何で、そこは何処といった意味が分かるハイレベルのオブジェクト・フォーマットで記述されとります。そのため、ディスク上で保持されている「実行」プログラムを単純にメモリにコピーしても動作しないのが普通だと思います。
一方組み込みマイコンの場合、ROMに書き込むときには全てのアドレスは解決済でないとなりません。物理メモリ番地なり相対アドレスなり表現方法は異なるものの、完全に決まってないとROM(通常Flashだけれども)に配置することができないです。所定のアドレスに置くべき単なるバイト列のレベルまで落ちているか、書き込みソフトでアドレスに1体1対応で配置できる程度になっていないとなりません。
今回別件記事で生成したオブジェクトファイル群
それぞれコメントすれば以下のようですかね。
-
- blink.bin、 ガチのバイナリファイル。ROMに書き込むべきバイト列そのもの
- blink.dis、 人間用に生成したテキストの逆アセンブルファイル。ROMに書き込むべきバイトはその中に記載されているが、人間用の情報に埋もれた状態
- blink.elf、 Linuxなどで標準的に使用されるオブジェクトフォーマット。バイナリとはいっても意味のあるレコード(可変長)の集まり。リンクのための情報もなども含まれる。
- blink.hex、ひと昔前のROMライタ(UV消去型のEPROMなど)へ書き込んむときの標準形式。多分、今でも工場にマスクROMを注文するときなどに使われるケド、だいたいマスクROM自体が絶滅危惧種。もしかして絶滅した?ファイルとしてはテキストフォーマット。ガチのバイナリでは落ちてしまうアドレス情報なども含み、またレコードのチェックサムなどもあり。
- blink.uf2、マイクロソフト社制定のFlash書き込み用のバイナリフォーマット。固定長レコード。ヘッダ内に付加情報がありアドレスやチェックも可能。データ部分はバイナリというよりペイロードの雰囲気。
代表選手がそろっているので、以下、順番に見ていきたいと思います。
blink.dis
このファイルは人間様用のテキストファイルです。通常ここからバイナリを取り出してROMに書き込むようなことはしません(私はしたことあるケド。)
「バイナリ者」はディスアセンブルリスト、リスティングファイル、マップファイルみたいな名称のテキストファイルの一族にお世話になっていることが多いです。なんだバイナリじゃないじゃん。
以下の例だと elf ファイルから objdump を使って生成するものとほぼほぼ一緒の筈。最初にどのセクションが何番地から始まってという情報から開始です。なお、こういう文脈で text というのは、機械語のコードが詰まっている領域ですので念のため。
さて、 blink.dis ファイルの中で実際にFlashに書き込む先頭を見つけました。かなり後ろの方ですね。なお、最初にelf32-littlearm などとあったので、ここでの数値はリトルエンディアンの数字です。4b32b500という1ワードは、0番地から置くと 00 b5 32 4b という順番デス。
blink.elf
つづいて、上のblink.disファイルの元になっているblink.elf ファイルです、多分。高級なフォーマットではありますが、バイナリなのでツール使わねば読めませぬ。機械語コード部分(text)をアセンブルする場合は以下で。
$ arm-none-eabi-objdump -d blink.elf
ここで一言。ELFなどと言われると、確固とした1種のフォーマットが存在するかのように思われガチですが結構ゆるゆるです。実体はプロセッサ・コアの種類や各種の理由に応じて変幻自在。通常GNU、binutil の中のツール(上記objdumpもその一つ)でいろいろ操作できるのですが、ターゲットのELFに合わせたツール(この場合はarm-none-eabi-objdump)を使わないとなりまへん。そういう「高級さ」があるので、Flash書き込みソフトウエアが直接ELFをハンドルすることは普通ないように思われます。
さてobjdumpで逆アセンブルしたリストの先頭部分に先ほどの4b32b500というコードが見つかりました。ここだけ見れば blink.dis と一緒ね。
blink.hex
さて次は blink.hex です。御存じの方も多いと思いますが、hexフォーマットには2種類あります。古のマイコンの2大巨頭の決定によるインテルHEXとモトローラHEXです。見分け方は簡単。どちらもテキストファイルなので、エディタで開いて行の先頭文字を見るだけです。
-
- : ならインテルHEX
- S ならモトローラHEX(行頭のSが印象的なのでモトローラSと呼ぶことが多いかも)
GNUのツールは両形式サポートしていた筈ですが、今回のRaspberry Pi PicoのオブジェクトについてはインテルHEXご採用でした。先頭番地に書き込まれるべき 4B32B500 はリトルエンディアンでバイト順になっているので00B5324Bになってます。
上記の最初の行とか:の直後の値とかはインテルHEXフォーマットの資料を見ないとなりません。しかし御本家の公式資料は既に失われてしまったようです。Wikipedia の Intel HEX の項を参照すると、フォーマットの解説とともに、アーカイブされたオリジナル・ドキュメントへのリンクなど辿れます。なお、調べた中では、以下の日本語ページがなかなか分かりやすかったです。ありがとうございます。
blink.uf2
さて次に登場の、uf2形式が今回実際にFlash書き込みに使われている形式です。バイナリ形式なので、読み解くのに何かツールを使わねばなりません。今回は素のhexdumpで内容を確認してみました。
$ hexdump -C blink.uf2
uf2(USB Flashing Format)は、その名のとおりUSB経由でFlash書き込みするためのフォーマットらしいです。1レコードは512バイトの固定長です。先頭の32バイト(上記の緑の細線で囲ってある部分)が各レコードのヘッダ部分です。ヘッダの先頭には4バイトのマジックナンバー2つが必ず書き込まれてます。
-
- 最初はアスキー文字で読むと UF2\n
- 2番目は16進数字で読むと 9E5D5157
次の476バイトはデータ領域らしいですが、普通256バイトしか載せてないらしいっす。最後の4バイトにまたマジックナンバーが来ます。詳細についてはマイクロソフト社のドキュメントが以下にあるのでご参照くだされませ。知らんけど。
単なるバイナリと違って、MD5チェックサムが付けられるとか、アドレスだけでなくブロック番号も持てたり近代的です。
blink.bin
最後に登場するのが、ガチのバイナリです。やはりhexdumpで内容を確認してみました。
$ hexdump -C blink.bin
ぞろぞろとバイト列が並んでます。HEXやUF2はそれでも書き込むべき番地情報などをファイル内部に含めることができたのですが、BIN形式では単なるバイトの羅列なので、どこに置くのかはファイルとは別ルートで知らせる必要があります。かなりメンドクセーです。その代わり形式は簡単。バイトの並び。
ついついツマラナイことに突っ込んでしまったな。