Оболочка расширения Ruby вокруг библиотеки C не может загрузить установленную библиотеку

Расширение Ruby davenport-ruby для библиотеки C davenport не будет правильно загружаться в Ubuntu и Debian. Он нормально работает на машине разработки (MacOS), как показано в файле README проекта Smoke test Ruby, dvt

Загрузчик RubyGems (через сборщик) компилирует и устанавливает расширение следующим образом:

~/dvt$ rm -rf ~/.bundle
~/dvt$ bundle install
Fetching gem metadata from https://rubygems.org/.
Using bundler 2.0.2
Fetching davenport 1.0.2.pre
Installing davenport 1.0.2.pre with native extensions
Bundle complete! 1 Gemfile dependency, 2 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
~/dvt$ bundle info davenport
  * davenport (1.0.2.pre)
    Summary: Ruby binding for the Davenport library
    Homepage: https://github.com/wbreeze/davenport-ruby
    Path: /home/deploy/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0\
      /gems/davenport-1.0.2.pre
~/dvt$ ls /home/deploy/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0\
      /gems/davenport-1.0.2.pre/lib/davenport_ruby/
davenport_ruby.so
~/dvt$

Однако попытка запустить программу дает:

deploy@localhost:~/dvt$ ruby test.rb
Traceback (most recent call last):
    6: from test.rb:1:in `<main>'
    5: from /home/deploy/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems\
       /core_ext/kernel_require.rb:34:in `require'
...
    1: from /home/deploy/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems\
       /core_ext/kernel_require.rb:54:in `require':\
  libdavenport.so.0: cannot open shared object file: No such file or\
    directory - /home/deploy/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0\
      /gems/davenport-1.0.2.pre/lib/davenport_ruby/davenport_ruby.so\
(LoadError)

Файл библиотеки libdavenport.so.0 существует в /usr/local/lib. Создание этой части пути загрузки с помощью ruby -I /usr/local/lib test.rb дает тот же результат.

Файл библиотеки davenport_ruby.so существует в /home/deploy/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/davenport-1.0.2.pre/lib/davenport_ruby, как показано здесь:

/home/deploy/.rbenv/versions/2.6.3/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require': libdavenport.so.0: cannot open shared\
object file: No such file or directory - /home/deploy/.rbenv/versions/2.6.3\
/lib/ruby/gems/2.6.0/gems/davenport-1.0.2.pre/lib/davenport_ruby\
/davenport_ruby.so (LoadError)
~/dvt$ ls -l ~/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/\
davenport-1.0.2.pre/lib/davenport_ruby/
total 72
-rwxr-xr-x 1 deploy deploy 69640 Jul 25 17:02 davenport_ruby.so
~/dvt$

Опубликованная 1.0.2.pre версия расширения Ruby davenport взята из этого PR, который ссылка показывает все биты кода (и довольно много экспериментов). Это какая-то деталь или часть жемчужины расширения не совсем правильная?

davenport_ruby.so

Файл .so — это файл общей библиотеки, скомпилированный при установке драгоценного камня. Его нет в содержимом драгоценного камня:

~/davenport-ruby/dltem[i1]$ gem unpack davenport-1.0.2.pre.gem
Unpacked gem: '/Users/dcl/davenport-ruby/dltem/davenport-1.0.2.pre'
~/davenport-ruby/dltem[i1]$ ls -R
davenport-1.0.2.pre davenport-1.0.2.pre.gem

./davenport-1.0.2.pre:
History.txt README.rdoc Rakefile    ext     lib

./davenport-1.0.2.pre/ext:
davenport_ruby

./davenport-1.0.2.pre/ext/davenport_ruby:
davenport_ruby.c    extconf.rb

./davenport-1.0.2.pre/lib:
davenport.rb
~/davenport-ruby/dltem[i1]$

Когда RubyGems устанавливает гем, обнаруживает ли он s.extensions << 'ext/davenport_ruby/extconf.rb' в спецификации гем и выполняет ли этот файл с ruby? Запускается ли он make для результирующего make-файла?

Что позволит ruby test.rb работать без ошибки?

Этот список содержит сравнение уникальных глобальных символов в libdavenport.so.0 и davenport_ruby.so и вывод команд gem environment и ruby -e 'puts $:.join("\n")'.

лдд

В этом вопросе о проблеме not found (LoadError) предлагается использовать ldd для проверки связи. (Есть связанный вопрос, на который нет ответа, но с аналогичным предложением в комментарии.)

Действительно, библиотека расширений хоть и построена, но не ссылается на установленный libdavenport.so.0:

~/dvt$ bundle install
Fetching gem metadata from https://rubygems.org/.
Using bundler 2.0.2
Fetching davenport 1.0.2.pre
Installing davenport 1.0.2.pre with native extensions
Bundle complete! 1 Gemfile dependency, 2 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
~/dvt$ ldd ~/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/davenport-1.0.2.pre/lib/davenport_ruby/davenport_ruby.so
    linux-vdso.so.1 (0x00007fff2d3e3000)
    libdavenport.so.0 => not found
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9a5be42000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9a5baa3000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f9a5c349000)
~/dvt$

ldconfig

Этот очень старый вопрос на AskUbuntu предлагает использовать команду ldconfig. Так как libdavenport.so.0 установлен в /usr/local/lib, команда была ldconfig /usr/local/lib. Однако для его запуска требуется другой логин, который может получить привилегии root. Вернемся к учетной записи deploy, которая выполняет установку программы Ruby:

~/dvt$ ruby test.rb
Hola
[1, 3, 2, 4]
~/dvt$

Вуаля. Сейчас это работает. Возникает вопрос, как заставить это работать с развертыванием/установкой и с пользователем, у которого нет привилегий root. (Для установки библиотеки libdavenport также требовались привилегии суперпользователя, хотя она была скомпилирована и установлена ​​из исходного кода.)


person Douglas Lovell    schedule 25.07.2019    source источник
comment
Почему библиотека C libdavenport.so.0 вызывает библиотеку расширений davenport_ruby.so (которая присутствует), а не наоборот? Библиотека C ничего не ссылается в расширении, скорее расширение ссылается на библиотеку C. Может ли это быть проблемой конфликта имен со ссылкой?   -  person Douglas Lovell    schedule 26.07.2019


Ответы (2)


Запуск ldconfig /usr/local/lib от имени root оказывается ответом. Это позволяет загрузчику найти установленную libdavenport.so.0 библиотеку во время выполнения.

На последующие вопросы ответ заключается в том, что make install, вероятно, не сможет устранить необходимость запуска ldconfig на некоторых системах. Существует старая проблема с другой библиотекой, которая предприняла попытку, все еще открытую, в esnet/iperf. Они не разобрались. Если и сделали, то не обновили выпуск. Более новая проблема с libcheck/check имеет аналогичные проблемы.

Текущая документация Gnu для libtool открыта в ее ошибках реализации. ,

Цель установки Makefile должна предупредить установщика пакета, чтобы он установил правильные переменные среды (LD_LIBRARY_PATH или эквивалент) или запустить ldconfig.

«Решение» в данном случае — это документация для библиотеки Davenport, обновление README, сделанное с помощью этот PR.

На Cprogramming.com есть хорошее краткое объяснение связывания и загрузки библиотек. a> (который поддерживается рекламой, но не так болезненно; статья заслуживает ссылки).

person Douglas Lovell    schedule 30.07.2019

libdavenport.so.0: cannot open shared object file: No such file or\
directory - /home/deploy/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0\
  /gems/davenport-1.0.2.pre/lib/davenport_ruby/davenport_ruby.so

Эта ошибка означает, что собственное расширение davenport_ruby.so не может загрузить общую библиотеку Давенпорта libdavenport.so.0. Вам необходимо убедиться, что общая библиотека находится в вашем пути загрузки, используя ldconfig или добавив ее путь в LD_LIBRARY_PATH.


Старый неверный ответ:

Расширения Ruby C должны быть скомпилированы для платформы, на которой вы их запускаете. Если вы просто загрузите код Ruby, который использует расширение, он не будет работать из коробки.

Вы построили расширение, используя gem или bundle? Вы запускали это на машине с Ubuntu, на которой пытаетесь запустить скрипт?

Камень не должен содержать .so. Когда вы устанавливаете гем, он должен собрать .so и поместить его куда-нибудь в путь загрузки Ruby, чтобы скрипты могли его потребовать.

Предполагая, что вы действительно установили гем и он успешно построил .so, я могу предположить две возможные проблемы.

  1. Вы установили драгоценный камень с неправильным инструментом или параметрами, поэтому он поместил .so в неправильное место.
  2. Вы используете Ruby неправильно (используете ли вы rbenv или rvm?), поэтому он ищет .so не в том месте.

Запустите gem environment и ruby -e 'p $:', чтобы сравнить каталог установки gem с путем загрузки Ruby.

person Max    schedule 25.07.2019
comment
Спасибо, что посмотрели. В вопросе теперь есть список содержимого драгоценных камней и вопросы о davenport_ruby.so и сборке расширения. Файл gem был создан с использованием gem build davenport.gemspec. Он был установлен с помощью сборщика и Gemfile. - person Douglas Lovell; 26.07.2019
comment
Большое спасибо за эти советы по проверке установки драгоценного камня и собственной компиляции. Команда ruby -e 'p $:' была TIL. Суть, связанная внизу вопроса, теперь содержит вывод обоих. Похоже, что гем расширения устанавливается, компилируется и загружается программой дымового теста. Если установка и загрузка гема выглядят нормально, возможно, проблема в самом коде C или в линковке. - person Douglas Lovell; 26.07.2019
comment
Проголосовал с благодарностью, а также потому, что ответ содержит действительно отличные стратегии для проверки сборки и настройки. Это исключало многое. - person Douglas Lovell; 30.07.2019