prelink - CELF `tree`

組込みエンジニアのためのLinux入門
ダイナミックリンク編(3)
2007.8.31
株式会社アプリックス
小林哲之
1
このスライドの対象とする方
• 今までずっと組込み機器のプロジェクト
に携わってきて最近はOSにLinuxを使っ
ている方々
2
このスライドの目的
• Linuxで使用されているダイナミックリン
クの仕組みを理解し現在のプロジェクト
に役立てる。
– 仕組みを知らなくてもプログラムは動くが、
トラブルに対処したり性能を引き出すため
には仕組みの理解が重要。
3
今日のお題
• 共有ライブラリの簡単な歴史
• Prelinkとは
• Prelinkのtips
4
共有ライブラリの簡単な歴史
• a.out時代
• ELF時代
• ELF + prelink
5
a.out 時代
• a.outはLinuxの最初のオブジェクトフォーマット。
• 共有ライブラリのリンクは静的に行う。
• ライブラリの配置アドレスは人手によって管理されて
いた。
– ライブラリの更新でサイズが変わると大変。
– 世の中の全てのライブラリを重ならないように配置しよう
とするとアドレス空間が足りなくなる!
• ライブラリの作成も大変。
• 起動時の処理はシンプルなので速い。
6
ELF時代
• ELFはダイナミックリンクが簡単にできるよう
に考慮されたオブジェクトフォーマット
• ダイナミックリンク
– ライブラリの配置アドレスは起動時に決定
• ライブラリのアドレスの集中管理が不要になった
• ライブラリの作成も簡単になった
– 起動時にリンクを行う
• 起動時のオーバーヘッドが大きくなった
7
ELF + prelink
a.out ELF
ELF +
prelink
ライブラリの作成、管理
大変
ラク
ラク
起動時のオーバーヘッド
小
大
小
PrelinkはELFのライブラリの作成、管理の容易さを保ったまま
起動時のオーバーヘッド削減を目指す
8
Prelinkとは
• 通常は起動時に行われるリンクの処理を、
あらかじめ済ませておくことにより起動時間の
短縮をねらう。
• CELFテクニカルジャンボリーでもたびたび紹
介され、起動時間短縮の効果が実証されて
いる。
9
CELFでのprelinkの紹介
• テクニカルジャンボリー #3 (2005.7)
– “携帯電話にLinuxを実装する!”
– シンボルの解決にかかる時間が2479msec →125msec
• テクニカルジャンボリー#9 (2006.7)
– “NECによる携帯電話向けリナックスの改良”
• テクニカルジャンボリー #13 (2007.2)
– “Evaluation of MIPS pre-link”
• Prelinkのまとめページ
– http://tree.celinuxforum.org/CelfPubWiki/PreLinking
10
Prelinkは実際に何をやってくれるのか
• ダイナミックリンクライブラリは起動時に配置アドレ
スが決まるので、その後のシンボルの解決等の
ローダの処理の負荷が大きかった。
• Prelinkでは
– 全てのダイナミックリンクライブラリを走査して配置するア
ドレスを決定する。
– それぞれのダイナミックリンクライブラリのロードアドレス
を決定したアドレスにセットする.
– 依存関係のある実行ファイル、ダイナミックリンクライブラ
リのアドレス参照を解決する。
• これによりローダの起動時の処理が大幅に軽減さ
れた。
11
prelinkの使用法
$ prelink -avR
-a /etc/prelink.conf に指定してあるディレクトリのダイナミックリンクライブラリと
実行ファイルのprelinkを行う
-v vorbose
-R アドレスをランダムに決める (ウィルス耐性が高まる)
$ prelink <filename>
追加で指定されたファイルのprelinkを行う
12
/etc/prelink.cache
各ライブラリのアドレスと依存関係がバイナリフォーマットで格納される。
prelink –p で確認することができる。
# prelink -p
152 objects found in prelink cache `/etc/prelink.cache'
/usr/lib/libwwwfile.so.0.1.0 [0x41eeda3e] 0x4a588000-0x4a597dec:
/lib/tls/libdl-2.3.3.so [0x536dae10]
/lib/tls/libc-2.3.3.so [0xedb6e392]
/lib/ld-2.3.3.so [0x040c82cc]
/usr/lib/libsctp.so.1.0.2 [0x26ac5ae2] 0x4adc8000-0x4add12ac:
/lib/tls/libc-2.3.3.so [0xedb6e392]
/lib/ld-2.3.3.so [0x040c82cc]
/usr/lib/libreadline.so.4.3.old [0xf90fc4bc] 0x414180000x4144b770:
/lib/tls/libc-2.3.3.so [0xedb6e392]
/lib/ld-2.3.3.so [0x040c82cc]
/usr/lib/libwwwnews.so.0.1.0 [0xdc63747e] 0x4ac78000-0x4ac86310:
/lib/tls/libdl-2.3.3.so [0x536dae10]
/lib/tls/libc-2.3.3.so [0xedb6e392]
/lib/ld-2.3.3.so [0x040c82cc]
....
13
通常のライブラリ
$ readelf -l libc-2.3.3.so
Elf file type is DYN (Shared object file)
Entry point 0x13708
There are 11 program headers, starting at offset 52
Program Headers:
Type
Offset
EXIDX
0x0fbc3c
PHDR
0x000034
INTERP
0x0fb410
[Requesting program
LOAD
0x000000
LOAD
0x0fda64
DYNAMIC
0x0fef1c
NOTE
0x000194
NOTE
0x0fdafc
TLS
0x0fdae0
GNU_EH_FRAME
0x0fcf4c
GNU_RELRO
0x0fda64
ロードアドレスが0
VirtAddr
PhysAddr
FileSiz MemSiz
0x000fbc3c 0x000fbc3c 0x01310 0x01310
0x00000034 0x00000034 0x00160 0x00160
0x000fb410 0x000fb410 0x00014 0x00014
interpreter: /lib/ld-linux.so.3]
0x00000000 0x00000000 0xfcf78 0xfcf78
0x00105a64 0x00105a64 0x02628 0x04da8
0x00106f1c 0x00106f1c 0x000e0 0x000e0
0x00000194 0x00000194 0x00020 0x00020
0x00105afc 0x00105afc 0x00074 0x00074
0x00105ae0 0x00105ae0 0x00008 0x00028
0x000fcf4c 0x000fcf4c 0x0002c 0x0002c
0x00105a64 0x00105a64 0x0159c 0x0159c
Flg
R
R E
R
Align
0x4
0x4
0x4
R E
RW
RW
R
R
R
R
R
0x8000
0x8000
0x4
0x4
0x4
0x4
0x4
0x1
14
prelinkしたライブラリ
$ readelf -l libc-2.3.3.so
Elf file type is DYN (Shared object file)
Entry point 0x491c3708
There are 11 program headers, starting at offset 52
Program Headers:
Type
Offset
EXIDX
0x0fbc3c
PHDR
0x000034
INTERP
0x0fb410
[Requesting program
LOAD
0x000000
LOAD
0x0fda64
DYNAMIC
0x0fef1c
NOTE
0x000194
NOTE
0x0fdafc
TLS
0x0fdae0
GNU_EH_FRAME
0x0fcf4c
GNU_RELRO
0x0fda64
ロードアドレスが
割り当てられている
VirtAddr
PhysAddr
FileSiz MemSiz
0x492abc3c 0x492abc3c 0x01310 0x01310
0x491b0034 0x491b0034 0x00160 0x00160
0x492ab410 0x492ab410 0x00014 0x00014
interpreter: /lib/ld-linux.so.3]
0x491b0000 0x491b0000 0xfcf78 0xfcf78
0x492b5a64 0x492b5a64 0x02628 0x04da8
0x492b6f1c 0x492b6f1c 0x000e0 0x000e0
0x491b0194 0x491b0194 0x00020 0x00020
0x492b5afc 0x492b5afc 0x00074 0x00074
0x492b5ae0 0x492b5ae0 0x00008 0x00028
0x492acf4c 0x492acf4c 0x0002c 0x0002c
0x492b5a64 0x492b5a64 0x0159c 0x0159c
Flg
R
R E
R
Align
0x4
0x4
0x4
R E
RW
RW
R
R
R
R
R
0x8000
0x8000
0x4
0x4
0x4
0x4
0x4
0x1
15
prelinkしたライブラリ
# objdump -h libc-2.3.3.so
libc-2.3.3.so:
file format elf32-littlearm
Sections:
Idx Name
Size
VMA
LMA
File off Algn
0 .note.ABI-tag 00000020 491b0194 491b0194 00000194 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA,
LINK_ONCE_SAME_CONTENTS
1 .hash
00002e74 491b01b4 491b01b4 000001b4 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
...
69 .gnu_debuglink 00000018 00000000 00000000 001008f4 2**2
CONTENTS, READONLY
70 .gnu.liblist 00000014 00000000 00000000 0010090c 2**2
CONTENTS, READONLY
71 .gnu.libstr
00000014 00000000 00000000 00100920 2**0
CONTENTS, READONLY
72 .gnu.prelink_undo 00000cac 00000000 00000000 00100934 2**2
CONTENTS, READONLY
#
この3つのセクションが追加された。
その分だけファイルサイズ増加。
16
Prelinkによるファイルサイズの増加
size(bytes)
prelinked size(bytes)
delta
delta%
libc.so
1,054,936
1,058,384
3,448
0.326844
libm.so
432,088
433,792
1,704
0.394364
libcrypt.so
27,556
29,429
1,873
6.797068
libdl.so
15,036
16,900
1,864
12.39691
libX11.so
769,468
770,988
1,520
0.197539
libglib-2.0.so
557,908
559,508
1,600
0.286786
2,846,996
2,849,320
2,324
0.08163
bash
615,940
645,220
29,280
4.75371
busybox
622,640
627,596
4,956
0.795966
28,356
31,356
3,000
10.57977
libgtk-x11-2.0.so
init
数KBの増加。
17
実行時のローダの動きを比較してみる
• 環境変数 LD_DEBUG=all で実行すると
ローダの実行ログが表示される。
• それをファイルに保存するためには
LD_DEBUG_OUTPUT=<file name>
• 統計情報のみ欲しい場合は
LD_DEBUG=statistics
18
LD_DEBUG=allの例
# LD_DEBUG=all ./gtk_hello
.......
559:
symbol=malloc; lookup in file=./gtk_hello
559:
symbol=malloc; lookup in file=/usr/lib/libgtk-x11-2.0.so.0
559:
symbol=malloc; lookup in file=/usr/lib/libgdk-x11-2.0.so.0
559:
symbol=malloc; lookup in file=/usr/lib/libXrandr.so.2
559:
symbol=malloc; lookup in file=/usr/lib/libXfixes.so.3
559:
symbol=malloc; lookup in file=/usr/lib/libXcursor.so.1
559:
symbol=malloc; lookup in file=/usr/lib/libatk-1.0.so.0
559:
symbol=malloc; lookup in file=/usr/lib/libgdk_pixbuf-2.0.so.0
559:
symbol=malloc; lookup in file=/usr/lib/libpangoxft-1.0.so.0
559:
symbol=malloc; lookup in file=/usr/lib/libXft.so.2
559:
symbol=malloc; lookup in file=/usr/lib/libXrender.so.1
559:
symbol=malloc; lookup in file=/usr/lib/libXext.so.6
559:
symbol=malloc; lookup in file=/usr/lib/libfontconfig.so.1
559:
symbol=malloc; lookup in file=/usr/lib/libfreetype.so.6
559:
symbol=malloc; lookup in file=/usr/lib/libz.so.1
559:
symbol=malloc; lookup in file=/usr/lib/libpangox-1.0.so.0
559:
symbol=malloc; lookup in file=/usr/lib/libX11.so.6
559:
symbol=malloc; lookup in file=/usr/lib/libpango-1.0.so.0
559:
symbol=malloc; lookup in file=/lib/tls/libm.so.6
559:
symbol=malloc; lookup in file=/usr/lib/libgobject-2.0.so.0
559:
symbol=malloc; lookup in file=/usr/lib/libgmodule-2.0.so.0
559:
symbol=malloc; lookup in file=/lib/tls/libdl.so.2
559:
symbol=malloc; lookup in file=/usr/lib/libglib-2.0.so.0
559:
symbol=malloc; lookup in file=/lib/tls/libc.so.6
559:
binding file /usr/lib/libexpat.so.0 to /lib/tls/libc.so.6: normal symbol `malloc' [GLIBC_2.4]
.......
この例では全部で38000行
19
LD_DEBUG=statisticsの例
# LD_DEBUG=statistics ./gtk-hello
574:
number of relocations:
574:
number of relocations from cache:
574:
number of relative relocations:
574:
574:
runtime linker statistics:
574:
final number of relocations:
574:
final number of relocations from cache:
547
44
14309
1957
44
プロセスID
20
Prelinkの効果
prelink
無し
number of relocations
number of relocations from cache
number of relative relocations
runtime
final number of relocations
runtime
final number of relocations from cache
全て
prelink
libのみ
prelink
547
547
0
44
44
33
14309
0
0
1957
1957
56
44
44
33
テストプログラムは gtkで画面上にボタンを1個表示するもの。
21
実行時の物理メモリ消費量の比較
prelink
無し
VmRSS (KBytes)
4300
libのみ
prelink
4216
全て
prelink
4164
-136KB
テストプログラムは前頁と同じもの。
cat /proc/PID/status で観測した。
起動時のrelocationの処理が軽くなったのに伴って物理メモリ使用量も
減少している。
22
簡単なまとめ
• プラス面
– 起動時間が短縮される
– 実行時の物理メモリ消費量も少なくて済む
• マイナス面
– ルートファイルシステム構築時に1工程増える
– ファイルサイズが少し増加する
大抵のケースではプラス面が勝るであろう。
23
Prelinkの柔軟性
• prelinkされていないライブラリが混じってし
まっても起動時間は最適でないけれども実行
はできる。このため致命的な問題が起こりにく
い。
• prelinkしたライブラリを元に戻すこともできる。
(undoのための情報を保持している。この情
報の削除に関して後述)
24
PrelinkのTips
• stripしてからprelinkするほうがファイルサイ
ズが小さくなる。
• undo情報を削除してファイルサイズを減らそ
うと試みた(しかしうまくいかなかった。)
25
ビルド手順へのprelinkの組込み
• stripしてからprelinkするほうがファイルサイ
ズが小さくなる。
strip
size: 6,508
prelink
元の実行ファイル
size: 9,452
こちらが小さい
size: 10,511
prelink
size: 13,699
strip
size: 9,692
先にstripしたほうが、prelinkのときに付加されるundoのための情報の
サイズが小さくて済むため
26
undo情報を削除してファイルサイズを
減らす
• undo情報は起動時には使用されることが無
い。組込みの場合はundoすることはあまりな
いと思われるため、undo情報は冗長?
• objcopyコマンドかstripコマンドでundo情報
のセクションを削除する
$ strip –remove-section=.gnu.prelink_undo <filename>
これで減らせるファイルサイズは数百バイト~数キロバイト
PCのLinux環境では問題がなかったが、手元にあったARMの環境では
実行ファイルでこれを行うと起動時にsegmentation faultになるものがあった。
ライブラリだけなら問題なかった。
27
参考文献
• “Prelink”
http://people.redhat.com/jakub/prelink/prelink.pdf
• “Linkers & Loaders” オーム社
• “BINARY HACKS” オライリージャパン
• “GNU Development Tools” Wataru Nishida
• GNU C ライブラリのソース
http://www.gnu.org/software/libc/
• その他たくさんのWEB検索結果
28