はじめに
・学習本: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のメモリマップ
メモリと領域
・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ステップ]
(https://qiita.com/Shohei_miyasako/items/9cf94b177ef39775ad28)