プロセス、実行形式、リンク

					2009年12月01日
情報科学類 オペレーティングシステム II

                                       筑波大学 システム情報工学研究科 
                                       コンピュータサイエンス専攻, 電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/literacy-2009/2009-12-01
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/

■今日の大事な話

◆参考書

Claudia Salzberg Rodriguez, Gordon Fischer, and Steven Smolski: "The Linux Kernel Primer: A Top-Down Approach for x86 and PowerPC Architectures", Prentice Hall (2005). ISBN-10: 0131181637

青木 峰郎: "ふつうのコンパイラをつくろう", ソフトバンククリエイティブ (2009). ISBN-13: 978-4797337952

■オペレーティングシステムII

◆この授業の狙い

◆この授業を受ける意義

将来カーネルをいじる人もいじらない人にも意義がある。

◆メタレベルのプログラム

インタプリタ言語を考える。例:Ruby。
% ls -l hello.rb [←]
-rw-r--r--   1 yas  prof  26 Nov 28 17:24 hello.rb
% cat hello.rb [←]
printf("hello, world!\n")
% ruby hello.rb [←]
hello, world!
% []
Ruby 言語のインタプリタ自身は、C 言語で記述されている。

rubyのCのソース(メタ)、コンパイル&リンク、実行形式、プロセス、hello.rbソース(プログラム)

図? メタレベルのプログラミング

プログラム「hello.rb」の動きを変更する方法
プログラミング
hello.rb を書き換える
メタレベルのプログラミング
ruby インタプリタのプログラムを書き換える
「hello.rb」は、ruby インタプリタから見ると、入力「データ」の1つ。

◆メタレベルのプログラムとしてのOS

% ls -l hello.c [←]
-rw-r--r--   1 yas  prof  39 Nov 28 17:35 hello.c
% cat hello.c [←]
main() {
	printf("hello, world!\n");
}
% cc hello.c -o hello [←]
% ./hello [←]
hello, world!
% []

プロセス、カーネル(メタ)、ハードウェア(メタメタ)

図? メタレベルのプログラムとしてのOSカーネル

プログラム「hello.c」の動きを変更する方法
プログラミング
hello.c を書き換える
メタレベルのプログラミング
OSカーネルのプログラムを書き換える
「hello(実行形式)」は、OSカーネルから見ると、入力「データ」に相当する。

OSカーネル自身は、普通は、ハードウェアによるインタプリタ(CPU、入出力)で 実行される。インタフェースは、機械語命令や入出力機器への操作。

OSカーネルへのインタフェースは、システム・コール。 == OSカーネルは、システム・コールのインタプリタ。

(OSのメタレベルのプログラムとして「仮想計算機モニタ」を使うこともある。)

◆シラバス

◆Linux

教材としては、Linux を使う。 2009年12月現在、学類では orchid-calc[1-6] で使える。 近日中に速い viola0[1-6] が利用可能になる。 iMac からは ssh で接続してつかう。

■システム・コールとライブラリ

システム・コールとライブラリは、プログラマにとってのオペレーティング・ システム。共通点は、両方とも、C言語からは、関数呼出しの形で使えること。 2つを合わせて、API (Application Programming Interface) とも言う。

◆OSの構造

図? 3層、複数コンポーネント

図? OSの構造

プログラム・コードの場所

◆システム・コールのライブラリの違い

システム・コール(system call) ライブラリ(library)

システムコールの例:

ライブラリの例:

システムコールとライブラリの見分け方(Unix編)

同じUnix系OSでも、細かい所でシステム・コールとライブラリが入れ替わって いることもある。

システム・コールは、トラップ命令(trap instruction)を含む。 その部分は、アセンブリ言語。

ライブラリ関数は、大部分はC言語で書かれている。 printf() のソース・プログラムがある。

C言語で記述したプログラムは、文法さえあっていれば コンパイルはできる。 ライブラリ関数やシステムコールがないと動作しない。

◆リンク

ライブラリやシステム・コールは、コンパイルされて オブジェクト・コードの形で保存されている。 実行形式を作る時に取り出され、 アプリケーション・プログラマから作成した オブジェクト・コード(main()関数など)と結合(リンク)される。

◆printf() のソース・プログラム

~yas/os2/glibc-2.3.6/stdio-common/printf.c

<省略>
  27:	int
  28:	printf (const char *format, ...)
  29:	{
  30:	  va_list arg;
  31:	  int done;
  32:	
  33:	  va_start (arg, format);
  34:	  done = vfprintf (stdout, format, arg);
  35:	  va_end (arg);
  36:	
  37:	  return done;
  38:	}
<省略>
  42:	strong_alias (printf, _IO_printf);
VFPRINTF(P)                                                       VFPRINTF(P)
...
       int vfprintf(FILE *restrict stream, const char *restrict format,
              va_list ap);
...
DESCRIPTION
       The vprintf(), vfprintf(), vsnprintf(), and vsprintf() functions shall
       be  equivalent  to  printf(),  fprintf(),  snprintf(),  and  sprintf()
       respectively, except that instead of being called with a variable num-
       ber  of arguments, they are called with an argument list as defined by
       <stdarg.h>.

◆write()システム・コール

write.o の逆アセンブル結果より、主要部分抜粋。 
	push   %ebx		# ebxレジスタの内容をスタックへ退避
	mov    0x10(%esp),%edx	# 第3引数をレジスタ edx へ
	mov    0xc(%esp),%ecx	# 第2引数をレジスタ ecx へ
	mov    0x8(%esp),%ebx	# 第1引数をレジスタ ebx へ
	mov    $0x4,%eax	# システム・コールの番号 eax へ
	int    $0x80		# トラップ命令。カーネルへ制御を移す。
	pop    %ebx		# ebxレジスタの内容を回復
	cmp    $0xfffff001,%eax	# エラーが起きたら
	jae    __syscall_error	# __syscall_errorへジャンプ (errno等の保存)
	ret

■プロセス

プロセスとは、機械語のプログラム(実行形式)がOSカーネルによりメモリに 読み込まれて、CPUが割り当てられたら実行できるもの。

プログラムは、ハードディスクやCD-ROMに入っていることもある。 1つのプログラムから複数のプロセスが作られることがある。

◆Unixにおけるプロセスに関するシステム・コール

fork-execve がかなり特殊。 標準入出力のリダイレクションやパイプによるプロセス間通信がやりやすい。

◆Unixにおけるプロセスに関するライブラリ関数

◆psコマンド

u, l, x, a オプションを使えるようにしたい。
% ps [←]
  PID TTY          TIME CMD
20591 pts/1    00:00:00 tcsh
20761 pts/1    00:00:00 nm
20762 pts/1    00:00:00 lv
20793 pts/1    00:00:00 ps
% ps u [←]
USER       PID %CPU %MEM   VSZ  RSS TTY      STAT START   TIME COMMAND
yas      20591  0.0  0.1  9224 2696 pts/1    Ss   15:30   0:00 -tcsh
yas      20761  0.0  0.0  3724  904 pts/1    T    15:54   0:00 nm /usr/lib/libc.
yas      20762  0.0  0.0  2720  624 pts/1    T    15:54   0:00 lv
yas      20794  0.0  0.0  6280 1376 pts/1    R+   16:18   0:00 ps u
% ps l [←]
F   UID   PID  PPID PRI  NI   VSZ  RSS WCHAN  STAT TTY        TIME COMMAND
0  1013 20591 20590  15   0  9224 2696 rt_sig Ss   pts/1      0:00 -tcsh
0  1013 20761 20591  16   0  3724  904 finish T    pts/1      0:00 nm /usr/lib/l
0  1013 20762 20591  16   0  2720  624 finish T    pts/1      0:00 lv
0  1013 20795 20591  16   0  4256  628 -      R+   pts/1      0:00 ps l
% ps x [←]
  PID TTY      STAT   TIME COMMAND
20590 ?        S      0:00 sshd: yas@pts/1  
20591 pts/1    Ss     0:00 -tcsh
20761 pts/1    T      0:00 nm /usr/lib/libc.a
20762 pts/1    T      0:00 lv
20796 pts/1    R+     0:00 ps x
% []

◆fork()システム・コール

プロセスをコピーにより生成する。引数はない。結果が異なる。
コピー元のプロセス(親プロセス)
結果は、子プロセスのPID
新しく作られたプロセス(子プロセス)
結果は、0。

◆execve() を伴わないfork()を使うプログラムの例

   1:	/*
   2:	        fork-hello.c -- 画面に文字列を表示するプログラム
   3:	        ~yas/syspro/proc/fork-hello.c
   4:	        Start: 2001/05/13 23:19:01
   5:	*/
   6:	
   7:	#include <stdio.h>
   8:	
   9:	main()
  10:	{
  11:	        fork();
  12:	        fork();
  13:	        fork();
  14:	        printf("hello\n");
  15:	}
% make fork-hello [←]
cc     fork-hello.c   -o fork-hello
% ./fork-hello  [←]
hello
hello
hello
hello
hello
hello
hello
hello
% []

図? fork() を 3 回するプログラム

図? fork()システム・コールによるプロセスのコピー

◆execve()システム・コール

execve() は、プロセスをコピーしないが、プログラムを切り替える。次のような 引数を取る。

◆execve()によるdateコマンドの実行

   1:	/*
   2:	        exec-date.c -- 実行中のプロセスのプログラムをdateプログラムに切替える
   3:	        ~yas/syspro/proc/exec-date.c
   4:	        Start: 2001/05/13 23:25:49
   5:	*/
   6:	
   7:	#include <unistd.h>
   8:	
   9:	extern char **environ;
  10:	
  11:	main()
  12:	{
  13:	    char *argv[2], *path ;
  14:	        path = "/bin/date" ;
  15:	        argv[0] = "date" ;
  16:	        argv[1] = 0 ;
  17:	        execve( path,argv,environ );
  18:	        execve( path,argv,environ );
  19:	        execve( path,argv,environ );
  20:	}
% make exec-date [←]
cc     exec-date.c   -o exec-date
% ./exec-date  [←]
Mon Nov 30 12:57:19 JST 2009
% []

図? execve() を 3 回するプログラム

図? execve()システム・コールによるプログラムの実行

■実行形式

◆コンパイルとリンク

コンパイラは、高級言語で書かれたプログラムを、よりハードウェアに近い他 の言語に変換する。Cコンパイラは、C言語で記述されたプログラムを機械語コー ド(オブジェクト・プログラム)へ変換する。

コンパイルしたオブジェクト・プログラムだけではまだ足りない。 次のものを付け足す(リンクする)して、実行可能(executable) プログラムになる。

ライブラリ
プログラムの中でも基本的なもの、よく使われるものを、 個々のプログラムから独立させて、 さまざまなプログラムで共通で使えるようにしたもの。
システム・コール
オペレーティング・システム・カーネルを呼び出すためのスタブ。 カーネルへ制御を移すための命令(トラップ命令)を含む。
実行時ルーチン(runtime routine)
オブジェクト・プログラムを実行する時に必要になるプログラム。 コンパイラは、実行時ルーチンをあてにしてして、変換する。
これらは、C言語、他の高級言語、および、アセンブリ言語で記述されており、 予めコンパイル&アセンブルしてオブジェクト・プログラムにしてまとめて(アー カイブして)ある。

実行時ルーチンの役割

用語

リンク、リンケージエディット
ライブラリや実行時ルーチンをオブジェクト・プログラムに継ぎ足す
リンカ、リンケージエディタ
リンク作業を行うプログラム

ソース・プログラムからプロセスまで

図? エディタ、ソース・プログラム、コンパイラ、オブジェクト・プログラム、リンカ、実行型式、プロセス

図? ソース・プログラムらプロセスまで

ccコマンド

cc コマンド (gcc コマンド) は、単なるコンパイラではなく、アセンブラや リンカを実行する。

cc -v

% cc -v hello.c -o hello [←]
Reading specs from /usr/lib/gcc/i386-redhat-linux/3.4.6/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info --enable-shared --enable-threads=posix
--disable-checking --with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --enable-java-awt=gtk
--host=i386-redhat-linux
Thread model: posix
gcc version 3.4.6 20060404 (Red Hat 3.4.6-11)
/usr/libexec/gcc/i386-redhat-linux/3.4.6/cc1 -quiet -v hello.c -quiet
-dumpbase hello.c -auxbase hello -version -o /tmp/cchy6jGb.s ignoring
nonexistent directory 
"/usr/lib/gcc/i386-redhat-linux/3.4.6/../../../../i386-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /usr/lib/gcc/i386-redhat-linux/3.4.6/include
 /usr/include
End of search list.
GNU C version 3.4.6 20060404 (Red Hat 3.4.6-11) (i386-redhat-linux)
        compiled by GNU C version 3.4.6 20060404 (Red Hat 3.4.6-11).
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 as -V -Qy -o /tmp/ccYqFElf.o /tmp/cchy6jGb.s
GNU assembler version 2.15.92.0.2 (i386-redhat-linux) using BFD version 2.15.92.0.2 20040927
 /usr/libexec/gcc/i386-redhat-linux/3.4.6/collect2 --eh-frame-hdr -m
 elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o hello
 /usr/lib/gcc/i386-redhat-linux/3.4.6/../../../crt1.o
 /usr/lib/gcc/i386-redhat-linux/3.4.6/../../../crti.o
 /usr/lib/gcc/i386-redhat-linux/3.4.6/crtbegin.o
 -L/usr/lib/gcc/i386-redhat-linux/3.4.6
 -L/usr/lib/gcc/i386-redhat-linux/3.4.6
 -L/usr/lib/gcc/i386-redhat-linux/3.4.6/../../.. /tmp/ccYqFElf.o -lgcc
 --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s
 --no-as-needed /usr/lib/gcc/i386-redhat-linux/3.4.6/crtend.o
 /usr/lib/gcc/i386-redhat-linux/3.4.6/../../../crtn.o
% []

図? プリプロセッサ、コンパイラ、アセンブラ、リンカ

図? ccコマンドの背後で動いているプログラム

cc -E

プリプロセッサだけ動かして、結果を標準出力へ出す。
 % cat -n today.c
      1  #define TODAY   "Tuesday"
      2  main()
      3  {
      4          printf("Today is %s.\n",TODAY );
      5  }
 % cc -E today.c > today.i
 % cat today.i
 # 1 "today.c"
 # 1 ""
 # 1 ""
 # 1 "today.c"

 main()
 {
  printf("Today is %s.\n","Tuesday" );
 }
 % []
C言語のプリプロセッサは、C言語そのものではなく、マクロプロセッサ。
マクロ展開
定義に従い(単純な)文字列を(複雑な)文字列に置き換える
マクロプロセッサ
マクロ展開を行うプログラム
cpp コマンド(C言語のマクロプロセッサ)は、「#」で始まる行を解釈する。
 % cpp[←]
 #define apple orange[←]
 apple computer[←]
 ^D
 # 1 "<stdin>"
 # 1 "<built-in>"
 # 1 "<command line>"
 # 1 "<stdin>"

 orange computer
 % []
Cプリプロセッサの機能

cc -S

アセンブラを実行しないで、アセンブリ言語のプログラムを .s ファイルに残 す。
% cat hello.c [←]
main() {
        printf("hello, world!\n");
}
% cc -S hello.c [←]
% cat hello.s [←]
        .file   "hello.c"
        .section        .rodata
.LC0:
        .string "hello, world!\n"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        andl    $-16, %esp
        movl    $0, %eax
        addl    $15, %eax
        addl    $15, %eax
        shrl    $4, %eax
        sall    $4, %eax
        subl    %eax, %esp
        subl    $12, %esp
        pushl   $.LC0
        call    printf
        addl    $16, %esp
        leave
        ret
        .size   main, .-main
        .section        .note.GNU-stack,"",@progbits
        .ident  "GCC: (GNU) 3.4.6 20060404 (Red Hat 3.4.6-11)"
% []

cc -c

リンカを実行しないで、.o ファイルを作成した段階で止まる。
% cc -c hello.c [←]
% ls hello.? [←]
hello.c  hello.o
% []

リンクの観察

リンクは、ld というプログラムで行われることが多い(下の例は、collect2)。
% cc -v hello.o -o hello [←]
Reading specs from /usr/lib/gcc/i386-redhat-linux/3.4.6/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --
infodir=/usr/share/info --enable-shared --enable-threads=posix --disab
le-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwi
nd-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 3.4.6 20060404 (Red Hat 3.4.6-11)
 /usr/libexec/gcc/i386-redhat-linux/3.4.6/collect2 --eh-frame-hdr -m e
lf_i386 -dynamic-linker /lib/ld-linux.so.2 -o hello /usr/lib/gcc/i386-
redhat-linux/3.4.6/../../../crt1.o /usr/lib/gcc/i386-redhat-linux/3.4.
6/../../../crti.o /usr/lib/gcc/i386-redhat-linux/3.4.6/crtbegin.o -L/u
sr/lib/gcc/i386-redhat-linux/3.4.6 -L/usr/lib/gcc/i386-redhat-linux/3.
4.6 -L/usr/lib/gcc/i386-redhat-linux/3.4.6/../../.. hello.o -lgcc --as
-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-n
eeded /usr/lib/gcc/i386-redhat-linux/3.4.6/crtend.o /usr/lib/gcc/i386-
redhat-linux/3.4.6/../../../crtn.o
% []

様々なライブラリや実行時ルーチンが参照されている。

objdumpによる逆アセンブル

アセンブラは、アセンブリ言語で記述されたプログラムを 機械語に変換する。

逆アセンブラは、機械語をアセンブリ言語に戻す。 objdump コマンドに -d オプション(disassemble)を与えると逆アセンブルを行 う。

% cc -c hello.c [←]
% ls hello.o [←]
hello.o
% objdump -d hello.o [←]

hello.o:     file format elf32-i386

Disassembly of section .text:

00000000 
: 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: b8 00 00 00 00 mov $0x0,%eax e: 83 c0 0f add $0xf,%eax 11: 83 c0 0f add $0xf,%eax 14: c1 e8 04 shr $0x4,%eax 17: c1 e0 04 shl $0x4,%eax 1a: 29 c4 sub %eax,%esp 1c: 83 ec 0c sub $0xc,%esp 1f: 68 00 00 00 00 push $0x0 24: e8 fc ff ff ff call 25 29: 83 c4 10 add $0x10,%esp 2c: c9 leave 2d: c3 ret % []

◆gdbによる逆アセンブル

gdb (GNU Debugger) にも、逆アセンブルの機能がある。メモリ表示 x のコマ ンドに、オプションとして/i (instruction) を与える。

◆ELF(Executable and Linkable Format)

ELF は、Linux 等でよく使われている実行形式やオブジェクト・プログラムの 形式。(MacOSX では Mach-O 形式、古いUnixでは a.out 形式。)
% ls -l hello.c [←]
-rw-r--r--  1 yas prof 39 Nov 28 17:35 hello.c
% cc -c hello.c [←]
% ls -l hello.o [←]
-rw-r--r--  1 yas prof 884 Nov 28  2009 hello.o
% file hello.o [←]
hello.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
% cc hello.o -o hello [←]
% ls -l hello [←]
-rwxr-xr-x  1 yas prof 4719 Nov 28  2009 hello
% file hello [←]
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for 
GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped
% []
file コマンドで調べると ELF 形式のファイルは、ELF と表示される。

◆ELFの役割

◆ELFファイルの構造

ヘッダとセクションの並び

ELFのセクションの並び、

図? ELFファイルの構造

重要なセクション
.text 機械語命令
.data 初期化された大域変数やstatic変数
.rodata (.dataに置くべきもののうち)読み読み専用のもの)
.bss 初期化されない大域変数やstatic変数(OSが0に初期化)
.symtab シンボル・テーブル
.strtab 文字列のテーブル。シンボル・テーブルの内容の文字列。

◆readelfコマンド

readelf コマンドで ELF 形式のファイルを詳しく調べることができる。 objdump コマンドでも代替できることが多い。
-S オプション
セクションの一覧
-h オプション
ヘッダの表示
-s オプション
シンボルテーブルの表示

◆nmコマンド

nmコマンドを使うと、実行形式やオブジェクト・プログラムに含まれているシ ンボルを表示することができる。
% nm -t x hello | egrep 'main|printf' [←]
         U __libc_start_main@@GLIBC_2.0
08048368 T main
         U printf@@GLIBC_2.0
% nm -t x hello | egrep ' [tT] | [uU] | [dD] ' [←]
080494a0 D _DYNAMIC
0804956c D _GLOBAL_OFFSET_TABLE_
08049490 d __CTOR_END__
0804948c d __CTOR_LIST__
08049498 d __DTOR_END__
08049494 d __DTOR_LIST__
0804949c d __JCR_END__
0804949c d __JCR_LIST__
08049580 D __data_start
08048430 t __do_global_ctors_aux
08048308 t __do_global_dtors_aux
08049584 D __dso_handle
080483ec T __libc_csu_fini
08048398 T __libc_csu_init
         U __libc_start_main@@GLIBC_2.0
08048454 T _fini
08048278 T _init
080482c0 T _start
080482e4 t call_gmon_start
0804833c t frame_dummy
08048368 T main
08049588 d p.0
         U printf@@GLIBC_2.0
% []
nmの結果は、番地、型、シンボルの3カラム。 型としては、次のものが重要。
T
Text。機械語のコード。
D
Data。データ。
B
BSS。データ。
U
Undefined。未定義のシンボル。
W
weak。(weakではない)通常のシンボルで置き換えられる可能性があるもの。
型で大文字は、大域的、小文字は、局所的(static変数、static関数等)。

これからわかること。

◆libc

printf() 等の C 言語からよく使われるライブラリ関数は、libc (C library) に含まれている。Gnu 版の libc は、Glibc (GNU C Library)。 libc には、静的リンク用と動的リンク用がある。

静的リンク用の libc は、ar 形式のアーカイブになっている。 普通、ranlib コマンドにより索引が付けられている。

% (nm -s /usr/lib/libc.a | egrep '^printf in' > /dev/tty )>& /dev/null [←]
printf in printf.o
% ar x /usr/lib/libc.a printf.o [←]
% ls -l printf.o [←]
-rw-r--r--  1 yas prof 696 Nov 30 13:34 printf.o
% nm  printf.o [←]
00000000 T _IO_printf
00000000 T printf
         U stdout
         U vfprintf
% []
ar コマンドに x (extract) という引数を与えると、ファイルを取り出すこと ができる。nm コマンドでシンボルを調べている。

動的リンク用

% ldd hello [←]
        libc.so.6 => /lib/tls/libc.so.6 (0x00aee000)
        /lib/ld-linux.so.2 (0x00ad4000)
% nm -tx /lib/tls/libc.so.6 | egrep ' T printf$' [←]
00b30ca0 T printf
% []
ldd コマンドを使うと、利用する動的リンク・ライブラリが表示される。

◆静的リンクの実行

cc コマンドは、最近は多くの環境で標準で動的リンクを行う。 静的リンクを行わせるには、-staticオプションを付ける。
% cc hello.c -o hello [←]
% cc -static hello.c -o hello-static  [←]
% ls -l hello hello-static  [←]
-rwxr-xr-x  1 yas prof   4719 Nov 30  2009 hello
-rwxr-xr-x  1 yas prof 431829 Nov 30  2009 hello-static
% file  hello hello-static [←]
hello:        ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), 
for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped
hello-static: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), 
for GNU/Linux 2.2.5, statically linked, not stripped
% ./hello [←]
hello, world!
% ./hello-static  [←]
hello, world!
% []

◆静的リンクの結果

objdump -d で逆アセンブルしてみる。
% objdump -d hello-static | lv [←]
...
080481d8 <main>:
 80481d8:       55                      push   %ebp
 80481d9:       89 e5                   mov    %esp,%ebp
 80481db:       83 ec 08                sub    $0x8,%esp
 80481de:       83 e4 f0                and    $0xfffffff0,%esp
 80481e1:       b8 00 00 00 00          mov    $0x0,%eax
 80481e6:       83 c0 0f                add    $0xf,%eax
 80481e9:       83 c0 0f                add    $0xf,%eax
 80481ec:       c1 e8 04                shr    $0x4,%eax
 80481ef:       c1 e0 04                shl    $0x4,%eax
 80481f2:       29 c4                   sub    %eax,%esp
 80481f4:       83 ec 0c                sub    $0xc,%esp
 80481f7:       68 68 11 09 08          push   $0x8091168
 80481fc:       e8 9b 0a 00 00          call   8048c9c <_IO_printf>
 8048201:       83 c4 10                add    $0x10,%esp
 8048204:       c9                      leave  
 8048205:       c3                      ret    
 8048206:       90                      nop    
 8048207:       90                      nop    
...
08048c9c <_IO_printf>:
 8048c9c:       55                      push   %ebp
 8048c9d:       89 e5                   mov    %esp,%ebp
 8048c9f:       8d 45 0c                lea    0xc(%ebp),%eax
 8048ca2:       50                      push   %eax
 8048ca3:       ff 75 08                pushl  0x8(%ebp)
 8048ca6:       ff 35 14 6e 0a 08       pushl  0x80a6e14
 8048cac:       e8 0b de 00 00          call   8056abc <_IO_vfprintf>
 8048cb1:       c9                      leave  
 8048cb2:       c3                      ret    
 8048cb3:       90                      nop    
リンカ(リンケージ・エディタ)は、オブジェクト・プログラムを結合しながら、 このような番地の変更を行う。

◆静的リンクにおけるリンカの動作

  1. 利用者が指定したオブジェクト・プログラムとライブラリに含まれている オブジェクト・プログラムをセクションごと(.text, .data, .bbsごと)に結合 する。これで、最終的な番地が決まる。
  2. シンボルを解決する。jump命令、call 命令、変数アクセス等の番地を、 最終的な番地のものにする。

結合、穴埋め。

図? リンカの動作

◆動的リンクの方法

基本的には、静的リンクと同じようなことを、プログラムがメモリにロードさ れた後に行う。いくつか難しいことがある。

動的リンクのライブラリは、普通、複数のプロセスで共有される。プロセ スごとに利用するライブラリが異なるので、ライブラリがロードされる番地も 異なる。どの番地にロードされてもよいコードが必要になる。そのようなコー ドは、再配置可能コード(relocatable code)やposition independent code (PIC) と呼ばれる。

◆静的リンクと動的リンクの比較

■クイズ1 プロセス、実行形式、リンク

★問題(101) psコマンド

ps コマンドで次のような表示がなされた。
% ps l [←]
F   UID   PID  PPID PRI  NI   VSZ  RSS WCHAN  STAT TTY        TIME COMMAND
0  1013 22963 22962  16   0  9456 2432 rt_sig Ss   pts/2      0:00 -tcsh
0  1013 23098 22963  16   0 12312 5900 finish T    pts/2      0:00 ls -l
0  1013 23101 22963  17   0  4096  628 -      R+   pts/2      0:00 ps l
% []
PID23098 のプロセスが実行される時に、execve() システムに対してどのような引数が渡されたと考えられるか。ただし、 ls コマンドのプログラムの実行形式は、/bin/ls にあるもの とする。

★問題(102) nmコマンド

あるC言語で記述されたプログラムを cc -c でコンパイルしてnm コマンドで表 示したら次のような結果になった。
% cc -c  f.c [←]
% nm f.o [←]
00000000 T f
         U g
% []
この元の C 言語のプログラム f.c の内容としてどのようなものが考えられるか。 3行以内の簡単なプログラムを書きなさい。

★問題(103) 静的リンク

次のような C 言語のプログラムを考える。
foo() {
    bar();
    bar();
}

bar() {
}
これをコンパイルし、静的リンクを行った結果の一部を以下に示す。
00000034 <foo>:
 0000034:       55                      push   %ebp
 0000035:       89 e5                   mov    %esp,%ebp
 0000037:       83 ec 08                sub    $0x8,%esp
 000003a:       e8 [   (a)   ]          call   0000048 <bar>
 000003f:       e8 [   (b)   ]          call   0000048 <bar>
 0000044:       c9                      leave  
 0000045:       c3                      ret    
 0000046:       90                      nop    
 0000047:       90                      nop    

00000048 <bar>:
 0000048:       55                      push   %ebp
 0000049:       89 e5                   mov    %esp,%ebp
 000004b:       c9                      leave  
 000004c:       c3                      ret    
(a) と (b) に入れるべき値(4バイトの整数値)を示しなさい。なお e8 は、call 命令のオペコードである。また、call 命令のオペランドは、プログ ラムカウンタ相対で指定するものとする。
Last updated: 2009/12/07 21:57:31
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>