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

前回は、動的ライブラリの関数へのシンボル参照は、/usr/lib/libSystem.B.dylib の中の dyld_stub_binder の中で解決された〜、というところまで見た。今回は dyld_stub_binder の中で何が起こっているのかをもう少し見てみる。


ソースは前回と同じ。


動的ライブラリはこれ

$ cat libfoo1.c
int do_foo(int i) {
    return i;
}


コンパイル

$ gcc -dynamiclib -o libfoo1.dylib libfoo1.c


動的ライブラリを呼び出す方はこれ

$ cat hoge1.c 
int do_foo(int);

int main(int argc, char **argv) {
    do_foo(5);
    do_foo(6);
    return 0;
}


コンパイル

$ gcc -L./ -lfoo1 -o hoge1 hoge1.c


gdb で hoge1 を実行。

$gdb hoge1


前回までのおさらい・・・・は前回、前々回参照ということで。dyld_stub_binder は dyld_stub_binder.s で定義されているアセンブリコード。


最初の do_foo(int) 呼び出し直後の dyld_stub_binder でブレイクしてディスアセンブルしてみる。
こんな感じ(抜粋)

0x00007fff8020bfa8 <dyld_stub_binder+0>:        push   %rbp
0x00007fff8020bfa9 <dyld_stub_binder+1>:        mov    %rsp,%rbp
0x00007fff8020bfac <dyld_stub_binder+4>:        sub    $0xc0,%rsp
(レジスタの退避)
0x00007fff8020c011 <dyld_stub_binder+105>:      mov    0x8(%rbp),%rdi
0x00007fff8020c015 <dyld_stub_binder+109>:      mov    0x10(%rbp),%rsi
0x00007fff8020c019 <dyld_stub_binder+113>:      callq  0x7fff8010fc4f <_Z21_dyld_fast_stub_entryPvl>
0x00007fff8020c01e <dyld_stub_binder+118>:      mov    %rax,%r11
(レジスタの復帰)
0x00007fff8020c07f <dyld_stub_binder+215>:      add    $0xc0,%rsp
0x00007fff8020c086 <dyld_stub_binder+222>:      pop    %rbp
0x00007fff8020c087 <dyld_stub_binder+223>:      add    $0x10,%rsp
0x00007fff8020c08b <dyld_stub_binder+227>:      jmpq   *%r11

前回はこの _Z21_dyld_fast_stub_entryPvl の中で do_foo(int)のアドレスが解決されて戻り値として戻ってきているなと言うところまでしか見ていなかった。今回はこの先をもうちょっと観てみよう。


このときのvmmapは以下のように成っている。

__TEXT                 0000000100000000-0000000100001000 [    4K] r-x/rwx SM=COW  /Users/teru/Documents/low_level/blog_dylib2/hoge1
__DATA                 0000000100001000-0000000100002000 [    4K] rw-/rwx SM=PRV  /Users/teru/Documents/low_level/blog_dylib2/hoge1
__LINKEDIT             0000000100002000-0000000100003000 [    4K] r--/rwx SM=COW  /Users/teru/Documents/low_level/blog_dylib2/hoge1
__TEXT                 0000000100003000-0000000100004000 [    4K] r-x/rwx SM=COW  /Users/teru/Documents/low_level/blog_dylib2/libfoo1.dylib
__LINKEDIT             0000000100004000-0000000100005000 [    4K] r--/rwx SM=COW  /Users/teru/Documents/low_level/blog_dylib2/libfoo1.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-00007fff5fc04000 [   16K] r-x/rwx SM=COW  /usr/lib/dyld
__TEXT                 00007fff5fc04000-00007fff5fc05000 [    4K] r-x/rwx SM=PRV  /usr/lib/dyld
__TEXT                 00007fff5fc05000-00007fff5fc0a000 [   20K] 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                 00007fff7001a000-00007fff7003d000 [  140K] rw-/rwx SM=COW  /usr/lib/libSystem.B.dylib
__TEXT                 00007fff80103000-00007fff8020b000 [ 1056K] r-x/r-x SM=COW  /usr/lib/libSystem.B.dylib
__TEXT                 00007fff8020b000-00007fff8020c000 [    4K] r-x/rwx SM=COW  /usr/lib/libSystem.B.dylib
__TEXT                 00007fff8020b000-00007fff803cb000 [ 1792K] r-x/r-x SM=COW  /usr/lib/libSystem.B.dylib
__TEXT                 00007fff86b99000-00007fff86b9e000 [   20K] r-x/r-x SM=COW  /usr/lib/system/libmathCommon.A.dylib
__LINKEDIT             00007fff870f6000-00007fff88627000 [ 21.2M] r--/r-- SM=COW  /usr/lib/system/libmathCommon.A.dylib

_Z21_dyld_fast_stub_entryPvl のアドレス 0x7fff8010fc4f は libSystem.B.dylib に含まれていることがわかる。_Z21_dyld_fast_stub_entryPvl というのはデマングルすると _dyld_fast_stub_entry(void*, long) というC++の関数で dyldAPIsInLibSystem.cpp で定義されている。


dyld_stub_binder.s もそうだが、dyldのソースに記述されているコードがなぜ libSystem.B.dylib に含まれているのかは謎。どこかで LibSystem は Umbrella Framework だからどうのこうのという記述を見た気がするが・・・。まぁ、そのうち調べよう。


C++の関数はこう

#if __i386__ || __x86_64__
__attribute__((visibility("hidden"))) 
void* _dyld_fast_stub_entry(void* loadercache, long lazyinfo)
{
    DYLD_NO_LOCK_THIS_BLOCK;
    static void* (*p)(void*, long) = NULL;

    if(p == NULL)
        _dyld_func_lookup("__dyld_fast_stub_entry", (void**)&p);
    return p(loadercache, lazyinfo);
}
#endif


実行中の物をディスアセンブルしたものはこう

(gdb) b _Z21_dyld_fast_stub_entryPvl
Breakpoint 4 at 0x7fff8010fc5f
(gdb) c
Continuing.

Breakpoint 4, 0x00007fff8010fc5f in _dyld_fast_stub_entry ()
(gdb) disas
Dump of assembler code for function _Z21_dyld_fast_stub_entryPvl:
0x00007fff8010fc4f <_Z21_dyld_fast_stub_entryPvl+0>:    push   %rbp
0x00007fff8010fc50 <_Z21_dyld_fast_stub_entryPvl+1>:    mov    %rsp,%rbp
0x00007fff8010fc53 <_Z21_dyld_fast_stub_entryPvl+4>:    mov    %rbx,-0x10(%rbp)
0x00007fff8010fc57 <_Z21_dyld_fast_stub_entryPvl+8>:    mov    %r12,-0x8(%rbp)
0x00007fff8010fc5b <_Z21_dyld_fast_stub_entryPvl+12>:   sub    $0x10,%rsp
0x00007fff8010fc5f <_Z21_dyld_fast_stub_entryPvl+16>:   mov    %rdi,%r12
0x00007fff8010fc62 <_Z21_dyld_fast_stub_entryPvl+19>:   mov    %rsi,%rbx
0x00007fff8010fc65 <_Z21_dyld_fast_stub_entryPvl+22>:   cmpq   $0x0,-0x100d9525(%rip)        # 0x7fff70036748 <_Zz21_dyld_fast_stub_entryPvlE1p>
0x00007fff8010fc6d <_Z21_dyld_fast_stub_entryPvl+30>:   jne    0x7fff8010fc82 <_Z21_dyld_fast_stub_entryPvl+51>
0x00007fff8010fc6f <_Z21_dyld_fast_stub_entryPvl+32>:   lea    -0x100d952e(%rip),%rsi        # 0x7fff70036748 <_ZZ21_dyld_fast_stub_entryPvlE1p>
0x00007fff8010fc76 <_Z21_dyld_fast_stub_entryPvl+39>:   lea    0x143f93(%rip),%rdi        # 0x7fff80253c10 <commonCryptoVersionString+96>
0x00007fff8010fc7d <_Z21_dyld_fast_stub_entryPvl+46>:   callq  0x7fff80109ffa <_dyld_func_lookup>
0x00007fff8010fc82 <_Z21_dyld_fast_stub_entryPvl+51>:   mov    %rbx,%rsi
0x00007fff8010fc85 <_Z21_dyld_fast_stub_entryPvl+54>:   mov    %r12,%rdi
0x00007fff8010fc88 <_Z21_dyld_fast_stub_entryPvl+57>:   mov    -0x100d9547(%rip),%r11        # 0x7fff70036748 <_ZZ21_dyld_fast_stub_entryPvlE1p>
0x00007fff8010fc8f <_Z21_dyld_fast_stub_entryPvl+64>:   mov    (%rsp),%rbx
0x00007fff8010fc93 <_Z21_dyld_fast_stub_entryPvl+68>:   mov    0x8(%rsp),%r12
0x00007fff8010fc98 <_Z21_dyld_fast_stub_entryPvl+73>:   leaveq 
0x00007fff8010fc99 <_Z21_dyld_fast_stub_entryPvl+74>:   jmpq   *%r11
End of assembler dump.

余談だが、_Z21_dyld_fast_stub_entryPvl+22 にちょっと注目。ここは C++ の if(p == NULL) に該当するところだが、0x7fff70036748 というアドレスから、static なローカル変数 p はスタックではなくて libSystem.B.dylib の __DATA セグメントに配置されている。static(静的)ってこういう事なんですね〜。


_dyld_func_lookup は dyldLibSystemGlue.c で定義されている

struct __DATA__dyld { long lazy; int (*lookup)(const char*, void**); };

static struct __DATA__dyld  myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, NULL };


__attribute__((weak, visibility("hidden"))) int _dyld_func_lookup(const char* dyld_func_name, void **address)
{
    return (*myDyldSection.lookup)(dyld_func_name, address);
}


実行中の物をディスアセンブルしたものはこう。アドレスからこれも libSystem.B.dylib 内のコードだな。

0x00007fff80109ffa <_dyld_func_lookup+0>:       push   %rbp
0x00007fff80109ffb <_dyld_func_lookup+1>:       mov    %rsp,%rbp
0x00007fff80109ffe <_dyld_func_lookup+4>:       mov    -0x100ecb9d(%rip),%r11        # 0x7fff7001d468 <Mydyldsection+8>
0x00007fff8010a005 <_dyld_func_lookup+11>:      leaveq 
0x00007fff8010a006 <_dyld_func_lookup+12>:      jmpq   *%r11
0x00007fff8010a009 <_dyld_func_lookup+15>:      nop   

Cのソースと見比べると myDyldSection.lookup のアドレスは 0x7fff7001d468 だ。
そこにはどんな値が入っているのかというと

(gdb) x /1xg  0x7fff7001d468
0x7fff7001d468 :       0x00007fff5fc01008

vmmapから /usr/lib/dyld 内のアドレスで有ることがわかる。このあたりから制御がdyldに移るようだ。内容はこう。

(gdb) disas 0x00007fff5fc01008
Dump of assembler code for function __dyld_dyld_func_lookup:
0x00007fff5fc01008 <__dyld_dyld_func_lookup+0>: jmpq   0x7fff5fc07e43 <__dyld__Z18lookupDyldFunctionPKcPm>
0x00007fff5fc0100d <__dyld_dyld_func_lookup+5>: nop    
0x00007fff5fc0100e <__dyld_dyld_func_lookup+6>: nop    
0x00007fff5fc0100f <__dyld_dyld_func_lookup+7>: nop 

該当するソースは dyldStartup.s というアセンブリコード。このアドレスがどの様にして上記の myDyldSection.lookup に代入されたのかは謎。処理内容は __dyld__Z18lookupDyldFunctionPKcPm、つまり lookupDyldFunction(char const*, unsigned long*) へジャンプしてるだけ。

lookupDyldFunction(char const*, unsigned long*) は dyldAPIs.cpp でこのように定義されている。

bool lookupDyldFunction(const char* name, uintptr_t* address)
{
    for (const dyld_func* p = dyld_funcs; p->name != NULL; ++p) {
        if ( strcmp(p->name, name) == 0 ) {
            if( p->implementation == unimplemented )
                dyld::log("unimplemented dyld function: %s\n", p->name);
            *address = (uintptr_t)p->implementation;
            return true;
        }
    }
    *address = 0;
    return false;
}

dyld_funcs は dyldAPIs.cpp で定義されているこのようなテーブルで、 dyld が提供するAPI名と実装する関数のアドレスを対応付けるテーブルだ。

struct dyld_func {
    const char* name;
    void*		implementation;
};

static struct dyld_func dyld_funcs[] = {
    {"__dyld_register_func_for_add_image",    (void*)_dyld_register_func_for_add_image },
    {"__dyld_register_func_for_remove_image", (void*)_dyld_register_func_for_remove_image },
・・・
#if !__arm__
    {"__dyld_find_unwind_sections",	      (void*)client_dyld_find_unwind_sections },
#endif
#if __i386__ || __x86_64__
    {"__dyld_fast_stub_entry",                (void*)dyld::fastBindLazySymbol },
#endif
    {"__dyld_image_path_containing_address",  (void*)dyld_image_path_containing_address },
・・・
    {NULL, 0}
};

で、今は何というAPIを探してるんでしたっけ?というと _dyld_fast_stub_entry(void* loadercache, long lazyinfo) の中で _dyld_func_lookup("__dyld_fast_stub_entry", (void**)&p) というふうに呼び出されているので __dyld_fast_stub_entry だ。


一応確認。関数の第一引数はrdiレジスタなので、lookupDyldFunction() まで実行してから以下のように確認。

(gdb) i reg rip
rip            0x7fff5fc07e43   0x7fff5fc07e43 <__dyld__Z18lookupDyldFunctionPKcPm>
(gdb) x /1s $rdi
0x7fff80253c10 :   "__dyld_fast_stub_entry"

確かに __dyld_fast_stub_entry を探している。


上記のテーブルによると、その実装は dyld::fastBindLazySymbol となっている。これもデバッガで確認してみる。lookupDyldFunction() をディスアセンブルするとこうなる。

(gdb) disas
Dump of assembler code for function __dyld__Z18lookupDyldFunctionPKcPm:
0x00007fff5fc07e43 <__dyld__Z18lookupDyldFunctionPKcPm+0>:      push   %rbp
0x00007fff5fc07e44 <__dyld__Z18lookupDyldFunctionPKcPm+1>:      mov    %rsp,%rbp
0x00007fff5fc07e47 <__dyld__Z18lookupDyldFunctionPKcPm+4>:      push   %r14
0x00007fff5fc07e49 <__dyld__Z18lookupDyldFunctionPKcPm+6>:      push   %r13
0x00007fff5fc07e4b <__dyld__Z18lookupDyldFunctionPKcPm+8>:      push   %r12
0x00007fff5fc07e4d <__dyld__Z18lookupDyldFunctionPKcPm+10>:     push   %rbx
0x00007fff5fc07e4e <__dyld__Z18lookupDyldFunctionPKcPm+11>:     mov    %rdi,%r13
0x00007fff5fc07e51 <__dyld__Z18lookupDyldFunctionPKcPm+14>:     mov    %rsi,%r14
0x00007fff5fc07e54 <__dyld__Z18lookupDyldFunctionPKcPm+17>:     lea    0x35945(%rip),%r12        # 0x7fff5fc3d7a0 <__dyld__ZL10dyld_funcs>
0x00007fff5fc07e5b <__dyld__Z18lookupDyldFunctionPKcPm+24>:     jmp    0x7fff5fc07e9e <__dyld__Z18lookupDyldFunctionPKcPm+91>
0x00007fff5fc07e5d <__dyld__Z18lookupDyldFunctionPKcPm+26>:     mov    %r13,%rsi
0x00007fff5fc07e60 <__dyld__Z18lookupDyldFunctionPKcPm+29>:     mov    %rbx,%rdi
0x00007fff5fc07e63 <__dyld__Z18lookupDyldFunctionPKcPm+32>:     callq  0x7fff5fc22fb0 <__dyld_strcmp>
0x00007fff5fc07e68 <__dyld__Z18lookupDyldFunctionPKcPm+37>:     test   %eax,%eax
0x00007fff5fc07e6a <__dyld__Z18lookupDyldFunctionPKcPm+39>:     jne    0x7fff5fc07e9a <__dyld__Z18lookupDyldFunctionPKcPm+87>
0x00007fff5fc07e6c <__dyld__Z18lookupDyldFunctionPKcPm+41>:     lea    0x23b(%rip),%rax        # 0x7fff5fc080ae <__dyld__ZL13unimplementedv>
0x00007fff5fc07e73 <__dyld__Z18lookupDyldFunctionPKcPm+48>:     cmp    %rax,0x8(%r12)
0x00007fff5fc07e78 <__dyld__Z18lookupDyldFunctionPKcPm+53>:     jne    0x7fff5fc07e8b <__dyld__Z18lookupDyldFunctionPKcPm+72>
0x00007fff5fc07e7a <__dyld__Z18lookupDyldFunctionPKcPm+55>:     mov    %rbx,%rsi
0x00007fff5fc07e7d <__dyld__Z18lookupDyldFunctionPKcPm+58>:     lea    0x1e184(%rip),%rdi        # 0x7fff5fc26008
0x00007fff5fc07e84 <__dyld__Z18lookupDyldFunctionPKcPm+65>:     xor    %eax,%eax
0x00007fff5fc07e86 <__dyld__Z18lookupDyldFunctionPKcPm+67>:     callq  0x7fff5fc02973 <__dyld__ZN4dyld3logEPKcz>
0x00007fff5fc07e8b <__dyld__Z18lookupDyldFunctionPKcPm+72>:     mov    0x8(%r12),%rax
0x00007fff5fc07e90 <__dyld__Z18lookupDyldFunctionPKcPm+77>:     mov    %rax,(%r14)
0x00007fff5fc07e93 <__dyld__Z18lookupDyldFunctionPKcPm+80>:     mov    $0x1,%eax
0x00007fff5fc07e98 <__dyld__Z18lookupDyldFunctionPKcPm+85>:     jmp    0x7fff5fc07eb0 <__dyld__Z18lookupDyldFunctionPKcPm+109>
0x00007fff5fc07e9a <__dyld__Z18lookupDyldFunctionPKcPm+87>:     add    $0x10,%r12
0x00007fff5fc07e9e <__dyld__Z18lookupDyldFunctionPKcPm+91>:     mov    (%r12),%rbx
0x00007fff5fc07ea2 <__dyld__Z18lookupDyldFunctionPKcPm+95>:     test   %rbx,%rbx
0x00007fff5fc07ea5 <__dyld__Z18lookupDyldFunctionPKcPm+98>:     jne    0x7fff5fc07e5d <__dyld__Z18lookupDyldFunctionPKcPm+26>
0x00007fff5fc07ea7 <__dyld__Z18lookupDyldFunctionPKcPm+100>:    movq   $0x0,(%r14)
0x00007fff5fc07eae <__dyld__Z18lookupDyldFunctionPKcPm+107>:    xor    %eax,%eax
0x00007fff5fc07eb0 <__dyld__Z18lookupDyldFunctionPKcPm+109>:    pop    %rbx
0x00007fff5fc07eb1 <__dyld__Z18lookupDyldFunctionPKcPm+110>:    pop    %r12
0x00007fff5fc07eb3 <__dyld__Z18lookupDyldFunctionPKcPm+112>:    pop    %r13
0x00007fff5fc07eb5 <__dyld__Z18lookupDyldFunctionPKcPm+114>:    pop    %r14
0x00007fff5fc07eb7 <__dyld__Z18lookupDyldFunctionPKcPm+116>:    leaveq 
0x00007fff5fc07eb8 <__dyld__Z18lookupDyldFunctionPKcPm+117>:    retq   
End of assembler dump.

C++のソースと見比べると

 *address = (uintptr_t)p->implementation;

にあたる部分は

0x00007fff5fc07e8b <__dyld__Z18lookupDyldFunctionPKcPm+72>:     mov    0x8(%r12),%rax
0x00007fff5fc07e90 <__dyld__Z18lookupDyldFunctionPKcPm+77>:     mov    %rax,(%r14)
0x00007fff5fc07e93 <__dyld__Z18lookupDyldFunctionPKcPm+80>:     mov    $0x1,%eax

の部分なのでこのraxの値を確認してみる。(__dyld_fast_stub_entry を探しているのでこのコードを通ることは明らか。)

(gdb) b *0x00007fff5fc07e93
Breakpoint 6 at 0x7fff5fc07e93
(gdb) c 
Continuing.

Breakpoint 6, 0x00007fff5fc07e93 in __dyld__Z18lookupDyldFunctionPKcPm ()
(gdb)  i reg rax               
rax            0x7fff5fc04721   140734799824673
(gdb) x /3i $rax
0x7fff5fc04721 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm>:   push   %rbp
0x7fff5fc04722 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+1>: mov    %rsp,%rbp
0x7fff5fc04725 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+4>: push   %rbx

という感じで、dyld::fastBindLazySymbol(ImageLoader**, unsigned long) のアドレスが予想通り取得されている。で、このアドレスが _dyld_fast_stub_entry() の p に代入されると。p は static 変数なので以降の呼び出しでは この dyld_func_lookup() の呼び出しは行われない。


ここでちょっと整理。dyld_stub_binder → _dyld_fast_stub_entry(void*, long) → _dyld_func_lookup("__dyld_fast_stub_entry", (void**)&p) で p に dyld::fastBindLazySymbol(ImageLoader**, unsigned long) のアドレスが取得される ← いまここ


で、_dyld_fast_stub_entry(void*, long) の

    return p(loadercache, lazyinfo);

という部分で、dyld::fastBindLazySymbol(ImageLoader**, unsigned long) が呼び出される。
dyld::fastBindLazySymbol(ImageLoader**, unsigned long) は dyld.cpp で次のように定義されている。

#if COMPRESSED_DYLD_INFO_SUPPORT
uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset)
{
    uintptr_t result = 0;
    // get image 
    if ( *imageLoaderCache == NULL ) {
        // save in cache
        *imageLoaderCache = dyld::findMappedRange((uintptr_t)imageLoaderCache);
        if ( *imageLoaderCache == NULL ) {
            const char* message = "fast lazy binding from unknown image";
            dyld::log("dyld: %s\n", message);
            halt(message);
        }
    }
	
    // bind lazy pointer and return it
    try {
        result = (*imageLoaderCache)->doBindFastLazySymbol(lazyBindingInfoOffset, gLinkContext);
    }
    catch (const char* message) {
        dyld::log("dyld: lazy symbol binding failed: %s\n", message);
        halt(message);
    }

    // return target address to glue which jumps to it with real parameters restored
    return result;
}
#endif // COMPRESSED_DYLD_INFO_SUPPORT


ここで、imageLoaderCache と lazyBindingInfoOffset がどのような値であったかを復習。_dyld_fast_stub_entry() の中で dyld::fastBindLazySymbol が呼び出される場所は

0x00007fff8010fc82 <_Z21_dyld_fast_stub_entryPvl+51>:   mov    %rbx,%rsi
0x00007fff8010fc85 <_Z21_dyld_fast_stub_entryPvl+54>:   mov    %r12,%rdi
0x00007fff8010fc88 <_Z21_dyld_fast_stub_entryPvl+57>:   mov    -0x100d9547(%rip),%r11        # 0x7fff70036748 <_ZZ21_dyld_fast_stub_entryPvlE1p>

の部分なので _Z21_dyld_fast_stub_entryPvl+57 でブレイクして rdi(第一引数) と rsi(第二引数) を確認。

(gdb) i reg rip
rip            0x7fff8010fc85   0x7fff8010fc85 <_dyld_fast_stub_entry(void*, long)+54>
(gdb) i reg rdi rsi
rdi            0x100001008      4294971400
rsi            0x0      0

この値はどこから来ているかというと、以下のようなスタブヘルパで設定されたもの。(詳しくは前回、前々回を参照)

0x100000f3a < stub helpers>:    lea    0xc7(%rip),%r11        # 0x100001008
0x100000f41 < stub helpers+7>:  push   %r11
0x100000f43 < stub helpers+9>:  jmpq   *0xb7(%rip)        # 0x100001000
0x100000f49 < stub helpers+15>: nop    
0x100000f4a < stub helpers+16>: pushq  $0x0
0x100000f4f < stub helpers+21>: jmpq   0x100000f3a < stub helpers>
0x100000f54 < stub helpers+26>: pushq  $0xe
0x100000f59 < stub helpers+31>: jmpq   0x100000f3a < stub helpers>

この stub helpers+16 でプッシュされた 0x0 が lazyBindingInfoOffset、 stub helpers+7 でプッシュされた 0x100001008 が imageLoaderCache となっている。0x100001008 と言うのは hoge1 の __nl_symbol_ptr セクション内の2個目のノンレジーポインタのアドレス。


では、dyld::fastBindLazySymbol を観てみよう。実行中のものをディスアセンブルしたものはこれ。

Dump of assembler code for function __dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm:
0x00007fff5fc04721 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+0>:    push   %rbp
0x00007fff5fc04722 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+1>:    mov    %rsp,%rbp
0x00007fff5fc04725 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+4>:    push   %rbx
0x00007fff5fc04726 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+5>:    sub    $0x8,%rsp
0x00007fff5fc0472a <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+9>:    lea    0x3c26f(%rip),%rcx        # 0x7fff5fc409a0 <__dyld__ZN4dyldL18sMappedRangesStartE>
0x00007fff5fc04731 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+16>:   cmpq   $0x0,(%rdi)
0x00007fff5fc04735 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+20>:   jne    0x7fff5fc04770 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+79>
0x00007fff5fc04737 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+22>:   jmpq   0x7fff5fc047c1 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+160>
0x00007fff5fc0473c <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+27>:   mov    (%rax,%rcx,1),%rdx
0x00007fff5fc04740 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+31>:   test   %rdx,%rdx
0x00007fff5fc04743 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+34>:   je     0x7fff5fc04753 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+50>
0x00007fff5fc04745 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+36>:   cmp    0x8(%rax,%rcx,1),%rdi
0x00007fff5fc0474a <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+41>:   jb     0x7fff5fc04753 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+50>
0x00007fff5fc0474c <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+43>:   cmp    0x10(%rax,%rcx,1),%rdi
0x00007fff5fc04751 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+48>:   jb     0x7fff5fc0476d <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+76>
0x00007fff5fc04753 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+50>:   add    $0x18,%rax
0x00007fff5fc04757 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+54>:   cmp    $0x2580,%rax
0x00007fff5fc0475d <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+60>:   jne    0x7fff5fc0473c <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+27>
0x00007fff5fc0475f <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+62>:   mov    0x2580(%rcx),%rcx
0x00007fff5fc04766 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+69>:   test   %rcx,%rcx
0x00007fff5fc04769 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+72>:   jne    0x7fff5fc047c1 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+160>
0x00007fff5fc0476b <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+74>:   jmp    0x7fff5fc047c8 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+167>
0x00007fff5fc0476d <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+76>:   mov    %rdx,(%rdi)
0x00007fff5fc04770 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+79>:   mov    (%rdi),%rdi
0x00007fff5fc04773 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+82>:   mov    (%rdi),%rax
0x00007fff5fc04776 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+85>:   lea    0x3bd23(%rip),%rdx        # 0x7fff5fc404a0 <__dyld__ZN4dyld12gLinkContextE>
0x00007fff5fc0477d <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+92>:   callq  *0x110(%rax)
0x00007fff5fc04783 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+98>:   jmp    0x7fff5fc047f0 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+207>
0x00007fff5fc04785 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+100>:  mov    %rax,%rbx
0x00007fff5fc04788 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+103>:  dec    %rdx
0x00007fff5fc0478b <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+106>:  jne    0x7fff5fc047b9 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+152>
0x00007fff5fc0478d <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+108>:  mov    %rax,%rdi
0x00007fff5fc04790 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+111>:  callq  0x7fff5fc19751 <__dyld___cxa_begin_catch>
0x00007fff5fc04795 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+116>:  mov    %rax,%rbx
0x00007fff5fc04798 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+119>:  mov    %rax,%rsi
0x00007fff5fc0479b <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+122>:  lea    0x2119e(%rip),%rdi        # 0x7fff5fc25940
0x00007fff5fc047a2 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+129>:  xor    %eax,%eax
0x00007fff5fc047a4 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+131>:  callq  0x7fff5fc02973 <__dyld__ZN4dyld3logEPKcz>
0x00007fff5fc047a9 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+136>:  mov    %rbx,%rdi
0x00007fff5fc047ac <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+139>:  callq  0x7fff5fc046cb <__dyld__ZN4dyld4haltEPKc>
0x00007fff5fc047b1 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+144>:  mov    %rax,%rbx
0x00007fff5fc047b4 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+147>:  callq  0x7fff5fc197cf <__dyld___cxa_end_catch>
0x00007fff5fc047b9 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+152>:  mov    %rbx,%rdi
0x00007fff5fc047bc <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+155>:  callq  0x7fff5fc24b26 <__dyld__Unwind_Resume>
0x00007fff5fc047c1 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+160>:  xor    %eax,%eax
0x00007fff5fc047c3 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+162>:  jmpq   0x7fff5fc0473c <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+27>
0x00007fff5fc047c8 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+167>:  movq   $0x0,(%rdi)
0x00007fff5fc047cf <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+174>:  lea    0x21192(%rip),%rsi        # 0x7fff5fc25968
0x00007fff5fc047d6 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+181>:  lea    0x21154(%rip),%rdi        # 0x7fff5fc25931
0x00007fff5fc047dd <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+188>:  xor    %eax,%eax
0x00007fff5fc047df <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+190>:  callq  0x7fff5fc02973 <__dyld__ZN4dyld3logEPKcz>
0x00007fff5fc047e4 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+195>:  lea    0x2117d(%rip),%rdi        # 0x7fff5fc25968
0x00007fff5fc047eb <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+202>:  callq  0x7fff5fc046cb <__dyld__ZN4dyld4haltEPKc>
0x00007fff5fc047f0 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+207>:  add    $0x8,%rsp
0x00007fff5fc047f4 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+211>:  pop    %rbx
0x00007fff5fc047f5 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+212>:  leaveq 
0x00007fff5fc047f6 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+213>:  retq   
End of assembler dump.


*imageLoaderCacheの値は dyld::fastBindLazySymbol のアドレスである 0x7fff5fc04721 まで実行したところで次のようにして確認。

(gdb) i reg rip
rip            0x7fff5fc04721   0x7fff5fc04721 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm>
(gdb) x /1xg $rdi
0x100001008:    0x0000000000000000

つまり *imageLoaderCache は NULL ですね。NULL なので dyld::findMappedRange が呼び出される。アセンブリを見てみると、この dyld::findMappedRange はインライン展開されていることがわかる。


dyld::findMappedRrange は dyld.cpp で次のように定義されている。

ImageLoader* findMappedRange(uintptr_t target)
{
    for (MappedRanges* p = &sMappedRangesStart; p != NULL; p = p->next) {
        for (int i=0; i < MappedRanges::count; ++i) {
            if ( p->array[i].image != NULL ) {
                if ( (p->array[i].start <= target) && (target < p->array[i].end) )
                    return p->array[i].image;
            }
        }
    }
    return NULL;
}


MappedRanges, sMappedRangesStart と言うのは dyld.cpp で次のように定義されている。

// The MappedRanges structure is used for fast address->image lookups.
// The table is only updated when the dyld lock is held, so we don't
// need to worry about multiple writers.  But readers may look at this
// data without holding the lock. Therefore, all updates must be done
// in an order that will never cause readers to see inconsistent data.
// The general rule is that if the image field is non-NULL then
// the other fields are valid.
//
struct MappedRanges
{
    enum { count=400 };
    struct {
        ImageLoader*   image;
        uintptr_t      start;
        uintptr_t      end;
    } array[count];
    MappedRanges*  next;
};

static MappedRanges    sMappedRangesStart;

あくまで推測だが、実行形式や動的ライブラリがロードされている場合、あるアドレス範囲へファイルをロードした ImageLoader というものが有って、それがこの構造体に格納されているということなのかもしれない。
であるとすれば、ここでの findMappedRange() 呼び出しは hoge1 の __DATA,__la_symbol_ptr をロードした ImageLoader を取得しているということになる。


sMappedRangesStart のアドレス、つまり __dyld__ZN4dyldL18sMappedRangesStartE のアドレスはディスアセンブル結果から 0x7fff5fc409a0 とわかるので少し内容を覗いてみる。

(gdb) x /30xg 0x7fff5fc409a0
0x7fff5fc409a0 <__dyld__ZN4dyldL18sMappedRangesStartE>: 0x00007fff5fc43c18      0x0000000100000000
0x7fff5fc409b0 <__dyld__ZN4dyldL18sMappedRangesStartE+16>:      0x0000000100003000      0x00007fff5fc43cb8
0x7fff5fc409c0 <__dyld__ZN4dyldL18sMappedRangesStartE+32>:      0x0000000100003000      0x0000000100005000
0x7fff5fc409d0 <__dyld__ZN4dyldL18sMappedRangesStartE+48>:      0x00007fff5fc43d88      0x00007fff80103000
0x7fff5fc409e0 <__dyld__ZN4dyldL18sMappedRangesStartE+64>:      0x00007fff802c3000      0x00007fff5fc43d88
0x7fff5fc409f0 <__dyld__ZN4dyldL18sMappedRangesStartE+80>:      0x00007fff7001a000      0x00007fff7003d000
0x7fff5fc40a00 <__dyld__ZN4dyldL18sMappedRangesStartE+96>:      0x00007fff5fc43d88      0x00007fff870f6000
0x7fff5fc40a10 <__dyld__ZN4dyldL18sMappedRangesStartE+112>:     0x00007fff889d9000      0x00007fff5fc43e20
0x7fff5fc40a20 <__dyld__ZN4dyldL18sMappedRangesStartE+128>:     0x00007fff86b99000      0x00007fff86b9e000
0x7fff5fc40a30 <__dyld__ZN4dyldL18sMappedRangesStartE+144>:     0x00007fff5fc43e20      0x00007fff870f6000
0x7fff5fc40a40 <__dyld__ZN4dyldL18sMappedRangesStartE+160>:     0x00007fff889d9000      0x0000000000000000
0x7fff5fc40a50 <__dyld__ZN4dyldL18sMappedRangesStartE+176>:     0x0000000000000000      0x0000000000000000
0x7fff5fc40a60 <__dyld__ZN4dyldL18sMappedRangesStartE+192>:     0x0000000000000000      0x0000000000000000
0x7fff5fc40a70 <__dyld__ZN4dyldL18sMappedRangesStartE+208>:     0x0000000000000000      0x0000000000000000
0x7fff5fc40a80 <__dyld__ZN4dyldL18sMappedRangesStartE+224>:     0x0000000000000000      0x0000000000000000

MappedRanges構造体として見ると ImageLoaderのアドレス、ロードした範囲の開始、終了、がそれっぽい数字で並んでいる。今は 0x100001008 をロードしたImageLoaderを探しているので、0x00007fff5fc43c18 が取得されるはず。

C++

		result = (*imageLoaderCache)->doBindFastLazySymbol(lazyBindingInfoOffset, gLinkContext);

は、アセンブリではここに該当。

0x00007fff5fc04770 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+79>:   mov    (%rdi),%rdi
0x00007fff5fc04773 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+82>:   mov    (%rdi),%rax
0x00007fff5fc04776 <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+85>:   lea    0x3bd23(%rip),%rdx        # 0x7fff5fc404a0 <__dyld__ZN4dyld12gLinkContextE>
0x00007fff5fc0477d <__dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm+92>:   callq  *0x110(%rax)

C++ではインスタンスメソッド呼び出しの第一引数(rdi)はインスタンスのアドレスなので callq 時の rdi の値を見れば取得された ImageLoader のアドレスを確認できる。

(gdb) x /1x $rdi
0x100001008:    0x00007fff5fc43c18
(gdb) b *0x00007fff5fc0477d
Breakpoint 8 at 0x7fff5fc0477d
(gdb) c
Continuing.

Breakpoint 8, 0x00007fff5fc0477d in __dyld__ZN4dyld18fastBindLazySymbolEPP11ImageLoaderm ()
(gdb) i reg rdi
rdi            0x7fff5fc43c18   140734800083992

予想通り 0x7fff5fc43c18 が取得されている。

次に、 ImageLoader::doBindFastLazySymbol の呼出なわけだが、これは ImageLoader.h で次のように定義されている。

    // called at runtime when a fast lazily bound function is first called
    virtual uintptr_t    doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context) = 0;

純粋仮想関数。上記アセンブリでraxをゴニョゴニョしているのは、この実装を呼び出すために仮想関数テーブルがうんたらな処理をしているからだとおもう(仮想関数の仕組はまだよくわかりません。そのうち調べよう ^^;)


なのでここはサクっとgdbに頼って次へ進む。callq 先はこうなった。

0x00007fff5fc168a0 in __dyld__ZN26ImageLoaderMachOCompressed20doBindFastLazySymbolEjRKN11ImageLoader11LinkContextE ()

ImageLoaderMachOCompressed::doBindFastLazySymbol(unsigned int, ImageLoader::LinkContext const&) が呼び出されている。これは ImageLoaderMachOCompressed.cpp で次のように定義されている。

uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(
        uint32_t lazyBindingInfoOffset, const LinkContext& context) {
    const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off;
    const uint8_t* const end = &start[fDyldInfo->lazy_bind_size];
    if (lazyBindingInfoOffset > fDyldInfo->lazy_bind_size)
        throw "fast lazy bind offset out of range";

    uint8_t type = BIND_TYPE_POINTER;
    uintptr_t address = 0;
    const char* symbolName = NULL;
    uint8_t symboFlags = 0;
    int libraryOrdinal = 0;
    bool done = false;
    uintptr_t result = 0;
    const uint8_t* p = &start[lazyBindingInfoOffset];
    while (!done && (p < end)) {
        uint8_t immediate = *p & BIND_IMMEDIATE_MASK;
        uint8_t opcode = *p & BIND_OPCODE_MASK;
        ++p;
        switch (opcode) {
        case BIND_OPCODE_DONE:
            done = true;
            break;
        case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
            libraryOrdinal = immediate;
            break;
        case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
            libraryOrdinal = read_uleb128(p, end);
            break;
        case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
            // the special ordinals are negative numbers
            if (immediate == 0)
                libraryOrdinal = 0;
            else {
                int8_t signExtended = BIND_OPCODE_MASK | immediate;
                libraryOrdinal = signExtended;
            }
            break;
        case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
            symbolName = (char*) p;
            symboFlags = immediate;
            while (*p != '\0')
                ++p;
            ++p;
            break;
        case BIND_OPCODE_SET_TYPE_IMM:
            type = immediate;
            break;
        case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
            if (immediate > fSegmentsCount)
                dyld::throwf(
                        "BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n",
                        immediate, fSegmentsCount);
            address = segActualLoadAddress(immediate) + read_uleb128(p, end);
            break;
        case BIND_OPCODE_DO_BIND:
            result = this->bindAt(context, address, type, symbolName, 0, 0,
                    libraryOrdinal, "lazy ", NULL);
            break;
        case BIND_OPCODE_SET_ADDEND_SLEB:
        case BIND_OPCODE_ADD_ADDR_ULEB:
        case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
        case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
        case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
        default:
            dyld::throwf("bad lazy bind opcode %d", *p);
        }
    }
    return result;
}

LinkContext、fLinkEditBase、fDyldInfo とは一体どういうデータなのか・・・。これを知るためにはローディングの仕組を理解する必要があるのかなぁ。もう少しで、動的ライブラリの関数がバインドされる瞬間を見れそうな気がするのだが・・・とりあえず今日はここまで。

まとめ

  • dyld の APIは文字列で索引を引いて呼び出す
  • 実行形式やライブラリがロードされているとき、ロードしたアドレス範囲ごとにImageLoaderのインスタンスが有る(と思われる)
  • 関数のアドレスが解決されるのは ImageLoaderMachOCompressed の中(と思われる)