コンパイラの解析 (1) プログラムのリンクと実行.

Slides:



Advertisements
Similar presentations
Item 1:View C++ as a federation of languages. C++ はただの ”C のクラスがあるバージョン ” ではない → 例外安全 (29 項 ) 、テンプレート (41 項 ) 、オーバーロード等の導入によりデザインや目指すコードが 変化している プログラミング言語はあくまで言語.
Advertisements

1 組込みエンジニアのための Linux 入門 ダイナミックリンク編 (2) 株式会社アプリックス 小林哲之.
組込みエンジニアのためのLinux入門 ダイナミックリンク編(3)
2014年度 プログラミングⅡ ~ Cプログラミングやってみよう ~.
2015年度 プログラミングⅡ ~ Cプログラミングやってみよう ~.
情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
2013年度 プログラミングⅡ ~ Cプログラミングやってみよう ~.
データ構造とアルゴリズム 第10回 mallocとfree
情報工学基礎(改訂版) 岡崎裕之.
アルゴリズムとプログラミング (Algorithms and Programming)
システムプログラミング 第5回 情報工学科 篠埜 功 ヒアドキュメント レポート課題 main関数の引数 usageメッセージ
OSとコマンド OS:コンピュータを使うための基本プログラム コマンド:OS上で使用できる命令 OS本体であるカーネルの内部コマンド
記憶クラス 変数をどのような記憶領域に割り当てるかを指定するのが記憶クラス 記憶クラスには、自動変数、静的変数、外部変数などがある。
App. A アセンブラ、リンカ、 SPIMシミュレータ
2006/10/19 山下 諒蔵 佐藤 春旗 前田 俊行 大山 恵弘 佐藤 秀明 住井英二郎
フィーリングで読む 40種類のアセンブラ 坂井弘亮 (KOZOSプロジェクト) Twitter ID:kozossakai.
精密工学科プログラミング基礎 第9回資料 (12/11 実施)
ファイル操作と文字列の利用.
コンパイラの解析 (2) GCJのデータ構造 - 1.
型付きアセンブリ言語を用いた安全なカーネル拡張
プログラミング言語入門 手続き型言語としてのJava
言語プロセッサ2007 平成19年9月26日(水) (Ver.2 平成19年10月3日変更)
プログラミングの基礎 第2回 systemcall
コンパイラの解析 (3) クラスとインスタンスの初期化.
セキュリティ(3) 05A2013 大川内 斉.
関数の定義.
コンピュータ系実験Ⅲ 「ワンチップマイコンの応用」 第1週目 アセンブリ言語講座
デバッガ dbx の使い方.
2005年度 データ構造とアルゴリズム 第3回 「C言語の復習:再帰的データ構造」
第7回 プログラミングⅡ 第7回
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
東京工科大学 コンピュータサイエンス学部 亀田弘之
オペレーティングシステムJ/K (システムプログラミング)
実践ロボットプログラミング LEGO Mindstorms EV3 で目指せロボコン!
コンパイラ資料 実行時環境.
2013年度 プログラミングⅡ ~ はじめてのプログラミング ~.
オペレーティングシステムJ/K 2004年11月18日
精密工学科プログラミング基礎Ⅱ 第4回資料 今回の授業で習得してほしいこと: 文字列の扱い ファイル入出力の方法 コマンドライン引数の使い方
C言語を用いたマシン非依存な JITコンパイラ作成フレームワーク
オブジェクト指向プログラミングと開発環境
配列変数とポインタ 静的確保と動的確保 ポインタ配列 2次元配列 時間計測 第1回レポートの課題
2014年度 プログラミングⅡ ~ はじめてのプログラミング ~.
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
B演習(言語処理系演習)第2回 田浦.
第1章 いよいよプログラミング!! ~文章の表示 printf~
アルゴリズムとプログラミング (Algorithms and Programming)
プログラミング演習I 2003年4月30日(第3回) 木村巌.
参照されないリテラル 長谷川啓
プログラムが実行されるまで 2002年4月14日 海谷 治彦.
実装について 前田俊行.
計算機プログラミングI 木曜日 1時限・5時限 担当: 増原英彦 第1回 2002年10月10日(木)
オペレーティングシステムJ/K (システムプログラミング)
ネットワーク・プログラミング デバイスドライバと環境変数.
オブジェクト指向言語論 第五回 知能情報学部 新田直也.
ネットワーク・プログラミング Linuxシステムとソフトウェア開発.
情報工学科 3年生対象 専門科目 システムプログラミング 第3回 makeコマンド 動的リンクライブラリ 情報工学科 篠埜 功.
コンパイラ 第12回 実行時環境 ― 変数と関数 ― 38号館4階N-411 内線5459
情報工学科 3年生対象 専門科目 システムプログラミング 第3回 makeコマンド 動的リンクライブラリ 情報工学科 篠埜 功.
全体の流れ 画像ファイルを開き,画像データをメモリ上にロード メモリ上にロードした画像データに処理を加える
プログラミング言語論 第九回 理工学部 情報システム工学科 新田直也.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
プログラミング言語論 第九回 理工学部 情報システム工学科 新田直也.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
情報処理Ⅱ 2005年11月25日(金).
プログラミング演習II 2003年12月10日(第7回) 木村巌.
情報処理Ⅱ 小テスト 2005年2月1日(火).
岩村雅一 知能情報工学演習I 第7回(後半第1回) 岩村雅一
プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)
第1章 文字の表示と計算 printfと演算子をやります.
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
Presentation transcript:

コンパイラの解析 (1) プログラムのリンクと実行

Table of Contents プログラムはどうやって動くか リンカのコマンド gdb libgcj

プログラムはどうやって動くか メモリ上にプログラムを展開して、プログラムカウンタをプログラム開始位置に指定する その他、レジスタの初期化、ワークの確保など だれが、どうやって、どんなプログラムをメモリ上に配置する?

プログラムをメモリ上に配置 Hello.exeやa.outなどは実行バイナリと呼ばれる 実行ファイルをプログラムローダに渡して、メモリ上に展開してもらう ローダにあった実行バイナリを作れば、プログラムは実行できる

コンパイラ・ドライバ gccやclは「コンパイラ・ドライバ」 コンパイラ・ドライバは次の作業を行う 実行バイナリを生成するところまで一気に行う コンパイラ・ドライバは次の作業を行う コンパイル -> コンパイラの役目 アセンブル -> アセンブラの役目 リンク -> リンカの役目

コンパイラ・ドライバ (2) コンパイラの役割 アセンブラの役割 リンカの役割 ソースプログラムを、アセンブルファイルに変換 アセンブルファイルをオブジェクトファイルに変換 リンカの役割 複数のオブジェクトファイルをかき集めて実行バイナリに変換

コンパイラの役割 C言語などのプログラムを、ターゲットマシンのアセンブルプログラムに変換 gcc –S hello.c 関数名などは解決しない 高級言語->アセンブル言語へのトランスレータ gcc –S hello.c -> hello.s が作成される

コンパイラの作成するコード 一部省略 int main(int argc, char** argv) { .LC0: .string "Hello, world!" main: pushl %ebp movl %esp, %ebp subl $8, %esp andl $-16, %esp subl $28, %esp pushl $.LC0 call puts leave ret int main(int argc, char** argv) { puts("Hello, world!"); } 一部省略

アセンブラの役割 アセンブルプログラムをオブジェクトコードに変換 gcc –c hello.s as –o hello.o hello.s 同一ファイル内のシンボルはここで解決できる ファイルをまたぐシンボルはここでは解決しない gcc –c hello.s as –o hello.o hello.s どちらもhello.oを作成

オブジェクトファイルの解析 objdumpコマンドが便利 例 objdump –t hello.o : シンボルを表示 objdump –d hello.o : プログラムを逆アセンブル 例 objdump –d hello.o

objdump –d hello.o 未解決なので シンボルテーブルを参照している $ objdump -d hello.o hello.o: file format elf32-i386 Disassembly of section .text: 00000000 <main>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 08 sub $0x8,%esp 6: 83 e4 f0 and $0xfffffff0,%esp 9: 83 ec 1c sub $0x1c,%esp c: 68 00 00 00 00 push $0x0 11: e8 fc ff ff ff call 12 <main+0x12> 16: c9 leave 17: c3 ret 未解決なので シンボルテーブルを参照している

リンカの役割 オブジェクトコードをまとめて実行バイナリにする シンボルはこの時点で全て解決する gcc hello.o ld hello.o

リンクエラー ld hello.o だとリンクできない! リンクにはシンボルの全ての情報が必要 $ ld hello.o ld: warning: cannot find entry symbol _start; defaulting to 08048094 hello.o(.text+0x12): In function `main': : undefined reference to `puts' リンクにはシンボルの全ての情報が必要

リンクに必要なもの _startシンボル putsシンボル プログラムエントリ 後述のcrt1.oに含まれる C言語標準関数 後述のlibc.so.6に含まれる

リンカのコマンド ld /usr/lib/crt1.o \ hello.o \ -dynamic-linker /lib/ld-linux.so.2 \ -lc \ /usr/lib/crti.o /usr/lib/crtn.o hello.oをリンクして実行可能にするだけで、これだけのものが必要

リンカのコマンド (1) ld /usr/lib/crt1.o \ hello.o \ -dynamic-linker /lib/ld-linux.so.2 \ -lc \ /usr/lib/crti.o /usr/lib/crtn.o

crt1.o (1) プログラムエントリのための_startを含む mainではなく_startからプログラムは開始 ただし、__libc_start_mainを経由 $ objdump -t /usr/lib/crt1.o | grep main 00000000 *UND* 00000000 main 00000000 *UND* 00000000 __libc_start_main

mainが無いときのエラー $ gcc nomain.c : undefined reference to `main‘ /usr/lib/crt1.o(.text+0x18): In function `_start': : undefined reference to `main‘ crt1.oをリンクする際のエラーなので、初心者には不親切?

crt1.o (2) 次の2つも呼び出す (どちらもlibcが持つ) プログラムの初期化、終了処理に使える __libc_csu_init: 実行前に呼び出す __libc_csu_fini: 実行後に呼び出す プログラムの初期化、終了処理に使える これらもリンクしないと実行できない

リンカのコマンド (2) ld /usr/lib/crt1.o \ hello.o \ -dynamic-linker /lib/ld-linux.so.2 \ -lc \ /usr/lib/crti.o /usr/lib/crtn.o

ld-linux.so.2 共有ライブラリを実行時にロードする ld -dynamic-linker /lib/ld-linux.so.2 ELF形式のバイナリ ld -dynamic-linker /lib/ld-linux.so.2 Linux版のダイナミックローダ 共有ライブラリを一つでも使用してたら必須 今回はputsを使ったので必須

-lc libc.soというC言語の標準ライブラリをリンク 実際に使われる際に動的にリンクされる putsを使うだけでもリンクが必要 前掲のld-linux.so.2の仕事

libc.soの実体 実はただのリンカスクリプト /lib/libc.so.6 の動的リンク /usr/libc_nonshared.aの静的リンク /* GNU ld script Use the shared library, but some functions are only in the static library, so try that secondarily. */ OUTPUT_FORMAT(elf32-i386) GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )

/lib/libc.so.6 標準関数の実体を持つライブラリ $ objdump -T /lib/tls/libc.so.6 | grep puts … 00508980 w DF .text 000001be GLIBC_2.0 puts

動的シンボル解決 Linux/i386では、シンボルを動的に解決するためのコードが自動で挿入される call puts@plt … jmp *(_GLOBAL_OFFSET_TABLE_+12) 最初は動的リンクを行う リンカを呼び出すプログラム 2回目以降はputsの実体を 呼び出す

/usr/lib/libc_nonshared.a 含まれる関数 __libc_csu_init _initを呼び出す __libc_csu_fini _finiを呼び出す そのほかにも色々と

リンカのコマンド (3) ld /usr/lib/crt1.o \ hello.o \ -dynamic-linker /lib/ld-linux.so.2 \ -lc \ /usr/lib/crti.o /usr/lib/crtn.o

crti.o, crtn.o _init, _finiを解決する __libc_csu_(init|fini)から呼び出される

_init()@crti.o callに続きが無い Disassembly of section .init: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 08 sub $0x8,%esp 6: e8 fc ff ff ff call 7 <_init+0x7> callに続きが無い これだとハングアップする?

crtn.oの意味 crtn.oの.initセクションをダンプしてみる Disassembly of section .init: 0: c9 leave 1: c3 ret _init()の続き crti.oと組み合わさって一つの関数init()

セクションのマジック セクションの結合 ld … crti.o crtn.o の順に並べると_init()は一つの関数として完成する 複数のオブジェクトにまたがる同一セクションは、リンカによって1箇所にまとめられる コマンドラインに指定した順序を保持する ld … crti.o crtn.o の順に並べると_init()は一つの関数として完成する crti.oとcrtn.oの間に.initセクションを持つオブジェクトをはさめば、_init()に任意のコードを追加できる

セクション オブジェクトコードはセクションごとにプログラムやデータを配置する セクションごとにまとめてメモリ上に配置される .text Read only, Executable, Initialized プログラムを配置する .data Read/Write, Initialized 初期化するデータ(グローバル変数など) .bss Read/Write 実行時に割り当てられるデータ(スタック) セクションごとにまとめてメモリ上に配置される

gdb (1) 実行バイナリの解析はGNU Debuggerが便利 プログラムの挙動を1命令ずつ追える gdb a.out ソースコードが手元になくても気合でトレースできる gdb a.out

gdb (2) – start 起動するとプロンプトが表示されて停止 $ gdb a.out GNU gdb Red Hat Linux (6.3.0.0-1.132.EL4rh) … (gdb) 起動するとプロンプトが表示されて停止 (gdb) 以降にgdbのコマンドを書く

gdb (3) – break _start _startでプログラムが停止するようにブレークポイントを設定 Breakpoint 1 at 0x804828c

gdb (4) – run プログラムを開始する (gdb) run Starting program: /home/arakawa/tmp/a.out Breakpoint 1, 0x0804828c in _start () 先ほど設定したブレークポイントにヒット

gdb (5) – x/i $pc プログラムカウンタ以降の命令を表示 Examine memory/4 Instructions 0x804828c <_start>: xor %ebp,%ebp 0x804828e <_start+2>: pop %esi 0x804828f <_start+3>: mov %esp,%ecx 0x8048291 <_start+5>: and $0xfffffff0,%esp Examine memory/4 Instructions $pc はプログラムカウンタの位置を保持している

gdb (6) – si 一命令だけ進める (gdb) si 0x0804828e in _start () Step Instruction

gdb (7) – display/i $pc 常に現在の命令を表示 Display Instruction 1: x/i $pc 0x804828e <_start+2>: pop %esi Display Instruction

gdb (8) – example こんな感じで次々と追える 0x080482a8 in _start () 1: x/i $pc 0x80482a8 <_start+28>: call 0x804827c (gdb) x/i 0x804827c 0x804827c: jmp *0x8049490 (gdb) x/2i *0x8049490 0x8048282: push $0x8 0x8048287: jmp 0x804825c (gdb) x/2i 0x804825c 0x804825c: pushl 0x8049484 0x8048262: jmp *0x8049488 (gdb) x/i *0x8049488 0x4a6b90 <_dl_runtime_resolve>: push %eax

Gdb (9) – q プログラムを終了させる Quit (gdb) q The program is running. Exit anyway? (y or n) y Quit

シンボル解決 シンボルはリンカが解決する 下記のようなプログラムでも“コンパイル”は可能 リンカが動くまでにシンボルが揃っていればよい int main(int argc, char** argv) { puts("Hello, world!"); }

libgcj GNU Java Compiler (gcj)が使用するJavaの実行時ライブラリ Java VM + Java APIをコンパイルしたもの これを外側から使用すれば、Javaコンパイラの作成が可能

java.lang.Math.sinの外部利用 (1) ちょっとしたルールさえ知っていれば、JavaのAPIをC言語からも使える 例:sin.c double _ZN4java4lang4Math3sinEd(double); int main() { printf("sin(3.14) = %lf\n", _ZN4java4lang4Math3sinEd(3.14)); }

java.lang.Math.sinの外部利用 (2) 実行例 で、ちょっとしたルールって? $ gcc sin.c -lgcj $ ./a.out sin(3.14) = 0.001593

libgcjの利用にあたって Javaの機能を全て実現するには、下記のことも考慮しなければならない クラスの登録 クラスの初期化 インスタンスの生成 ポリモーフィズムの実現 配列の扱い インスタンスの破棄 ガーベジコレクタとの調和 例外の処理 synchronizeの処理

続く ちょっとしたルールの解析方法 libgcjを外部から完全に利用するまでの作業 おそらく全3~5回くらい