はりぼて OS で ELF なアプリを起動してみた

2K Views

November 26, 16

スライド概要

はりぼて OS を改造して ELF バイナリを起動します。
ELF の内部構造や、好きな ELF 構造を生成するためのリンカスクリプトの書き方を説明し、はりぼて OS のどこを改造すればよいか示します。

profile-image

サイボウズ・ラボ株式会社で教育向けのOSやCPU、コンパイラなどの研究開発をしています。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

はりぼて OS で ELF な アプリを起動してみた @uchan_nos 2016/11/26 第4回自作OSもくもく会

2.

はりぼてOSによるアプリ起動 • デモ

3.

はりぼてOSによるアプリ起動 • コンソールにコマンドを打ち込むと起動する • 裏では: • カーネルが.hrbファイルをメモリに配置 • アプリ用のデータ領域確保やESP設定 • エントリポイントにジャンプ

4.

hrb ファイルフォーマット(p.460) ヘッダ .text .data off 0x00 0x04 0x08 0x0C 0x10 0x14 0x18 0x1C 0x20 意味 stack+.data+heap の大きさ “Hari” mmarea の大きさ ESPの初期値(= .data の転送先) .data のサイズ .data の初期値のファイル内オフセット 0xE9000000 エントリアドレス-0x20 heap 領域開始アドレス

5.

ロード後のメモリ配置 stack ヘッダ .text .hrbファイル そのまま .data .bss .data Segm 1 heap Segm 2 ESP初期値

6.

はりぼてOSのプログラムローダ(ELF化前) console.c

7.

実行可能形式いろいろ • COM • 実行時のメモリイメージそのままの形式 • PE • Windowsの標準 • a.out • Unix最初期の形式。TEXT, DATA, BSSだけをもつ • COFF • オブジェクトファイル形式として有名 • ELF • Executable and Linking Format

8.

ELF : Executable and Linking Format • 現在の Unix, Linux での標準 • オブジェクトファイル、実行可能ファイルどちらもOK • ツールチェーンで良くサポートされている • a.out や COFF に比べ柔軟。新しい言語にも対応できる • →これは ELF に対応するしかないでしょ!

9.

ELF ファイルフォーマット ELFヘッダ • ELFヘッダ、SHT 、PHT 、セクション群からなる • SHT:セクション・ヘッダ・テーブル • PHT:プログラム・ヘッダ・テーブル • セクション群:.text, .data, .bss, .shstrtab など • 詳しくは 『リンカ・ローダ実践開発テクニック』坂井弘亮, 2010 PHT セクション SHT

10.

ELF32ヘッダフォーマット ELFヘッダ PHT セクション SHT off field 意味 0x00 e_ident 先頭4バイトは 0x7f, ‘E’, ‘L’, ‘F’ 0x10 e_type ファイルタイプ ET_EXEC, ET_REL など 0x12 e_machine EM_386 など 0x14 e_version ファイルバージョン 0x18 e_entry エントリポイントのアドレス 0x1C e_phoff プログラムヘッダテーブルのファイル位置 0x20 e_shoff セクションヘッダテーブルのファイル位置 0x24 e_flags 未使用 0x28 e_ehsize ELF ヘッダサイズ 0x2A … 0x32 e_shstrndx セクション名格納用セクションの番号

11.

セクション・ヘッダ • .text、.data、.bss などのセクションを管理するヘッダ • コンパイラが出力したセクションをリンカがまとめる • セクション名 • セクションの属性(書き込み、実行) • セクションのロード先仮想アドレス • セクションデータのファイル位置とサイズ

12.

プログラム・ヘッダ • ローダが利用するセグメントを記述する • PHTは実行可能ファイルにしかない • セグメントのタイプ • セグメントのロード先仮想アドレス • セグメントデータのファイル位置とサイズ • セグメントのメモリ上のサイズ • セグメント属性(読み書き実行)

13.

ロードの仕様を考える • はりぼてOSでは、スタッ ク領域は.dataの前に置く • メモリ上では .text, .stack の先頭アドレスが両方と も 0 になって欲しい • .stack, .bss, .malloc はファイル上は0バイト 欲しい ELFファイル ELFヘッダ PHT .text .stack .data .bss .malloc SHT 欲しい メモリ配置 0番地 EXEC .text RW .stack .data .bss .malloc 0番地

14.

ELF用リンカ スクリプト EXECセグメントに ロードされる部分 ENTRY(_HariMain) INCLUDE ld_variables.lds SECTIONS { . = SIZEOF_HEADERS; .text : { *(.text) } "file offset of data" = .; . = 0; .stack : AT("file offset of data") { . = STACK; } .data : { *(.rodata*) *(.data) } .bss : { *(.bss) } .malloc : { . += MALLOC; } RWセグメントに ロードされる部分 }

15.

生成されるヘッダ構造 セクション .text .stack .data タイプ PROGBITS NOBITS PROGBITS ロード先 00000094 00000000 00000400 Offset 000094 00021c 00021c Size 000188 000400 000088 .bss .malloc NOBITS NOBITS 00000488 00000588 0002a4 0002a4 000100 00c000 セグメント LOAD LOAD LOAD Offset 000094 00021c 00021c 仮想アドレス 00000094 00000000 00000400 File Size 000188 000000 000088 ※これらは readelf -a hoge.elf で表示できるよ! Mem Size 000188 000400 00c188 Flag R E RW RW

16.

ローダの改造1 .elfファイルを探す console.c

17.

ローダの改造2 ELFのマジックナンバーを確認する …

18.

ローダの改造3 espの初期値を計算する

19.

calc_elf_esp .stackセクションがあれば、そのサイズを返す →.stackセクションが0番地から配置されることを暗に仮定している

20.

ローダの改造4 .dataのコピーとアプリ起動 p appsiz ELF EXEC ファイル 0*8+4 全体 q esp segsiz RW 1*8+4 .stack .data .bss .malloc

21.

calc_elf_datasize

22.

copy_elf_data

23.

ELFアプリ起動 デモ

24.

リンカスクリプト豆知識 location counter と virtual memory address (VMA) load memory address (LMA) の関係 参考:https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html

25.
[beta]
Location counter is . (dot)
SECTIONS
{
. = SIZEOF_HEADERS;
.text : { *(.text) }
"file offset of data" = .;
• VMA, LMA の現在値を保持するもの
• 値が加算されていくので「カウンタ」
• "file offset of data" はシンボル

26.

Virtual memory address (VMA) SECTIONS { . = SIZEOF_HEADERS; .text : { *(.text) } "file offset of data" = .; SIZEOF_HEADERS ファイルのヘッダの バイト数を返す リンカの組み込み変数 • VMAはそのセクションが配置されるメモリの仮想アドレス • 上記の例では “.text” の VMA は SIZEOF_HEADERS

27.

Virtual memory address (VMA) ヘッダ SIZEOF_HEADERS (= ELFヘッダ+PHT) バイト .text .data SHT "file offset of data"

28.

Load memory address (LMA) . = 0; .stack : AT("file offset of data") { . = STACK; } • VMA はファイル実行時のアドレス • 対して、LMA はファイルが読み込まれるアドレス • 今回、これを「ファイル内のオフセット」として使っている • 上記の例では “.stack” の VMA は 0、LMA は “.text” の直後

29.

. = 0; .stack : AT("file offset of data") { . = STACK; } .data : { *(.rodata*) *(.data) } .bss : { *(.bss) } Sect .stack .data .bss VMA 0 STACK STACK+sizeof(.data) LMA “file offset of data” “file offset of data” “file offset of data”+sizeof(.data) ATを指定しなければ、LMAに関して直前のATの効果が続く

30.

参考文献など • ELF化したはりぼてOSのソースコード https://github.com/osdev-jp/elf_haribote • 『リンカ・ローダ実践開発テクニック』坂井 弘亮, 2010 • ELFの仕様書 http://www.skyfree.org/linux/references/ELF_Format.pdf • LDのドキュメント https://sourceware.org/binutils/docs/ld/ • 3.1 Basic Linker Script Concepts • 3.6.8.2 Output Section LMA • はりぼてOSサポートページ内 “tools/obj2bim” http://hrb.osask.jp/wiki/?tools/obj2bim