dylib не может загрузить libstd при компиляции в рабочей области - PullRequest
5 голосов
/ 21 марта 2019

У меня есть проект со следующей структурой:

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

Где:

  • my_lib - это библиотека Rust с crate-type = ["dylib"]
  • my_bin - двоичное приложение Rust, использующее my_lib
  • my_script.py - это скрипт на Python 3, который также использует my_lib

Корень Cargo.toml содержит базовое объявление рабочей области:

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

Все работает правильно, если я выполняю cargo build и cargo 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/...), у сценария не возникнет проблем при загрузке библиотеки и выполнении ее функций.

Но если я использую файл библиотеки из каталога рабочего пространства (./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 устраняет проблему. Это, однако, далеко от идеала и не переносимо. Я также не понимаю, почему эта проблема возникает только при использовании рабочей области target .

Итак, почему цель рабочего пространства не может найти ржавчину libstd, когда каждая цель проекта может? Есть ли способ исправить эту проблему, при которой не нужно находить путь к цепочке инструментов и изменять переменную среды?

1 Ответ

3 голосов
/ 26 марта 2019

Динамическое связывание иногда нелегко. Сообщение об ошибке 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 mybin против target/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. Если вы не знаете, что это такое, я бы порекомендовал прочитать сообщения Майка Эша:

Также запустите man dlopen и прочитайте раздел SEARCHING. Копировать и вставлять здесь довольно долго, поэтому просто первое предложение:

dlopen () ищет совместимый файл Mach-O в каталогах, заданных набором переменных среды и текущим рабочим каталогом процесса.

Вы узнаете о DYLD_LIBRARY_PATH и других переменных среды.

Какой вывод команды 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 . SIP защищает папки типа /usr, /bin, /sbin, но не защищает /usr/local, например.

Что это значит? SIP делает много вещей, но одна из них разрушает значение DYLD_LIBRARY_PATH. Шебанг линии, как ...

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

... не будет работать для вас. Вы должны использовать интерпретатор Python, который не установлен в системных (и защищенных) папках. Установите один с помощью brew, установите Anaconda, ...

SIP можно отключить, но НЕ сделать это.

Другой способ исправить это - заменить @rpath в вашем mylib на полный путь через install_name_tool (man install_name_tool). Дополнительная информация в Почему для библиотек Mach-O в Mac Os X необходимы install_name_tool и otool? .

Пример:

$ 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

Как видите, @rpath сейчас нет, DYLD_LIBRARY_PATH не установлен, но работает (Hallo выводится через hallo функцию из libmylib.dylib) с системным интерпретатором Python.

Помните об одном: динамические библиотеки macOS ведут себя иначе, чем, например, в Linux.

Если вы не хотите с ним связываться, вы можете изменить mylib crate-type на ["rlib", "cdylib"], но это не то, что вы, вероятно, хотите.

...