Introduction

備忘録。
自作の dylib を簡単なコンソールプログラムにリンクしたところ、下記のようなライブラリが読み込めないというエラーを吐いてしまった。

1
2
3
4
5
$ ./program
dyld[2294]: Library not loaded: @rpath/libSample.dylib
Referenced from: <81E10092-BA58-35F7-A9A7-F289A3E87B8F> /Users/xxxxxxxx/Demo/CMake/14_ObjectiveCWrapper/install/osx/x86_64/program/bin/program
Reason: tried: '/System/Volumes/Preboot/Cryptexes/OS@rpath/libSample.dylib' (no such file), '/usr/local/lib/libSample.dylib' (no such file), '/usr/lib/libSample.dylib' (no such file, not in dyld cache)
zsh: abort ./program

単純な疑問として、Linux のように同じディレクトリにライブラリを置くだけではダメなの?って思った。

How to resolve?

対処手段は幾つかある。

1. DYLD_LIBRARY_PATH を指定する

実行時に dylib を読み込む場所を指定する。

1
$ DYLD_LIBRARY_PATH=$PWD ./program

上記ではカレントディレクトリから dylib を読み込むように指示して実行するので、program と同じディレクトリに dylib を配置すれば解決。

2. 実行ファイルを書き換える

DYLD_LIBRARY_PATH で指定するのは一時的な方法だが、恒久的に対処する方法がある。
まず、otool をつかって動的ライブラリの読込先を確認する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ otool -l ./program
./program:
Load command 0
cmd LC_SEGMENT_64
cmdsize 72
...
Load command 13
cmd LC_LOAD_DYLIB
cmdsize 96
name /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (offset 24)
time stamp 2 Thu Jan 1 09:00:02 1970
current version 1971.0.0
compatibility version 300.0.0
Load command 14
cmd LC_LOAD_DYLIB
cmdsize 48
name @rpath/libSample.dylib (offset 24)
time stamp 2 Thu Jan 1 09:00:02 1970
current version 0.0.0
compatibility version 0.0.0
Load command 15
cmd LC_LOAD_DYLIB
cmdsize 48
name /usr/lib/libc++.1.dylib (offset 24)
time stamp 2 Thu Jan 1 09:00:02 1970
current version 1500.65.0
compatibility version 1.0.0
Load command 16
cmd LC_LOAD_DYLIB
cmdsize 56
name /usr/lib/libSystem.B.dylib (offset 24)
time stamp 2 Thu Jan 1 09:00:02 1970
current version 1319.100.3
compatibility version 1.0.0
...

または

1
2
3
4
5
6
$ otool -L program       
program:
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1971.0.0)
@rpath/libSample.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1500.65.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.100.3)

でもよい。

とにかく、目的の動的ライブラリの読込み先が @rpath/libSample.dylib であることがわかる。
では、この @rpath は何かというと、実行時に動的ライブラリを探索するパスのリストを示している。
問題の出力メッセージにもあったように、

  • ‘/System/Volumes/Preboot/Cryptexes/OS@rpath/libSample.dylib’ (no such file)
  • ‘/usr/local/lib/libSample.dylib’ (no such file)
  • ‘/usr/lib/libSample.dylib’ (no such file, not in dyld cache)

というように、システムパスを見に行っている。
ここに、カレントディレクトリのような任意のディレクトリを追加できればいい。

ちなみに rpathRPATH (Runpath Search Paths) であって Reference ではない。

具多的な対処は下記。

  • executable_path を指定
1
$ install_name_tool -add_rpath "@executable_path/." program

otool -l の結果が変化する。

1
2
3
4
5
6
7
8
9
10
$ otool -l ./program
./program:
Load command 0
cmd LC_SEGMENT_64
cmdsize 72
...
Load command 19
cmd LC_RPATH
cmdsize 32
path @executable_path/. (offset 12)
  • loader_path を指定
1
$ install_name_tool -add_rpath "@loader_path/." program

otool -l の結果が変化する。

1
2
3
4
5
6
7
8
9
10
$ otool -l ./program
./program:
Load command 0
cmd LC_SEGMENT_64
cmdsize 72
...
Load command 19
cmd LC_RPATH
cmdsize 32
path @loader_path/. (offset 12)

どちらも、最後に追加のセクションが現れる。
otool -L の結果は変化しないので注意。

@executable_path@loader_path の違いは

  • @executable_path は、実行時にローダーが実際のパスに置換するもので、上記の場合は 実行ファイルから見て同一のディレクトリ になる
    • 実行可能ファイルがディスク上に存在する場所である。
    • @executable_path/.. なら一つ上のディレクトリになる。
  • @loader_path は、実行時にローダーが実際のパスに置換するもので、上記の場合は 現在読み込まれているバイナリ (実行可能ファイルやライブラリ自体) から見て同一のディレクトリ になる
    • 現在読み込んでいるバイナリ (それがアプリケーション、ライブラリ、フレームワークなどであるかに関係なく) がディスク上に存在する場所である。
    • @loader_path/.. なら一つ上のディレクトリになる。

3. ビルド時に対処する

CMake で対処するとしたら下記のようにする。

1
2
3
4
5
6
if (APPLE)
set_target_properties(MyExecutable PROPERTIES
BUILD_WITH_INSTALL_RPATH TRUE
INSTALL_RPATH "@executable_path;@executable_path/lib"
)
endif()

ビルド後に生成されたプログラムに対する、otool -l の結果が変化する。

1
2
3
4
5
6
7
8
9
10
11
12
$ otool -l ./program
./program:
...
Load command 17
cmd LC_RPATH
cmdsize 32
path @executable_path (offset 12)
Load command 18
cmd LC_RPATH
cmdsize 40
path @executable_path/lib (offset 12)
...

INSTALL_RPATH は複数指定可能で、上記なら、実行ファイルと同じディレクトリまたは実行ファイルと同じディレクトリにある lib ディレクトリからライブラリを探しに行く。