リンカースクリプト入門

RISC-Vのベアメタル環境向けに実行ファイルを作りたかったが、 どうやってコードの配置を行うかわからなかったので、 既存のリンカスクリプトを参考にしつつリンカスクリプトを調べてみた。

参照するのは riscv-test-env に入っている、 ベアメタル環境向けの以下のスクリプト

OUTPUT_ARCH( "riscv" )
ENTRY(_start)

SECTIONS
{
  . = 0x80000000;
  .text.init : { *(.text.init) }
  . = ALIGN(0x1000);
  .tohost : { *(.tohost) }
  . = ALIGN(0x1000);
  .text : { *(.text) }
  . = ALIGN(0x1000);
  .data : { *(.data) }
  .bss : { *(.bss) }
  _end = .;
}

GNU ldはこのドキュメントを参照した。

リンカーの役割は、複数の入力オブジェクトファイルに含まれるセクションを、出力オブジェクトファイル(あるいは実行ファイル)のセクションに配置すること。 セクションは実行時に読み込まれる loadable と、実行時に確保されるが読み込むデータがない allocatable がある。 またどちらでもないセクションも存在し、デバッグ情報などを格納している場合もある。 loadable または allocatable なセクションは2つのアドレス、VMA(virtual memory address)とLMA(load memory address)を持つ。 たいていの環境では、どちらも同一だが、場合によってはROMに読み込まれて、実行時にコピーされるような環境も存在する。 またオブジェクトファイルは symbol table も含んでいる。symbol は名前とアドレスとその他情報を持っている。 C などは関数やグローバル変数に symbol を利用する。

まずは、OUTPUT_ARCH だが、これは出力マシンアーキテクチャを指定する。引数に与えられる名前は BFD ライブラリで使われる名前。 この辺を見ると良い気がする。

ENTRY はプログラムのエントリーポイントを指定する。引数は symbol 名。 スタートアドレスがあればなくても問題ないと思うが、どうやら ELF ヘッダーは entry point の指定ができるらしく、 そのために使われるらしい。

SECTIONS が一番重要なセクションの配置に関するコマンドになる。 SECTIONS のあとにはブレースを使って、複数の sections-command を並べる。 sections-command はいくつか種類があるが、今回使っているのは、symbol assignment と output section description である。

symbol assignment は symbol に対して、アドレスを指定する。 . は特別な symbol で location counter と呼ばれる。 location counter は出力される section のアドレスを示し、代入されると出力先のアドレスが移動する。 またセクションを配置するごとに、自動的に値が変更される。

. = 0x80000000; はこれよりあとのセクションが 0x80000000 に配置されるように設定するという意味になる。 . = ALIGN(0x1000);ALIGN 関数を呼び出した結果を、location counter に設定している。 ALIGN 関数は現在の location counter の値を、引数で指定したバイト数でアラインメントした次のアドレスを返す。 結果として、ここでは次の配置アドレスが 0x1000 単位に整列された値に設定されることになる

output section description はコロンの左辺で指定した出力先セクションに対して、右辺で指定した入力セクションを配置する意味になる。 左辺に指定するセクション名には曖昧性を除くために、スペースが必須になっている。

コロンの右側にはブレースの中に、output-section-command を書く。 output-section-command は input section description などがかける。

input section description は入力オブジェクトファイル名とその後にカッコで入力セクションを指定する。 それぞれワイルドカードを指定できる。

.text.init : { *(.text.init) }.text.init 出力セクションに対して、任意の入力ファイルの .text.init セクションを配置するという意味になる。