OSの起動に必要な「ブートローダー」を自作してみようH8マイコンボードで動作する組み込みOSを自作してみよう!(3)(3/3 ページ)

» 2011年06月14日 11時37分 公開
[坂井弘亮,@IT MONOist]
前のページへ 1|2|3       
※本記事はアフィリエイトプログラムによる収益を得ています

5.2.ELF形式の展開

 次に、「ELF形式」について説明します。

 OSの実行モジュールは、実際に動作するときのベタなメモリイメージでファイルになっているわけではありません。目的ごとに幾つかの領域に分割されており、ヘッダが付加された状態でファイルになっています。

 このフォーマットには幾つかの種類があるのですが、現在はELF形式と呼ばれるものが主流になっています。図3は、ELF形式の構造です。


ELF形式のフォーマット 図3 ELF形式のフォーマット

 ELF形式は、モジュールの内部を「セクション」と「セグメント」という領域単位で管理します。セクションは「セクションヘッダ」、セグメントは「プログラムヘッダ」により管理されています。ブートローダーはセグメント単位で実行モジュールをメモリ上に展開します。

 例えば、先に作成した「Hello World」の実行モジュールも、ELF形式になっています。試しに解析してみましょう。ELF形式は「readelf」というコマンドで解析することができます。リスト7が「readelf」の出力結果です。

% readelf -a kozos
ELF Header:
  Magic:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, big endian
……(中略)……
 
Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00ffc020 000074 0003e4 00  AX  0   0  2
  [ 2] .rodata           PROGBITS        00ffc404 000458 000036 01 AMS  0   0  1
  [ 3] .data             PROGBITS        00ffc43c 00048e 00000c 00  WA  0   0  4
  [ 4] .bss              NOBITS          00ffc448 00049a 000020 00  WA  0   0  1
  [ 5] .shstrtab         STRTAB          00000000 00049a 000024 00      0   0  1
……(中略)……
 
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00ffbfac 0x00ffbfac 0x0048e 0x0048e R E 0x1
  LOAD           0x00048e 0x00ffc43c 0x00ffc43c 0x0000c 0x0002c RW  0x1
 
……(後略)……
リスト7 readelfによるHello Worldの解析

 リスト7では、まずELFヘッダの情報が表示され、さらにセクションヘッダ、プログラムヘッダの情報が表示されています。このため、ブートローダーがELF形式を解釈しメモリ上に展開することができれば、「Hello World」を実行することができることになります。

 リスト8は、「elf.c」からセグメント展開処理を抜粋したものです。「elf_load_program()」ではループによってプログラムヘッダを順次参照し、セグメント情報を取得して、「memcpy()」によりメモリ上に展開しています。これもやはり数十行で書くことができます。

/* セグメント単位でのロード */
static int elf_load_program(struct elf_header *header)
{
  int i;
  struct elf_program_header *phdr;
 
  for (i = 0; i < header->program_header_num; i++) {
    /* プログラム・ヘッダを取得 */
    phdr = (struct elf_program_header *)
      ((char *)header + header->program_header_offset +
       header->program_header_size * i);
 
    if (phdr->type != 1) /* ロード可能なセグメントか? */
      continue;
 
    memcpy((char *)phdr->physical_addr, (char *)header + phdr->offset,
           phdr->file_size);
    memset((char *)phdr->physical_addr + phdr->file_size, 0,
           phdr->memory_size - phdr->file_size);
  }
 
  return 0;
}
リスト8 ELF形式のセグメント展開部分

 「elf.c」は、「xmodem.c」と同じ94行で書かれています。表1を見ると、ブートローダーのソースコードの総量は678行です。簡単なブートローダーならば、この程度の行数で書けてしまうものなのです。

6.「Hello World」の仕組み

 今回起動した「Hello World」のソースコードは、表2のようなファイル構成になっています。

ファイル名 行数 内容
defines.h 11 各種定義
ld.scr 47 リンカスクリプト
lib.c 135 ライブラリ
lib.h 18 「lib.c」のヘッダファイル
main.c 26 メイン関数
serial.c 112 シリアルドライバ
serial.h 10 「serial.c」のヘッダファイル
startup.s 10 スタートアップ(アセンブラ)
合計 369 全ソースコードの総ステップ数
表2 Hello Worldのソースコード一覧

 実は、「Hello World」のソースコード自体は連載第1回で紹介した「Hello World」とほとんど同じです(ライブラリ関数が幾つか追加されている点と細かい差異を除けば)。大きな違いは、フラッシュROMに書き込んで電源ONで「Hello World」を直接起動しているのではなく、ブートローダーを介して起動している点です。そのため今回は、割り込みベクターの設定(vector.c)は不要になり(削除され)、さらにメモリマッピングの調整でリンカスクリプト(ld.scr)を修正しています。

 リスト9は、「Hello World」のリンカスクリプトです。本連載で利用している「H8/3069F」というマイコンは、16Kバイトの内蔵RAMを持っており、「0xffbf20」〜「0xffff20」というアドレスにマッピングされています。リスト9は、内蔵RAM上に「ram」という領域を定義し、テキスト領域やデータ領域は全てRAM上にマッピングしています。

OUTPUT_FORMAT("elf32-h8300")
OUTPUT_ARCH(h8300h)
ENTRY("_start")
 
MEMORY
{
        ramall(rwx)     : o = 0xffbf20, l = 0x004000 /* 16KB */
        ram(rwx)        : o = 0xffc020, l = 0x003f00
        stack(rw)       : o = 0xffff00, l = 0x000000 /* end of RAM */
}
 
SECTIONS
{
        .text : {
                _text_start = . ;
                *(.text)
                _etext = . ;
        } > ram
 
        .rodata : {
                _rodata_start = . ;
                *(.strings)
                *(.rodata)
                *(.rodata.*)
                _erodata = . ;
        } > ram
 
        .data : {
                _data_start = . ;
                *(.data)
                _edata = . ;
        } > ram
 
        .bss : {
                _bss_start = . ;
                *(.bss)
                *(COMMON)
                _ebss = . ;
        } > ram
 
        . = ALIGN(4);
        _end = . ;
 
        .stack : {
                _stack = .;
        } > stack
}
リスト9 Hello Worldのリンカスクリプト

7.次回

 今回はブートローダーを作成し、ブートローダーから「Hello World」を起動しました。次回は、簡単なタスク切り替えを実装してみることで、“組み込みOSの本質”に迫っていきたいと思います。お楽しみに! (次回に続く)


筆者紹介:

坂井弘亮(さかいひろあき)


某企業でネットワーク系の開発を行いながら、個人で組み込みOS「KOZOS」の開発や書籍/雑誌記事執筆、各種セミナーでの発表やイベント出展、勉強会の開催など多方面で活動中。IPAセキュリティ&プログラミングキャンプ 2010講師。著書は「12ステップで作る組込みOS自作入門」など多数。


モノづくりが大好き!(筆者Webサイト)

KOZOSのブログ(筆者ブログ)



前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.