動的ライブラリの観察その1

今回からは動的ライブラリが利用される様子を観察してみる。

ライブラリとは

リンク可能なオブジェクトファイルの集まり。ライブラリには静的ライブラリと動的ライブラリが有る。

静的ライブラリ

  • 実行形式ファイルには必要なコードが全てコピーされる
  • 実行形式ファイルのサイズは大きくなり起動時間は長い
  • 実行形式ファイルの占有メモリ容量は大きい
  • ライブラリに修正が加えられても再度コンパイルしなければ実行形式ファイルには反映されない
  • 他のファイルに依存すること無く実行形式ファイル単体で実行可能


こんな感じ

動的ライブラリ

  • 実行形式ファイルにはどのライブラリを利用するかという情報だけが記録される
  • 必要なライブラリは起動時にメモリに読み込まれる
  • 実行時に動的に読み込むことも可能
  • 実行形式ファイルのサイズは小さく起動時間は早い
  • 実行形式ファイルの占有メモリ容量は小さい。
  • ライブラリに修正が加えられれば実行形式は変更しなくても反映される
  • 必要なライブラリが存在しなければ実行できない


こんな感じ


ちなみに、Mac OS X ではユーザーバイナリの静的リンクはサポートしていない。(Static linking of user binaries on Mac OS X)

実際の動的ライブラリで観察

まずはプロセスのアドレス空間のどの辺りに読み込まれるのかを見てみる。


単純な動的ラブラリを作る。

$ cat libhoge4.c 
int do_hoge(int i) {
    return i;
}


コンパイル

$ gcc --save-temps -dynamiclib -o libhoge4.dylib libhoge4.c
$ file libhoge4.dylib
libhoge4.dylib: Mach-O 64-bit dynamically linked shared library x86_64


動的ライブラリを利用するコード

$ cat hoge4.c
#include <unistd.h>
#include <stdio.h>

int do_hoge(int);

int main(int argc, char **argv) {
    do_hoge(5);
    do_hoge(6);
    puts("asdf\n");
}


コンパイル

$ gcc --save-temps -L./ -lhoge4 -o hoge4 hoge4.c
$ otool -L hoge4
hoge4:
        libhoge4.dylib (compatibility version 0.0.0, current version 0.0.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.0.0)

otoolコマンドでリンクされている動的ライブラリの一覧を表示することができる。hoge4が先程作った動的ライブラリとリンクされていることがわかる。


gdbで実行。メイン関数から戻る辺りで止めておく。

$ gdb hoge4
(gdb) disas main
Dump of assembler code for function main:
0x0000000100000ee8 :    push   %rbp
0x0000000100000ee9 :    mov    %rsp,%rbp
0x0000000100000eec :    sub    $0x10,%rsp
0x0000000100000ef0 :    mov    %edi,-0x4(%rbp)
0x0000000100000ef3 :   mov    %rsi,-0x10(%rbp)
0x0000000100000ef7 :   mov    $0x5,%edi
0x0000000100000efc :   callq  0x100000f1a 
0x0000000100000f01 :   mov    $0x6,%edi
0x0000000100000f06 :   callq  0x100000f1a 
0x0000000100000f0b :   lea    0x48(%rip),%rdi        # 0x100000f5a
0x0000000100000f12 :   callq  0x100000f26 
0x0000000100000f17 :   leaveq 
0x0000000100000f18 :   retq   
End of assembler dump.
(gdb) b *0x0000000100000f18
Breakpoint 1 at 0x100000f18
(gdb) run
Starting program: hoge4 
Reading symbols for shared libraries ++. done
asdf


Breakpoint 1, 0x0000000100000f18 in main ()
(gdb)


vmmapコマンドで仮想アドレス空間のレイアウトを表示する。これにはどのファイルの内容がどのアドレス範囲に読み込まれているかや、そのアドレス範囲の属性(読込可否、書込実可否、実行可否、共有モードetc)が含まれている。ちなみに、Linuxでも cat /proc/<プロセスID>/maps とすればよく似た情報を得ることができる。

$ ps | grep hoge4
89976 s004  S+     0:00.07 /usr/libexec/gdb/gdb-i386-apple-darwin hoge4
89987 s004  SX     0:00.01 hoge4
89998 s005  S+     0:00.00 grep hoge4

$ vmmap -interleaved 89987 ← hoge4 のプロセスID
Virtual Memory Map of process 89987 (hoge4)
Output report format:  2.2  -- 64-bit process

==== regions for process 89987  (non-writable and writable regions are interleaved)
__TEXT                 0000000100000000-0000000100001000 [    4K] r-x/rwx SM=COW  hoge4
__DATA                 0000000100001000-0000000100002000 [    4K] rw-/rwx SM=PRV  hoge4
__LINKEDIT             0000000100002000-0000000100003000 [    4K] r--/rwx SM=COW  hoge4
__TEXT                 0000000100003000-0000000100004000 [    4K] r-x/rwx SM=COW  libhoge4.dylib
__LINKEDIT             0000000100004000-0000000100005000 [    4K] r--/rwx SM=COW  libhoge4.dylib
STACK GUARD            0000000100005000-0000000100006000 [    4K] ---/rwx SM=NUL  
MALLOC (admin)         0000000100006000-0000000100007000 [    4K] rw-/rwx SM=COW  
STACK GUARD            0000000100007000-0000000100009000 [    8K] ---/rwx SM=NUL  
MALLOC (admin)         0000000100009000-0000000100014000 [   44K] rw-/rwx SM=COW  
STACK GUARD            0000000100014000-0000000100016000 [    8K] ---/rwx SM=NUL  
MALLOC (admin)         0000000100016000-0000000100021000 [   44K] rw-/rwx SM=COW  
STACK GUARD            0000000100021000-0000000100022000 [    4K] ---/rwx SM=NUL  
MALLOC (admin)         0000000100022000-0000000100023000 [    4K] r--/rwx SM=COW  
MALLOC_TINY            0000000100100000-0000000100200000 [ 1024K] rw-/rwx SM=COW  DefaultMallocZone_0x100006000
MALLOC_SMALL           0000000100800000-0000000101000000 [ 8192K] rw-/rwx SM=COW  DefaultMallocZone_0x100006000
STACK GUARD            00007fff5bc00000-00007fff5f400000 [ 56.0M] ---/rwx SM=NUL  
Stack                  00007fff5f400000-00007fff5fbfd000 [ 8180K] rw-/rwx SM=ZER  
Stack                  00007fff5fbfd000-00007fff5fbfe000 [    4K] rw-/rwx SM=PRV  
Stack                  00007fff5fbfe000-00007fff5fbff000 [    4K] rw-/rwx SM=ZER  
Stack                  00007fff5fbff000-00007fff5fc00000 [    4K] rw-/rwx SM=COW  thread 0
__TEXT                 00007fff5fc00000-00007fff5fc0a000 [   40K] r-x/rwx SM=COW  /usr/lib/dyld
__TEXT                 00007fff5fc0a000-00007fff5fc0b000 [    4K] r-x/rwx SM=PRV  /usr/lib/dyld
__TEXT                 00007fff5fc0b000-00007fff5fc3c000 [  196K] r-x/rwx SM=COW  /usr/lib/dyld
__DATA                 00007fff5fc3c000-00007fff5fc41000 [   20K] rw-/rwx SM=COW  /usr/lib/dyld
__DATA                 00007fff5fc41000-00007fff5fc43000 [    8K] rw-/rwx SM=ZER  /usr/lib/dyld
__DATA                 00007fff5fc43000-00007fff5fc44000 [    4K] rw-/rwx SM=COW  /usr/lib/dyld
__DATA                 00007fff5fc44000-00007fff5fc7b000 [  220K] rw-/rwx SM=ZER  /usr/lib/dyld
__LINKEDIT             00007fff5fc7b000-00007fff5fc8f000 [   80K] r--/rwx SM=COW  /usr/lib/dyld
__DATA                 00007fff70b60000-00007fff70b83000 [  140K] rw-/rwx SM=COW  /usr/lib/libSystem.B.dylib
__TEXT                 00007fff864f3000-00007fff866b2000 [ 1788K] r-x/r-x SM=COW  /usr/lib/libSystem.B.dylib
__TEXT                 00007fff86780000-00007fff86785000 [   20K] r-x/r-x SM=COW  /usr/lib/system/libmathCommon.A.dylib
__LINKEDIT             00007fff86f3b000-00007fff88439000 [ 21.0M] r--/r-- SM=COW  /usr/lib/system/libmathCommon.A.dylib

==== Legend
SM=sharing mode:  
        COW=copy_on_write PRV=private NUL=empty ALI=aliased 
        SHM=shared ZER=zero_filled S/A=shared_alias

libhoge4.dylib が hoge4 のすぐ上にロードされている。libSystem.B.dylib はずいぶん上の端にロードされている。libmathCommon.A.dylibというのは・・・・

$ otool -L /usr/lib/libSystem.B.dylib 
/usr/lib/libSystem.B.dylib:
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.0.0)
        /usr/lib/system/libmathCommon.A.dylib (compatibility version 1.0.0, current version 315.0.0)

libSystem.B.dylibが依存しているライブラリか。dyldというのはダイナミックローダー。これが実行形式ファイルや各動的ライブラリをロードしてくれる。
それにしても上の図とはライブラリの配置がずいぶん違っているな。"共有メモリ"という仕組みの関係でこう成っているのかもしれない。そのうち調べよう。


上の結果にさらに otool -l で得たセクションの情報も合わせてみるとこうなる。

__TEXT                 0000000100000000-0000000100001000 [    4K] r-x/rwx SM=COW  hoge4
  sectname __text
   segname __TEXT
      addr 0x0000000100000eac
      size 0x000000000000006d

  sectname __symbol_stub1
   segname __TEXT
      addr 0x0000000100000f1a
      size 0x0000000000000012
 
  sectname __stub_helper
   segname __TEXT
      addr 0x0000000100000f2c
      size 0x000000000000002e

  sectname __cstring
   segname __TEXT
      addr 0x0000000100000f5a
      size 0x0000000000000006

  sectname __unwind_info
   segname __TEXT
      addr 0x0000000100000f60
      size 0x0000000000000054

  sectname __eh_frame
   segname __TEXT
      addr 0x0000000100000fb8
      size 0x0000000000000048
 
__DATA                 0000000100001000-0000000100002000 [    4K] rw-/rwx SM=COW  hoge4

  sectname __nl_symbol_ptr
   segname __DATA
      addr 0x0000000100001000
      size 0x0000000000000010

  sectname __la_symbol_ptr
   segname __DATA
      addr 0x0000000100001010
      size 0x0000000000000018

  sectname __program_vars
   segname __DATA
      addr 0x0000000100001040
      size 0x0000000000000028

  sectname __data
   segname __DATA
      addr 0x0000000100001068
      size 0x0000000000000020

__LINKEDIT             0000000100002000-0000000100003000 [    4K] r--/rwx SM=COW  hoge4
__TEXT                 0000000100003000-0000000100004000 [    4K] r-x/rwx SM=COW  libhoge4.dylib
__LINKEDIT             0000000100004000-0000000100005000 [    4K] r--/rwx SM=COW  libhoge4.dylib
STACK GUARD            0000000100005000-0000000100006000 [    4K] ---/rwx SM=NUL  
MALLOC (admin)         0000000100006000-0000000100007000 [    4K] rw-/rwx SM=COW  
STACK GUARD            0000000100007000-0000000100009000 [    8K] ---/rwx SM=NUL  
MALLOC (admin)         0000000100009000-0000000100014000 [   44K] rw-/rwx SM=COW  
STACK GUARD            0000000100014000-0000000100016000 [    8K] ---/rwx SM=NUL  
MALLOC (admin)         0000000100016000-0000000100021000 [   44K] rw-/rwx SM=PRV  
STACK GUARD            0000000100021000-0000000100022000 [    4K] ---/rwx SM=NUL  
MALLOC (admin)         0000000100022000-0000000100023000 [    4K] r--/rwx SM=COW  
MALLOC_TINY            0000000100100000-0000000100200000 [ 1024K] rw-/rwx SM=COW  DefaultMallocZone_0x100006000
STACK GUARD            00007fff5bc00000-00007fff5f400000 [ 56.0M] ---/rwx SM=NUL  
Stack                  00007fff5f400000-00007fff5fbfd000 [ 8180K] rw-/rwx SM=ZER  
Stack                  00007fff5fbfd000-00007fff5fbfe000 [    4K] rw-/rwx SM=PRV  
Stack                  00007fff5fbfe000-00007fff5fbff000 [    4K] rw-/rwx SM=ZER  
Stack                  00007fff5fbff000-00007fff5fc00000 [    4K] rw-/rwx SM=COW  thread 0
__TEXT                 00007fff5fc00000-00007fff5fc0a000 [   40K] r-x/rwx SM=COW  /usr/lib/dyld
__TEXT                 00007fff5fc0a000-00007fff5fc0b000 [    4K] r-x/rwx SM=PRV  /usr/lib/dyld
__TEXT                 00007fff5fc0b000-00007fff5fc16000 [   44K] r-x/rwx SM=COW  /usr/lib/dyld
__TEXT                 00007fff5fc16000-00007fff5fc17000 [    4K] r-x/rwx SM=PRV  /usr/lib/dyld
__TEXT                 00007fff5fc17000-00007fff5fc3c000 [  148K] r-x/rwx SM=COW  /usr/lib/dyld
__DATA                 00007fff5fc3c000-00007fff5fc41000 [   20K] rw-/rwx SM=COW  /usr/lib/dyld
__DATA                 00007fff5fc41000-00007fff5fc43000 [    8K] rw-/rwx SM=ZER  /usr/lib/dyld
__DATA                 00007fff5fc43000-00007fff5fc44000 [    4K] rw-/rwx SM=COW  /usr/lib/dyld
__DATA                 00007fff5fc44000-00007fff5fc7b000 [  220K] rw-/rwx SM=ZER  /usr/lib/dyld
__LINKEDIT             00007fff5fc7b000-00007fff5fc8f000 [   80K] r--/rwx SM=COW  /usr/lib/dyld
__DATA                 00007fff70b60000-00007fff70ba0000 [  256K] rw-/rwx SM=COW  /usr/lib/libSystem.B.dylib
__TEXT                 00007fff864f3000-00007fff865fb000 [ 1056K] r-x/r-x SM=COW  /usr/lib/libSystem.B.dylib
__TEXT                 00007fff865fb000-00007fff865fc000 [    4K] r-x/rwx SM=COW  /usr/lib/libSystem.B.dylib
__TEXT                 00007fff865fb000-00007fff86780000 [ 1556K] r-x/r-x SM=COW  /usr/lib/libSystem.B.dylib
__TEXT                 00007fff86780000-00007fff86785000 [   20K] r-x/r-x SM=COW  /usr/lib/system/libmathCommon.A.dylib
__TEXT                 00007fff86785000-00007fff867ba000 [  212K] r-x/r-x SM=COW  /usr/lib/libSystem.B.dylib
__LINKEDIT             00007fff86f3b000-00007fff88439000 [ 21.0M] r--/r-- SM=COW  /usr/lib/system/libmathCommon.A.dylib

まとめ

  • 動的ライブラリを利用する実行形式ファイルが実行されている時の具体的なメモリレイアウトを確認した