LoginSignup
3
2

More than 1 year has passed since last update.

12Stepで作る 組込みOS自作入門 3ndステップ ~静的変数の読み書き

Last updated at Posted at 2022-01-23

はじめに

・学習本:12ステップで作る 組込みOS自作入門
・ソースコード:こちら の「osbook_03.zip」
・ハードウェアマニュアル:こちら

物理メモリと仮想メモリ

・汎用OSではメモリ・マップを意識することは少ない。
 仮想メモリという機構が働くため、各アプリで必要なメモリ量を
 要求するだけでよい。(ex.アプリxは0x00000000~0x0000FFFFを使用)
 実際にデータを保存する物理メモリ( + HDD、SDD など)への紐づけには
 MMU(Memory Management Unit)を使用する。

・組込みプログラミングでは仮想メモリの機能は実装しない。
 そのため、メモリ・マップの意識が必要。

 仮想メモリの説明はこちらが役に立った。

ROMとRAM

・ROM(Read Only Memory)はプログラム側からの書き込みは不可
電源OFFでも内容が保持される。
 H8/3069Fは電気的に内容の書き換えが可能フラッシュROM

・RAM(Random Access Memory)はプログラム側からの書き込みは可能
電源OFFで内容は失われる。

・2章での説明で外部DRAMの接続などでもアドレスを指定することができる。
 しかし基盤の大きさを考慮して、組込み機器向けのCPUではROMとRAMが
 内蔵している場合が多い。(512KBのROMと16KBのRAM)

H8/3069Fのメモリマップ

今回はマニュアルのP82、モード5で動作している。
image.png

メモリと領域

CPUはメモリにあるプログラムしか実行しない。そのためCPUが
 プログラムを実行するためには、実行形式ファイルをメモリ上にコピーする。
 コピーは領域単位で行われる。

領域名 レジスタ
テキスト領域 CPUが実行する機械語コード
データ領域 初期値を持つ静的変数
BSS領域 初期値を持たない静的変数

・関数内で定義した変数が動的変数。それ以外の変数が静的変数。
・データ領域は値を定義しているので、実行形式ファイルの段階で
 値を保持しておく必要がある。
・BSS領域では値を定義していないので、実行形式の段階ではサイズ情報のみ
 保持して、プログラムの実行時にメモリ上に確保する。
 BSS領域の確保時に変数は「ゼロクリア」、ポインタは「NULL」に初期化
 するため、組込みシステムでサイズを抑えたい場合はBSS領域に格納する
 というテクニックもある。

セクション

・実行形式ファイル内には機械語の命令だけがただ羅列しているのではない。
 領域ごとに保持して、また領域の先頭に付加情報である「ヘッダ」を持っている。
 
・ファイルの中身の確認。本実行形式はELF形式のため、以下のコマンドを実行。

$ readelf -a kzload.elf
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
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Renesas H8/300
  Version:                           0x1
  Entry point address:               0x100
  Start of program headers:          52 (bytes into file)
  Start of section headers:          1204 (bytes into file)
  Flags:                             0x810000
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         2
  Size of section headers:           40 (bytes)
  Number of section headers:         8
  Section header string table index: 5

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .vectors          PROGBITS        00000000 000074 000100 00  WA  0   0  4
  [ 2] .text             PROGBITS        00000100 000174 0002dc 00  AX  0   0  2
  [ 3] .rodata           PROGBITS        000003dc 000450 00001f 01 AMS  0   0  1
  [ 4] .data             PROGBITS        000003fc 000470 00000c 00  WA  0   0  4
  [ 5] .shstrtab         STRTAB          00000000 00047c 000038 00      0   0  1
  [ 6] .symtab           SYMTAB          00000000 0005f4 0003c0 10      7  44  4
  [ 7] .strtab           STRTAB          00000000 0009b4 000167 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), p (processor specific)

There are no section groups in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000074 0x00000000 0x00000000 0x003fb 0x003fb RWE 0x1
  LOAD           0x000470 0x000003fc 0x000003fc 0x0000c 0x0000c RW  0x1

 Section to Segment mapping:
  Segment Sections...
   00     .vectors .text .rodata
   01     .data

There is no dynamic section in this file.

There are no relocations in this file.

The decoding of unwind sections for machine type Renesas H8/300 is not currently supported.

Symbol table '.symtab' contains 60 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 SECTION LOCAL  DEFAULT    1 .vectors
     2: 00000100     0 SECTION LOCAL  DEFAULT    2 .text
     3: 000003dc     0 SECTION LOCAL  DEFAULT    3 .rodata
     4: 000003fc     0 SECTION LOCAL  DEFAULT    4 .data
     5: 00000000     0 FILE    LOCAL  DEFAULT  ABS vector.c
     6: 0000010a     0 NOTYPE  LOCAL  DEFAULT    2 .L1^B1
     7: 00000000     0 FILE    LOCAL  DEFAULT  ABS main.c
     8: 0000014c     0 NOTYPE  LOCAL  DEFAULT    2 .L2
     9: 00000000     0 FILE    LOCAL  DEFAULT  ABS lib.c
    10: 00000162     0 NOTYPE  LOCAL  DEFAULT    2 .L7
    11: 00000156     0 NOTYPE  LOCAL  DEFAULT    2 .L8
    12: 00000184     0 NOTYPE  LOCAL  DEFAULT    2 .L15
    13: 00000176     0 NOTYPE  LOCAL  DEFAULT    2 .L16
    14: 000001be     0 NOTYPE  LOCAL  DEFAULT    2 .L26
    15: 000001b6     0 NOTYPE  LOCAL  DEFAULT    2 .L21
    16: 000001b0     0 NOTYPE  LOCAL  DEFAULT    2 .L22
    17: 000001c0     0 NOTYPE  LOCAL  DEFAULT    2 .L17
    18: 0000019c     0 NOTYPE  LOCAL  DEFAULT    2 .L27
    19: 000001dc     0 NOTYPE  LOCAL  DEFAULT    2 .L34
    20: 000001d2     0 NOTYPE  LOCAL  DEFAULT    2 .L35
    21: 000001fa     0 NOTYPE  LOCAL  DEFAULT    2 .L38
    22: 000001ec     0 NOTYPE  LOCAL  DEFAULT    2 .L37
    23: 00000210     0 NOTYPE  LOCAL  DEFAULT    2 .L53
    24: 0000022c     0 NOTYPE  LOCAL  DEFAULT    2 .L50
    25: 00000226     0 NOTYPE  LOCAL  DEFAULT    2 .L45
    26: 00000220     0 NOTYPE  LOCAL  DEFAULT    2 .L46
    27: 0000022e     0 NOTYPE  LOCAL  DEFAULT    2 .L41
    28: 00000208     0 NOTYPE  LOCAL  DEFAULT    2 .L55
    29: 0000025e     0 NOTYPE  LOCAL  DEFAULT    2 .L57
    30: 00000258     0 NOTYPE  LOCAL  DEFAULT    2 .L60
    31: 00000252     0 NOTYPE  LOCAL  DEFAULT    2 .L61
    32: 0000026c     0 NOTYPE  LOCAL  DEFAULT    2 .L56
    33: 00000266     0 NOTYPE  LOCAL  DEFAULT    2 .L59
    34: 0000026a     0 NOTYPE  LOCAL  DEFAULT    2 .L58
    35: 00000244     0 NOTYPE  LOCAL  DEFAULT    2 .L63
    36: 00000290     0 NOTYPE  LOCAL  DEFAULT    2 .L65
    37: 000002bc     0 NOTYPE  LOCAL  DEFAULT    2 .L71
    38: 000002b0     0 NOTYPE  LOCAL  DEFAULT    2 .L72
    39: 000002ea     0 NOTYPE  LOCAL  DEFAULT    2 .L86
    40: 0000030e     0 NOTYPE  LOCAL  DEFAULT    2 .L75
    41: 00000000     0 FILE    LOCAL  DEFAULT  ABS serial.c
    42: 000003fc    12 OBJECT  LOCAL  DEFAULT    4 _regs
    43: 000003ae     0 NOTYPE  LOCAL  DEFAULT    2 .L4
    44: 00000202    50 NOTYPE  GLOBAL DEFAULT    2 _strcmp
    45: 00000000   256 OBJECT  GLOBAL DEFAULT    1 _vectors
    46: 00000276    46 NOTYPE  GLOBAL DEFAULT    2 _putc
    47: 000002a4    36 NOTYPE  GLOBAL DEFAULT    2 _puts
    48: 00000362    36 NOTYPE  GLOBAL DEFAULT    2 _serial_is_send_[...]
    49: 00000168    40 NOTYPE  GLOBAL DEFAULT    2 _memcpy
    50: 0000014e    26 NOTYPE  GLOBAL DEFAULT    2 _memset
    51: 00000100     0 NOTYPE  GLOBAL DEFAULT    2 _start
    52: 0000032c    54 NOTYPE  GLOBAL DEFAULT    2 _serial_init
    53: 000002c8   100 NOTYPE  GLOBAL DEFAULT    2 _putxval
    54: 000001e4    30 NOTYPE  GLOBAL DEFAULT    2 _strcpy
    55: 00000190    58 NOTYPE  GLOBAL DEFAULT    2 _memcmp
    56: 00000234    66 NOTYPE  GLOBAL DEFAULT    2 _strncmp
    57: 000001ca    26 NOTYPE  GLOBAL DEFAULT    2 _strlen
    58: 00000386    86 NOTYPE  GLOBAL DEFAULT    2 _serial_send_byte
    59: 0000010c    66 NOTYPE  GLOBAL DEFAULT    2 _main

Section Headers:

・ELF形式はファイルの内部をセクションという単位で区切っている。
 「.vectors」「.text」「.rodata」・・・

Symbol table '.symtab'

・各領域のメモリ上への配置位置
 例1:serial.cの配列regs[]はシンボル名は「_regs」、メモリ上で「0x000003fc」
   これは初期値を定義しているので、データ領域に配置される。
   確かに「.data」はメモリ上で「0x000003fc」に配置している。

 例2:main.cの関数main()は、メモリ上で「0x0000014c」
   これは関数(機械語の命令)を定義しているので、テキスト領域に配置される。
   確かに「.text」はメモリ上で「0x00000100」に配置している。

変数の配置

・自動変数はスタックに割り当てられる。
・静的変数は静的領域(データ領域 or BSS領域)に割り当てられる。

しかし、問題が...
・現状のソースでは静的変数を書きかえたい場合、ROMに配置されているので
 書き換えることができない。

int a = 5;

main(){
  a = 10; 
  ・・・
}

リンカ・スクリプト

CPUはメモリにある機械語しか実行しない。
 そのため、起動時に動作するプログラムはROM上に存在する必要がある。

メモリの配置はリンクの段階で実施される。
 リンクの際にどの部分をどのアドレスに配置するか定義する必要がある。
 → リンカ・スクリプトというファイルで指定する。実行形式ファイルを作成する際に、機械語コードや静的変数をどのアドレスに割り当てるか指示するためのファイル。

リンカ・スクリプトの説明

SECTIONS
{
    /* メモリの先頭番地から配置 */
    . = 0x0;

    /* 割込みベクタ*/
    .vectors : {
        vector.o(.data)
    }

    /*テキスト領域*/
    .text : {
        *(.text)
    }

    .rodata : {
        *(.strings)
        *(.rodata)
        *(.rodata.*)
    }

    /*データ領域*/
    .data : {
        *(.data)
    }

    /*BSS領域*/
    .bss : {
        *(.bss)
        *(COMMON)
    }
}

. = 0x0

・「.」はロケーションカウンタでカレント・アドレスを指す。

.vectors :

・「.vectors」というセクションを作成、その中にvector.oの
 .dataセクションの内容(割込みベクタの配列)を配置する。
 vector.oとは、vector.cをコンパイルしてできるオブジェクトファイル。
 

.text :

・「.text」というセクションを作成、その中に全てのオブジェクトファイル(*)の.textセクションの内容(機械語コード)を配置する。

.rodata :

・「.rodata」というセクションを作成。その中にConstで定義した文字列や
 文字列リテラルなどが配置される。
 文字列リテラルとは「"~"」で囲まれた文字列。

静的変数の配置

・現状、.dataと.bssセクションは.rodataの続きに配置される。
 つまりROMに配置されてしまう。

・では、書き込むときにRAM領域(0xffbf20~)から書き込みを開始すればよOK・・・?
 問題①:H8/3069Fの仕様としてフラッシュROMにしか書き込みできない。
 問題②:RAMに配置すると電源OFFで消える。

・問題①、②の対策
 一旦ROMに書き込んだ後に、実行時にRAMにコピー、その後はRAMを参照する。
 

物理アドレスと仮想アドレス

・物理アドレス・・・変数の初期値を配置するアドレス(Physical Address)
・仮想アドレス・・・プログラムが変数にアクセス際のアドレス(Virtual Address)
 
 セクションの「Program Headers:」以下の「VirtAddr」「PhysAddr」が該当

ELF形式のセクションとセグメントの違い

・セグメント・・・プログラムの実行時にはメモリ上に展開(ロード)するために参照
 セクション・・・リンク時に同じ内容の領域をリンカがまとめるために参照

BackNumber

1.12ステップで作る組込みOS自作入門 1stステップ
2.12ステップで作る組込みOS自作入門 2ndステップ

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2