読者です 読者をやめる 読者になる 読者になる

【組込OS自作入門】3rdステップのメモ

ここではメモリ配置を設定するリンカスクリプトの書き方を学ぶ。
セクションとセグメントの区別がつくかどうかが鍵。

基本概念のお勉強

メモリ構成

メモリにはROMとRAMがある.

  • ROM
    • 読み込み専用でプログラムからの書き込み不可
    • 電源OFFでも内容が保持される
  • RAM
    • 読み書き可能
    • 電源OFFでも内容が失われる

H8は,512kbyteのフラッシュROMと16kbyteのRAMを内蔵している.

メモリと領域

メモリ上に展開されたプログラムは単に機械語コードだけが置かれているわけではなく,実際にはいくつかの領域から構成されている.

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

静的変数とは自動変数でないものを指す.
たとえば関数の外で定義された変数や,関数内部でstatic定義された変数など
静的領域=データ領域+BSS(Block Started by Symbol)領域。

セクション

gccが生成する実行形式ファイルのフォーマットをELF形式という.
ELF形式は実行ファイルの内部をセクションという単位で区切る.
.textセクションはテキスト領域に
.dataセクションはデータ領域に
.bssセクションはBSS領域に相当する.

リンカによって出力された実行形式ファイルを解析してみる

readelf -a kzload.elf

これを見ると,変数や関数がどのセクションに配置されてるかわかる.

変数の実体と配置

変数の実体はメモリに配置される.
メモリへの配置方法としては2通り

  • 自動変数は,スタックに割り当てる
  • 静的変数は,静的領域(データ領域もしくはBSS領域)に割り当てる

自動変数はRAM上のスタックにあるため,書き込みもできる.
問題は静的変数.

リンカスクリプト

リンカスクリプトは実行形式ファイルを作成するときに,機械語コードや制定領域をどののようにアドレス割当するのか,リンカに対して支持するためのファイルです.

  • ロケーション・カウンタ
    • 現在のカレント・アドレスを表す

例えば下記の意味するところは、

  • .vectorsというセクションを作成し、vectors.oの.dataセクションの内容をそこに配置する
  • .textというセクションを作成し、各オブジェクトファイルの機械語コードを.textセクションに配置する

など。

SECTIONS
{
	.vectors : {
		vector.o(.data)
	} > vectors

	.text : {
		_text_start = . ;
		*(.text)
		_etext = . ;
	} > rom

RAMとROMのアドレスにうまくデータを書き込むようロケーション・カウンタを使って配置していく作業
ちなみに,静的変数の配置には以下のような問題がある.

  • 変数本体をROMに置くと,書き込みができず
  • 変数本体をRAMに置くと,電源OFFで値が消えてしまう.

これを避けて静的変数を書き換え可能にするには以下の操作が必要

  • 変数の初期値はROMに保存する設定にしておく,h8writeでのプログラム転送時にROMに書き込む
  • 電源ONでプログラムを起動した際に,プログラムの先頭付近でROM上の変数の初期値をRAM上にコピーする
  • プログラムから変数にアクセスする場合には,RAM上のコピー先のアドレスに対してアクセスする

変数の初期値が配置されるアドレスと,プログラムが変数にアクセスするときのアドレスを異なるものにする必要がある.
前者を物理アドレス(PA:Physical Address)orロードアドレス(LA:Load Address)とよぶ.
後者を論理アドレス(LA:Logical Address)orリンクアドレス(LA:Link Address)or仮想アドレス(VA:Virtual Address)と呼ぶ.

ELF形式はセクションのほかにセグメントという管理単位も持つ.
プログラムの実行時に,セグメント情報が参照され,メモリ上に展開される.(ロード)

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

  • セクション
    • 実行形式ファイルを作成するリンカのためにある。
    • 個々のオブジェクトファイルを結合して実行形式ファイルを作成するために
    • セクションヘッダで管理されている
  • セグメント
    • セグメントはローダのためにある。
    • メモリ上に展開するときにセグメント情報のとおりに行う。
    • プログラムヘッダで管理されている
  • MEMORYコマンドによるメモリ領域の定義
  • SECTIONSコマンドによるセクション定義
    • あるセクションをどのメモリ領域に配置するか

その他の用語の定義

オブジェクトファイル

アセンブラによって出力される。
UNIXの場合、オブジェクトファイルは明確に区切られた下記6セクションで構成される

  1. オブジェクトファイルヘッダー
    • 該当ファイルの残りの部分のサイズと位置を示す。
  2. テキストセグメント
    • ソースファイルの中のルーチンに対応する機械語コードを保持する。
  3. データセグメント
    • ソースファイル中のデータの二進数表現を保持する。
  4. リロケーション情報
    • 絶対アドレスに依存する命令語およびデータ語を示す。
  5. シンボルテーブル
    • ソースファイル中の外部ラベルとアドレスを対応付け、未解決の参照先を列挙する
  6. デバッグ情報
    • プログラムをどのようにコンパイルしたかを詳しく記録する。それに基づいてデバッガは命令のアドレスとソースファイル中の行の対応関係を知り、データ構造を読める形で印刷できる。

便宜上、アセンブラでは各オブジェクトファイルは同じアドレス(たとえばロケーション0)から始まるものと想定する。
それをリンカが主記憶中に再配置(relocate)する。
その手助けをするために、アセンブラはリロケーション情報を出力する。

リンカ

個別にアセンブルされた機械語プログラムを組み合わせ,未解決のすべてのラベルの参照先を確定して実行ファイルを作成するシステム・プログラム
主に3つの機能がある。

  • プログラムが使用しているライブラリルーチンをプログラムライブラリの中から探し出す。
  • 各モジュールのコードの主記憶上のロケーションを決定し絶対アドレスを参照している命令の参照先アドレスを調整する
  • ファイル間にまたがる参照のアドレスを決定する

ちなみに個々のオブジェクトファイルも内部の形式は実行ファイルと同じ。
それぞれ.dataや.textを持っている

ローダ

実行形式のファイルのイメージをメモリ上に展開する作業を行うプログラム。
そのときにセグメント情報を見て、そのとおりにメモリ上に展開する

セクション
セグメント
領域