macOSは動的ライブラリがファイルシステムから見えないらしい

# TL;DR
macOS Big Sur 11.0.1 以降だと、ダイナミックリンクライブラリがキャッシュされていてファイルシステムから見えなくなっている(Finder.appから見えない)

# ことの発端
Ghidraを使ってバイナリを見ていたところ、ダイナミックリンクライブラリが見つからないとエラーが出た。

```bash:Ghidraのエラー
----- Loading /Users/XXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXXX -----
Linking external programs to XXXXXXXXXXXXXXXXXXXXX...
  [/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation] -> not found
  [/System/Library/Frameworks/CFNetwork.framework/Versions/A/CFNetwork] -> not found
  [/usr/lib/libresolv.9.dylib] -> not found
  [/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation] -> not found
  [/System/Library/Frameworks/ServiceManagement.framework/Versions/A/ServiceManagement] -> not found
  [/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration] -> not found
  [/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices] -> not found
  [/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit] -> not found
  [/usr/lib/libobjc.A.dylib] -> not found
  [/System/Library/Frameworks/Security.framework/Versions/A/Security] -> not found
  [/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit] -> not found
  [/System/Library/Frameworks/Virtualization.framework/Versions/A/Virtualization] -> not found
  [/System/Library/Frameworks/Hypervisor.framework/Versions/A/Hypervisor] -> not found
  [/usr/lib/libSystem.B.dylib] -> not found
```

`/usr/lib/libSystem.B.dylib` とかは明らかにシステムに無くてはならないものなのに、見つからないのはおかしい。

```bash:本当に存在しないlibSystem.B.dylib
cha84rakanal@ ~ % ls /usr/lib/libSystem.B.dylib
ls: /usr/lib/libSystem.B.dylib: No such file or directory
cha84rakanal@ ~ % 
```

# 公式ドキュメントより

> New in macOS Big Sur 11.0.1, the system ships with a built-in dynamic linker cache of all system-provided libraries. As part of this change, copies of dynamic libraries are no longer present on the filesystem. Code that attempts to check for dynamic library presence by looking for a file at a path or enumerating a directory will fail. Instead, check for library presence by attempting to dlopen() the path, which will correctly check for the library in the cache. (62986286)

https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11_0_1-release-notes#Kernel

macOS Big Sur 11.0.1 よりダイナミックリンクライブラリはファイルシステム上に存在しなくなりました。と書いてありました。プログラムの実行時、OS側がよしなに取り扱っているわけですね。ちなみに、ダイナミックリンクライブラリのキャッシュの実体は、 `/System/Library/dyld` においてあります。

# dlopenは動作する

ファイルシステム上に存在しないパスに対して、ダイナミックリンクライブラリを動的にロードする `dlopen` 関数が動作するか確認してみる。確認には、次のプログラムを使った。

```c:dltest.c
#include <mach-o/dyld.h>
#include <dlfcn.h>
#include <stdio.h>

int library_exists(const char* path) {
    void* handle = dlopen(path, RTLD_LAZY);
    if (handle == NULL) return 0;
    dlclose(handle);
    return 1;
}

int main(void) {
    int result;
    char* path = "/System/Library/Frameworks/Virtualization.framework/Versions/A/Virtualization";
    result = library_exists(path);
    printf("library_exists(%s) == %d", path, result);
}
```

```bash:
cha84rakanal % ls /System/Library/Frameworks/Virtualization.framework/Versions/A/Virtualization
ls: /System/Library/Frameworks/Virtualization.framework/Versions/A/Virtualization: No such file or directory
cha84rakanal % cc -g dltest.c && ./a.out 
library_exists(/System/Library/Frameworks/Virtualization.framework/Versions/A/Virtualization) == 1
cha84rakanal % 
```

ファイルが無いので、 `0` を期待しましたが`1` が帰ってきてるので`dlopen` 関数が動作していますね。

dyldの `dlopen` 関数の解剖については、WIPです。

<!--
## `dlopen`の分解

lldbを使って確認

```
libdyld.dylib`dlopen:
    0x184caf29c <+0>:  mov    x2, x1
    0x184caf2a0 <+4>:  mov    x1, x0
    0x184caf2a4 <+8>:  adrp   x8, 364425
    0x184caf2a8 <+12>: ldr    x0, [x8, #0x670]
    0x184caf2ac <+16>: ldr    x16, [x0]
    0x184caf2b0 <+20>: mov    x17, x0
    0x184caf2b4 <+24>: movk   x17, #0x63fa, lsl #48
    0x184caf2b8 <+28>: autda  x16, x17
    0x184caf2bc <+32>: ldr    x3, [x16, #0x70]!
    0x184caf2c0 <+36>: mov    x4, x16
    0x184caf2c4 <+40>: mov    x16, x4
    0x184caf2c8 <+44>: movk   x16, #0xbf31, lsl #48
->  0x184caf2cc <+48>: braa   x3, x16
```

```
(lldb) register read
General Purpose Registers:
        x0 = 0x00000001000c0060
        x1 = 0x0000000100003f50  "/System/Library/Frameworks/Virtualization.framework/Versions/A/Virtualization"
        x2 = 0x0000000000000001
        x3 = 0x0000000100031954  dyld`dyld4::APIs::dlopen(char const*, int)
        x4 = 0x000000010006e470  dyld`vtable for dyld4::APIs + 128
        x5 = 0x0000000000000000
        x6 = 0x0000000000000000
        x7 = 0x0000000000000000
        x8 = 0x00000001ddc38000  libc++.1.dylib`typeinfo for std::__1::messages<char> + 16
        x9 = 0x0000000000000002
       x10 = 0x0000000000000000
       x11 = 0x0000000000000002
       x12 = 0x0000000000000002
       x13 = 0x0000000000000000
       x14 = 0x0000000000000018
       x15 = 0x0000000000000000
       x16 = 0xbf3100010006e470 (0x000000010006e470) dyld`vtable for dyld4::APIs + 128
       x17 = 0x63fa0001000c0060
       x18 = 0x0000000000000000
       x19 = 0x00000001000c0060
       x20 = 0x0000000100003ed4  a.out`main at dltest.c:12
       x21 = 0x000000010006c070  dyld`dyld4::sConfigBuffer
       x22 = 0x0000000000000000
       x23 = 0x0000000000000000
       x24 = 0x0000000000000000
       x25 = 0x0000000000000000
       x26 = 0x0000000000000000
       x27 = 0x0000000000000000
       x28 = 0x0000000000000000
        fp = 0x000000016fdfee20
        lr = 0x0000000100003e98  a.out`library_exists + 28 at dltest.c:6:11
        sp = 0x000000016fdfee00
        pc = 0x0000000184caf2cc  libdyld.dylib`dlopen + 48
      cpsr = 0x60001000
```

braa 命令

https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/BRAA--BRAAZ--BRAB--BRABZ--Branch-to-Register--with-pointer-authentication-

https://www.slideshare.net/sounakano/arm-248296447

`dyld'dyld4::APIs::dlopen(char const*, int)` へ

```
dyld`dyld4::APIs::dlopen:
->  0x100031954 <+0>:   pacibsp 
    0x100031958 <+4>:   stp    x22, x21, [sp, #-0x30]!
    0x10003195c <+8>:   stp    x20, x19, [sp, #0x10]
    0x100031960 <+12>:  stp    x29, x30, [sp, #0x20]
    0x100031964 <+16>:  add    x29, sp, #0x20
    0x100031968 <+20>:  mov    x19, x2
    0x10003196c <+24>:  mov    x21, x1
    0x100031970 <+28>:  mov    x22, x0
    0x100031974 <+32>:  mov    x20, x30
    0x100031978 <+36>:  xpaci  x20
    0x10003197c <+40>:  ldr    x8, [x0, #0x8]
    0x100031980 <+44>:  ldr    w8, [x8, #0x34]
    0x100031984 <+48>:  cmp    w8, #0x2
    0x100031988 <+52>:  ccmp   x1, #0x0, #0x4, eq
    0x10003198c <+56>:  b.eq   0x1000319a8               ; <+84>
    0x100031990 <+60>:  mov    x0, x21
    0x100031994 <+64>:  bl     0x10001f110               ; dyld4::Loader::leafName(char const*)
    0x100031998 <+68>:  adrp   x1, 51
    0x10003199c <+72>:  add    x1, x1, #0xbfb            ; "libGL.dylib"
    0x1000319a0 <+76>:  bl     0x10000e6f0               ; strcmp
    0x1000319a4 <+80>:  cbz    w0, 0x100031a00           ; <+172>
    0x1000319a8 <+84>:  ldr    x16, [x22]
    0x1000319ac <+88>:  mov    x17, x22
    0x1000319b0 <+92>:  movk   x17, #0x63fa, lsl #48
    0x1000319b4 <+96>:  autda  x16, x17
    0x1000319b8 <+100>: mov    x17, #0x308
    0x1000319bc <+104>: add    x16, x16, x17
    0x1000319c0 <+108>: ldr    x4, [x16]
    0x1000319c4 <+112>: mov    x5, x16
    0x1000319c8 <+116>: mov    x0, x22
    0x1000319cc <+120>: mov    x1, x21
    0x1000319d0 <+124>: mov    x2, x19
    0x1000319d4 <+128>: mov    x3, x20
    0x1000319d8 <+132>: ldp    x29, x30, [sp, #0x20]
    0x1000319dc <+136>: ldp    x20, x19, [sp, #0x10]
    0x1000319e0 <+140>: ldp    x22, x21, [sp], #0x30
    0x1000319e4 <+144>: autibsp 
    0x1000319e8 <+148>: eor    x16, x30, x30, lsl #1
    0x1000319ec <+152>: tbz    x16, #0x3e, 0x1000319f4   ; <+160>
    0x1000319f0 <+156>: brk    #0xc471
    0x1000319f4 <+160>: mov    x16, x5
    0x1000319f8 <+164>: movk   x16, #0xd48c, lsl #48
    0x1000319fc <+168>: braa   x4, x16
    0x100031a00 <+172>: ldr    x16, [x22]
    0x100031a04 <+176>: mov    x17, x22
    0x100031a08 <+180>: movk   x17, #0x63fa, lsl #48
    0x100031a0c <+184>: autda  x16, x17
    0x100031a10 <+188>: mov    x17, #0x1d0
    0x100031a14 <+192>: add    x16, x16, x17
    0x100031a18 <+196>: ldr    x8, [x16]
    0x100031a1c <+200>: mov    x9, x16
    0x100031a20 <+204>: mov    x0, x22
    0x100031a24 <+208>: mov    x1, x20
    0x100031a28 <+212>: mov    x17, x9
    0x100031a2c <+216>: movk   x17, #0xb64d, lsl #48
    0x100031a30 <+220>: blraa  x8, x17
    0x100031a34 <+224>: cbz    x0, 0x1000319a8           ; <+84>
    0x100031a38 <+228>: mov    w1, #0x2
    0x100031a3c <+232>: mov    w2, #0x1
    0x100031a40 <+236>: bl     0x100040c28               ; dyld3::MachOFile::builtForPlatform(dyld3::Platform, bool) const
    0x100031a44 <+240>: tbz    w0, #0x0, 0x1000319a8     ; <+84>
    0x100031a48 <+244>: mov    x0, #0x0
    0x100031a4c <+248>: ldp    x29, x30, [sp, #0x20]
    0x100031a50 <+252>: ldp    x20, x19, [sp, #0x10]
    0x100031a54 <+256>: ldp    x22, x21, [sp], #0x30
    0x100031a58 <+260>: retab
```

```
(lldb) register read
General Purpose Registers:
        x0 = 0x00000001000c0060
        x1 = 0x0000000100003f50  "/System/Library/Frameworks/Virtualization.framework/Versions/A/Virtualization"
        x2 = 0x0000000000000001
        x3 = 0x0000000100003e98  a.out`library_exists + 28 at dltest.c:6:11
        x4 = 0x0000000100031a5c  dyld`dyld4::APIs::dlopen_from(char const*, int, void*)
        x5 = 0x000000010006e708  dyld`vtable for dyld4::APIs + 792
        x6 = 0x0000000000000000
        x7 = 0x0000000000000000
        x8 = 0x0000000000000001
        x9 = 0x0000000000000002
       x10 = 0x0000000000000000
       x11 = 0x0000000000000002
       x12 = 0x0000000000000002
       x13 = 0x0000000000000000
       x14 = 0x0000000000000018
       x15 = 0x0000000000000000
       x16 = 0xd48c00010006e708
       x17 = 0x0000000000000308
       x18 = 0x0000000000000000
       x19 = 0x00000001000c0060
       x20 = 0x0000000100003ed4  a.out`main at dltest.c:12
       x21 = 0x000000010006c070  dyld`dyld4::sConfigBuffer
       x22 = 0x0000000000000000
       x23 = 0x0000000000000000
       x24 = 0x0000000000000000
       x25 = 0x0000000000000000
       x26 = 0x0000000000000000
       x27 = 0x0000000000000000
       x28 = 0x0000000000000000
        fp = 0x000000016fdfee20
        lr = 0x0000000100003e98  a.out`library_exists + 28 at dltest.c:6:11
        sp = 0x000000016fdfee00
        pc = 0x00000001000319fc  dyld`dyld4::APIs::dlopen(char const*, int) + 168
      cpsr = 0x40001000
```

```
->  0x100031b58 <+252>: bl     0x1000308e8               ; dyld4::APIs::findImageContaining(void*)

dyld`dyld4::RuntimeState::withLoadersReadLock:


dyld`invocation function for block in dyld4::APIs::findImageContaining(void*):
->  0x100031914 <+120>: bl     0x10001eb4c               ; dyld4::Loader::contains(dyld4::RuntimeState&, void const*, void const**, unsigned long long*, unsigned char*) const

dyld`dyld4::JustInTimeLoader::contains:

->  0x1000246d4 <+92>:  bl     0x100041ac0               ; dyld3::MachOFile::preferredLoadAddress() const


```

https://github.com/apple-oss-distributions/dyld/blob/main/common/MachOFile.cpp#L1468
```c
uint64_t MachOFile::preferredLoadAddress() const
{
    __block uint64_t textVmAddr = 0;
    forEachSegment(^(const SegmentInfo& info, bool& stop) {
        if ( strcmp(info.segName, "__TEXT") == 0 ) {
            textVmAddr = info.vmAddr;
            stop = true;
            
        }
    });
    return textVmAddr;
}
```

vmmap

-->