在工作空间中编译时,dylib无法加载libstd

时间:2019-03-21 14:00:41

标签: python macos rust dylib rust-cargo

我有一个结构如下的项目:

Cargo.toml
my_script.py
my_lib:
    - Cargo.toml
    - src
my_bin:
    - Cargo.toml
    - src

位置:

  • my_lib是带有crate-type = ["dylib"]
  • 的Rust库
  • my_bin是使用my_lib
  • 的Rust二进制应用程序
  • my_script.py是Python 3脚本,也使用my_lib

Cargo.toml包含一个基本的工作区声明:

[workspace]
members = [
    "my_lib",
    "my_bin"
]

如果我执行cargo buildcargo run -p my_bin,则一切正常。问题出在Python脚本中。

在此脚本中,我使用以下代码加载my_lib lib文件:

from ctypes import cdll
from sys import platform

if platform == 'darwin':
    prefix = 'lib'
    ext = 'dylib'
elif platform == 'win32':
    prefix = ''
    ext = 'dll'
else:
    prefix = 'lib'
    ext = 'so'

# Working path:
# lib_path = './my_lib/target/debug/{}my_lib.{}'.format(prefix, ext)

# Buggy "Library not loaded: @rpath/libstd-d00eaa6834e55536.dylib" path:
lib_path = './target/debug/{}my_lib.{}'.format(prefix, ext)

lib = cdll.LoadLibrary(lib_path)
my_func = lib.my_func
my_func()

如果我使用来自库目录./my_lib/target/...)中的库文件,则脚本在加载库和执行其功能方面没有问题。

但是,如果我使用 workspace目录./target/...)中的库文件,则在尝试加载库时出现以下错误:

OSError: dlopen(./target/debug/libpeglrs.dylib, 6): Library not loaded: @rpath/libstd-d00eaa6834e55536.dylib

以相同的方式,尝试直接从工作空间目标目录执行my_bin会产生相同的错误(即使cargo run -p my_bin可以正常工作)。

使用软件“ Dependency Walker”,我发现my_lib库找不到Rust libstd库(具有前面的错误消息说明)。

手动将包含Rust工具链库的路径导出到环境PATH中可解决此问题。然而,这远非理想且不便携。我也不明白为什么仅在使用工作区目标时会出现此问题。

那么,为什么每个项目目标都可以找到工作区目标libstd时生锈?有没有一种方法可以解决此问题,而无需找到工具链路径并修改环境变量?

1 个答案:

答案 0 :(得分:3)

有时动态链接并不容易。错误消息Library not loaded: @rpath/libstd-d00eaa6834e55536.dylib很清楚。您遇到DYLD_LIBRARY_PATH(macOS)的问题。

TL; DR

您的DYLD_LIBRARY_PATH不包含Rust库路径。将以下内容放入您的~/.bash_profile

source "$HOME/.cargo/env"
export RUST_SRC_PATH="$(rustc --print sysroot)/lib/rustlib/src/rust/src"
export DYLD_LIBRARY_PATH="$(rustc --print sysroot)/lib:$DYLD_LIBRARY_PATH"

说明

我遵循了您的项目结构,但有一件事情-我删除了_my_bin-> mybin,...)。

cargo run --bin mybintarget/debug/mybin

首先,请检查otool -L target/debug/mybin的内容:

target/debug/mybin:
    /Users/robertvojta/Work/bar/target/debug/deps/libmylib.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libstd-d4fbe66ddea5f3ce.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)
    /usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)

请注意@rpath。如果您不知道这是什么,我建议您阅读Mike Ash的帖子:

也运行man dlopen并阅读SEARCHING部分。复制并粘贴到这里很长,所以,只有第一句话:

  

dlopen()在一组环境变量指定的目录和进程的当前工作目录中搜索兼容的Mach-O文件。

您将了解DYLD_LIBRARY_PATH和其他环境变量。

在您的Shell中,echo $DYLD_LIBRARY_PATH命令的输出是什么?我认为它是空的/不包含Rust库路径。

将以下几行添加到您的mybin:main.rs ...

println!(
    "DYLD_LIBRARY_PATH={}",
    std::env::var("DYLD_LIBRARY_PATH").unwrap_or("N/A".to_string())
);

...并运行cargo run --bin mybin。您应该会看到类似这样的内容:

DYLD_LIBRARY_PATH=~/.rustup/toolchains/stable-x86_64-apple-darwin/lib

cargo run为您注入此环境变量。

从哪里可以获得正确的价值?运行rustc --print sysroot并将/lib附加到输出中。

如果您想直接运行mybin(不运行cargo),可以按照以下方式进行:

DYLD_LIBRARY_PATH="$(rustc --print sysroot)/lib:$DYLD_LIBRARY_PATH" target/debug/mybin

Python脚本

run.py脚本中添加类似的行:

import os
print('DYLD_LIBRARY_PATH: {}'.format(os.environ.get('DYLD_LIBRARY_PATH', 'N/A')))

如果它打印N/A,则未设置DYLD_LIBRARY_PATH。您可以通过类似的方式解决此问题:

DYLD_LIBRARY_PATH="$(rustc --print sysroot)/lib:$DYLD_LIBRARY_PATH" python run.py

macOS和系统完整性保护

请注意,您不能为此使用系统Python ...

$ echo $DYLD_LIBRARY_PATH
~/.rustup/toolchains/stable-x86_64-apple-darwin/lib:
$ /usr/bin/python run.py
DYLD_LIBRARY_PATH: N/A
Traceback (most recent call last):
  File "./run.py", line 21, in <module>
    lib = cdll.LoadLibrary(lib_path)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ctypes/__init__.py", line 443, in LoadLibrary
    return self._dlltype(name)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ctypes/__init__.py", line 365, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: dlopen(./target/debug/libmylib.dylib, 6): Library not loaded: @rpath/libstd-d4fbe66ddea5f3ce.dylib
  Referenced from: /Users/robertvojta/Work/bar/target/debug/libmylib.dylib
  Reason: image not found

...但是您可以使用通过brew安装的一个...

$ echo $DYLD_LIBRARY_PATH
/Users/robertvojta/.rustup/toolchains/stable-x86_64-apple-darwin/lib:
$ /usr/local/bin/python run.py
DYLD_LIBRARY_PATH: /Users/robertvojta/.rustup/toolchains/stable-x86_64-apple-darwin/lib:

原因是SIP。 SIP是在El Capitan中引入的,可能会以您的方式出现。您可以体验以下内容:

$ env | grep DYLD
$ echo $DYLD_LIBRARY_PATH
/Users/robertvojta/.rustup/toolchains/stable-x86_64-apple-darwin/lib:

这是SIP description页面。 SIP保护/usr/bin/sbin等文件夹,但不保护/usr/local

是什么意思? SIP可以做很多事情,但是其中之一就是破坏DYLD_LIBRARY_PATH的价值。舍邦线像...

  • #!/usr/bin/env python
  • #!/usr/bin/python

...不会为您工作。您必须使用未安装在系统(受保护)文件夹中的Python解释器。通过brew安装一个,安装Anaconda,...

可以禁用SIP,但请勿执行此操作。

解决此问题的另一种方法是通过@rpathmylib)用完整路径替换install_name_tool中的man install_name_toolWhy is install_name_tool and otool necessary for Mach-O libraries in Mac Os X?中的更多信息。

示例:

$ otool -L target/debug/mybin
target/debug/mybin:
    /Users/robertvojta/Work/bar/target/debug/deps/libmylib.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libstd-d4fbe66ddea5f3ce.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)
    /usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)
$ install_name_tool -change @rpath/libstd-d4fbe66ddea5f3ce.dylib /Users/robertvojta/.rustup/toolchains/stable-x86_64-apple-darwin/lib/libstd-d4fbe66ddea5f3ce.dylib target/debug/libmylib.dylib
$ otool -L target/debug/libmylib.dylib
target/debug/libmylib.dylib:
    /Users/robertvojta/Work/bar/target/debug/deps/libmylib.dylib (compatibility version 0.0.0, current version 0.0.0)
    /Users/robertvojta/.rustup/toolchains/stable-x86_64-apple-darwin/lib/libstd-d4fbe66ddea5f3ce.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)
    /usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)
$ /usr/bin/python run.py
DYLD_LIBRARY_PATH: N/A
Hallo

如您所见,现在没有@rpathDYLD_LIBRARY_PATH没有设置,但是可以工作({{1}通过Hallo的{​​{1}}函数打印)和系统Python解释器。

请注意一件事-例如,与Linux相比,macOS动态库的行为有所不同。

如果您不想弄乱它,可以将hallo libmylib.dylib更改为mylib,但这不是您想要的。