いやなブログさんのPIE (位置独立実行形式) を作成する を読んで。

PIEという形式についてまるで聞いたことがなかったので色々と遊んでみた(いやなブログさんはこういう情報をどこから入手しているのかしら?)

まず、readelfコマンドで実行ファイルのヘッダを読んでみると

$readelf -h pie_hello

ELF ヘッダ:
マジック: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
クラス: ELF32
データ: 2 の補数、リトルエンディアン
バージョン: 1 (current)
OS/ABI: UNIX - System V
ABI バージョン: 0
タイプ: DYN (共有オブジェクトファイル)
マシン: Intel 80386
バージョン: 0x1
エントリポイントアドレス: 0x4dc
プログラムの開始ヘッダ: 52 (バイト)
セクションヘッダ始点: 2564 (バイト)
フラグ: 0x0
このヘッダのサイズ: 52 (バイト)
プログラムヘッダサイズ: 32 (バイト)
プログラムヘッダ数: 7
セクションヘッダ: 40 (バイト)
Number of section headers: 28
Section header string table index: 25


タイプの所に注目。
通常のET_EXE(実行可能ファイル)ではなくET_DYN(共有オブジェクトファイル)になっている。
共有オブジェクトファイルってのは要するに共有ライブらりと同じタイプだ。

つまりカーネルからは共有ライブラリとして見えていて、ランタイムリンカー(ld.so)で細工をしているということを意味する。


実はLinuxにおいて、実行ファイルと共有ライブラリの違いは恐ろしく少ない。
共有ライブラリはELFでいうところの動的オブジェクトという仕組みを使うのだが、
動的オブジェクトと実行可能ファイルの違いは再配置情報があるか否かが本質的な違いであり、
共有ライブラリが通常エントリポイントをもたず実行可能でないのは、あくまで使い方の習慣に
過ぎない。

たとえば、libcなぞは実は実行可能であって

[mkosaki@centos pie]$ /lib/libc-2.3.4.so
GNU C Library stable release version 2.3.4, by Roland McGrath et al.
Copyright (C) 2005 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 3.4.4 20050721 (Red Hat 3.4.4-2).
Compiled on a Linux 2.4.20 system on 2005-10-08.
Available extensions:
GNU libio by Per Bothner
crypt add-on version 2.1 by Michael Glad and others
linuxthreads-0.10 by Xavier Leroy
The C stubs add-on version 2.1.2.
BIND-8.2.3-T5B
NIS(YP)/NIS+ NSS modules 0.19 by Thorsten Kukuk
Glibc-2.0 compatibility add-on by Cristian Gafton
GNU Libidn by Simon Josefsson
libthread_db work sponsored by Alpha Processor Inc
Thread-local storage support included.
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.


などというメッセージを出力してくれる。

カーネル的には、ET_EXEとET_DYNの違いってのはET_EXEは再配置可能でないので
実行ファイルをメモリに貼り付けるときにdo_mmap()をMAP_FIXED flagをONにするぐらいしか
配慮の違いをしていない。

#おいら、今回の件について調べるまでなんでET_EXEかどうかをチェックするのか
#思いつかなかったよ。
#ちょっと反省


というわけで、あんまり調べてないがPIEとは

・PICと違い、自分の再配置に必要な情報だけが入っている(とinfoには書いてあるな)
・エントリポイントを持った共有ライブラリ

がその正体であると結論付けることができました。
メデタシメデタシ




・・・・と終わると綺麗なのだが、このブログっぽいアホさが足りないので蛇足っぽく続けてみる。








ためしに、リンク時に
gcc hello_pie.o -o hello_pie -pie -static


などとスタティックリンクしてみると

./pie_hello
bash: ./pie_hello: /usr/lib/libc.so.1: bad ELF interpreter: そのようなファイルやディレクトリはありません


というつれないお返事をいただき、実行できません。よよよ

もう一度readelfしてinterpを見ると、/usr/lib/libc.so.1 が埋め込まれていました。
むむ、-staticは本来interpセクションを生成しないはずだけど、ここでは-pieが勝ってしまって
生成されてしまった。
しかしながら、gccのinterpセクションのデフォルトがlibc.so.1なのに、最近のディストリじゃ
んなファイルは存在しない。という事になっているのが原因かと思います。

とゆーか、こんなアホなリンクオプションの組み合わせはテストしてないに違いない(決め付け)

そこで無理やりランタイムリンカーにld-2.3.4.so(普段使っているシステムデフォルトのランタイムリンカー)を明示的に埋め込んでみる

  gcc hello_pie.o -o hello_pie -statc -pie -Wl,--dynamic-linker,/lib/ld-2.3.4.so 


[mkosaki@centos pie]$ catchsegv ./pie
*** Segmentation fault
Register dump:

EAX: 00970000 EBX: bfea0a37 ECX: 00000004 EDX: 00970114
ESI: 00000000 EDI: 00554670 EBP: bfea0988 ESP: bfea097c

EIP: 0097c9cd EFLAGS: 00210206

CS: 0073 DS: 007b ES: 007b FS: 0000 GS: 0000 SS: 007b

Trap: 0000000d Error: 00000000 OldMask: 00000000
ESP/signal: bfea097c CR2: 00000000

Backtrace:
/lib/libSegFault.so[0x4b839f]
/lib/libc.so.6[0xab58d8]
??:0(??)[0x97c1da]
??:0(??)[0x97bf81]

Memory map:

0041d000-0041e000 r-xp 00000000 fd:00 65555 /lib/libNoVersion-2.3.4.so
0041e000-00420000 rwxp 00000000 fd:00 65555 /lib/libNoVersion-2.3.4.so
004b7000-004ba000 r-xp 00000000 fd:00 65557 /lib/libSegFault.so
004ba000-004bc000 rwxp 00002000 fd:00 65557 /lib/libSegFault.so
00548000-0055d000 r-xp 00000000 fd:00 65538 /lib/ld-2.3.4.so
0055d000-0055e000 r-xp 00015000 fd:00 65538 /lib/ld-2.3.4.so
0055e000-0055f000 rwxp 00016000 fd:00 65538 /lib/ld-2.3.4.so
00970000-009d9000 r-xp 00000000 fd:00 1922710 /home/mkosaki/projects/pie/pie
009d9000-009db000 rwxp 00068000 fd:00 1922710 /home/mkosaki/projects/pie/pie
009db000-009dc000 rwxp 009db000 00:00 0
00a8d000-00bb2000 r-xp 00000000 fd:00 65560 /lib/libc-2.3.4.so
00bb2000-00bb3000 r-xp 00125000 fd:00 65560 /lib/libc-2.3.4.so
00bb3000-00bb6000 rwxp 00126000 fd:00 65560 /lib/libc-2.3.4.so
00bb6000-00bb8000 rwxp 00bb6000 00:00 0
00ff1000-00ff8000 r-xp 00000000 fd:00 65598 /lib/libgcc_s-3.4.4-20050721.so.1
00ff8000-00ff9000 rwxp 00006000 fd:00 65598 /lib/libgcc_s-3.4.4-20050721.so.1
08fb8000-08fd9000 rw-p 08fb8000 00:00 0
b7f02000-b7f03000 rw-p b7f02000 00:00 0
bfe9f000-c0000000 rw-p bfe9f000 00:00 0
ffffe000-fffff000 ---p 00000000 00:00 0
[mkosaki@centos pie]$ catchsegv ./pie
*** Segmentation fault
Register dump:

EAX: 00554000 EBX: bfef2977 ECX: 00000004 EDX: 00554114
ESI: 00000000 EDI: 00606670 EBP: bfef28c8 ESP: bfef28b8

EIP: 00aa8000 EFLAGS: 00210206

CS: 0073 DS: 007b ES: 007b FS: 0000 GS: 0000 SS: 007b

Trap: 0000000e Error: 00000004 OldMask: 00000000
ESP/signal: bfef28b8 CR2: 00aa8000

Backtrace:
/lib/libSegFault.so[0x25739f]
/lib/libc.so.6[0x3fc8d8]
??:0(??)[0x5601da]
??:0(??)[0x55ff81]

Memory map:

00256000-00259000 r-xp 00000000 fd:00 65557 /lib/libSegFault.so
00259000-0025b000 rwxp 00002000 fd:00 65557 /lib/libSegFault.so
003d4000-004f9000 r-xp 00000000 fd:00 65560 /lib/libc-2.3.4.so
004f9000-004fa000 r-xp 00125000 fd:00 65560 /lib/libc-2.3.4.so
004fa000-004fd000 rwxp 00126000 fd:00 65560 /lib/libc-2.3.4.so
004fd000-004ff000 rwxp 004fd000 00:00 0
00554000-005bd000 r-xp 00000000 fd:00 1922710 /home/mkosaki/projects/pie/pie
005bd000-005bf000 rwxp 00068000 fd:00 1922710 /home/mkosaki/projects/pie/pie
005bf000-005c0000 rwxp 005bf000 00:00 0
005fa000-0060f000 r-xp 00000000 fd:00 65538 /lib/ld-2.3.4.so
0060f000-00610000 r-xp 00015000 fd:00 65538 /lib/ld-2.3.4.so
00610000-00611000 rwxp 00016000 fd:00 65538 /lib/ld-2.3.4.so
00997000-0099e000 r-xp 00000000 fd:00 65598 /lib/libgcc_s-3.4.4-20050721.so.1
0099e000-0099f000 rwxp 00006000 fd:00 65598 /lib/libgcc_s-3.4.4-20050721.so.1
00f30000-00f31000 r-xp 00000000 fd:00 65555 /lib/libNoVersion-2.3.4.so
00f31000-00f33000 rwxp 00000000 fd:00 65555 /lib/libNoVersion-2.3.4.so
0917f000-091a0000 rw-p 0917f000 00:00 0
b7ef8000-b7ef9000 rw-p b7ef8000 00:00 0
bfef1000-c0000000 rw-p bfef1000 00:00 0
ffffe000-fffff000 ---p 00000000 00:00 0



これでメデタク(?)毎回違うアドレスで死んでくれる実行ファイルができました。

ああ、死ぬのはええんよ。
毎回違うアドレスにマップされるのに、再配置不可能なんだから、そら死ぬやろ。

catchsegvでメッセージが出るという事は.ctorの初期化は走っているということで
.ctorが動いたという事はランタイムリンカーの仕事は無事完了して
実行ファイルのスタートアップコードに到達したという事だからOKOKですよ。


P.S.
恒例の今回見つけたバグまたはuglyな仕様コーナー
と、いうわけで、普通やらないコーナーケースに備えて
execシステムコールで実行ファイルがET_DYNでもノーチェックで
ランタイムリンカーへ処理が移っちゃうのですが(この時点で親プロセスからはexec成功に見える)
これはエントリポイントが見つからないときは、exec成功させずに
ENOEXECぐらいを返すべきだと思いますね。
再配置はできるけど、ラインタイムリンカー抜けた瞬間にSIGSEGVって意味わからんし
どうでしょ?



あぶないよ!!とびだしみえるきみのにく
中身がみえちゃった! ランキング!