(読み物)kernel7.img の正体
本科目で使用しているRaspberry Pi 3は,電源を入れると,SDカード中のファイル kernel7.img の中身を主記憶の0x8000番地以降にコピーして,その先頭から実行を開始するように作られている. kernel7.img はメモリイメージと呼ばれる形式のファイルで,主記憶に配置したいバイト列(だけ)をそのまま格納したファイルである.逆に,第I部や第II部で実行可能ファイルとして使ってきたELFファイルは,主記憶に配置したいバイト列以外の各種の情報も格納したファイルである.これらの違いを見てみよう.
kernel7.img(ファイル名を変える前はlight.img)をビルドしたときの手順は以下の通りだった.
$ arm-none-eabi-as light.s -o light.o
$ arm-none-eabi-ld -m armelf light.o -o light.elf
$ arm-none-eabi-objcopy light.elf -O binary light.img
最初の2行は,第II部でも使ってきたアセンブルとリンクをするコマンドである. その結果作られたlight.elfには, 機械語プログラムそのものの他に,そのプログラムを起動する際にOS(正確にはローダ)が使用する各種の情報(主記憶のどの番地に配置するか,どの番地から実行開始するか,書き込みを禁止する領域や読み書き可能にする領域の範囲,など)が格納されている. 一方,light.imgでは,それらの付加情報が取り除かれて,純粋に機械語プログラム(とプログラム中のデータ)だけが格納される.
例えばファイルサイズを比べてみよう.
$ ls -l light.elf light.img
-rwxr-xr-x 1 user group 33476 Dec 18 00:00 light.elf
-rwxr-xr-x 1 user group 28 Dec 18 00:00 light.img
light.img は28バイトしかないのに対し,light.elf は数十KBもある.light.elf のプログラム部分だけ逆アセンブルすると,以下のようになる.
$ arm-none-eabi-objdump -d light.elf
light.elf: file format elf32-littlearm
Disassembly of section .init:
00008000 <_start>:
8000: e59f0010 ldr r0, [pc, #16] ; 8018 <loop+0x4>
8004: e3a01001 mov r1, #1
8008: e5801004 str r1, [r0, #4]
800c: e3a01b01 mov r1, #1024 ; 0x400
8010: e580101c str r1, [r0, #28]
00008014 <loop>:
8014: eafffffe b 8014 <loop>
8018: 3f200000 .word 0x3f200000
プログラムは7ワード,つまり28バイトしかないことがわかる. プログラム(命令列)以外の情報としては何が格納されているのだろうか. 例えば,上の実行結果を見るとプログラムの先頭番地が0x8000番地になっているが,この配置開始番地は機械語命令列以外の情報として格納されている.プログラムの実行開始番地(必ずしも先頭番地とは限らない)も格納されている. OSの上で実行可能ELFファイルを実行した際には,OSはそれらの情報に従ってプログラム(命令列)を主記憶に配置し,指示された番地から実行を開始する.
一方,light.imgの中身は以下のようになっている.
$ xxd -g1 light.img
00000000: 10 00 9f e5 01 10 a0 e3 04 10 80 e5 01 1b a0 e3 ................
00000010: 1c 10 80 e5 fe ff ff ea 00 00 20 3f .......... ?
(リトルエンディアンになっているが)上記の逆アセンブル結果に表示されている機械語命令列そのものだということがわかる. 配置開始番地もわからないし,実行開始番地もわからない.プログラム領域とデータ領域の区別もない.「0x8000番地に配置し,先頭から実行開始する」ことが決まっているので,それ以外の情報は不要だし邪魔というわけだ.
ELFのような複雑な構造のファイルは,中身を解析して必要な情報を取り出すのに手間が掛かるが,イメージファイルは中身をそのまま主記憶にコピーするだけでよい. ELFファイルを実行する際はOSがファイルの中身を解析しているが,そのOS自体を読み出して起動する仕組みにまでELFファイルを使う必要はなく,必要最小限の簡素な仕組みにした方が合理的だ.というわけで,OSそのもの(kernel7.img)はイメージファイルという簡素な形式で保存され,電源投入時に起動されるようになっている. 第III部では,Raspberry PI 3のOS起動の仕組みをそのまま使って自作プログラムを実行させようとしているので,自作プログラムのイメージファイルを作ってSDカードに置くわけだ.