При запуске компоновщика C++ отсутствует библиотека (поведение SONAME)
Я сделал программу, которая использует две общие библиотеки (которые я скомпилировал) и размещены так:
/home_directory_where_I_compile_and_run_everything
-->/lib/libjson_linux-gcc-4.4.6_libmt.so
-->/lib/libre2.so.0
Когда я компилирую свою программу, я передаю относительное расположение этих библиотек компоновщику, например так:
g++ ...... stuff ........ my_program.cc lib/libjson_linux-gcc-4.4.6_libmt.so lib/libre2.so.0
И он прекрасно компилируется, однако при запуске программы не удается найти libre2.so, и если я проверяю его с помощью ldd, вот что происходит:
....
lib/libjson_linux-gcc-4.4.6_libmt.so (0x00007f62906bc000)
libre2.so.0 => not found
....
Очевидно, он признает, что путь на libjson относителен, но на libre2.so.0 этого не происходит (он обрезает весь путь и просто оставляет libre2.so.0).
Может кто-нибудь сказать мне, почему это происходит?
Кроме того, есть ли способ изменить это с помощью аргумента g++?
Лучший.
* ОБНОВЛЕНИЕ * Вау проверить это! Я изменил имя libre2.so.0 на stuff.so, а затем попытался скомпилировать по существу так же, как это:
g++ ...... stuff ........ my_program.cc lib/libjson_linux-gcc-4.4.6_libmt.so lib/stuff.so
И это в любом случае терпит неудачу; не только он терпит неудачу, он терпит неудачу, потому что он не может найти "libre2.so.0".
Whyyy?
* ОБНОВЛЕНИЕ № 2 *
Выход для readelf -d the_program.o
0x0000000000000001 (NEEDED) Shared library: [lib/libjson_linux-gcc-4.4.6_libmt.so]
0x0000000000000001 (NEEDED) Shared library: [libre2.so.0]
Теперь, если бы я мог просто сделать это [libre2.so.0] как [lib/libre2.so.0], это было бы хорошо.
* ОБНОВЛЕНИЕ № 3 *
Как узнал @troubadour:
Когда исполняемый файл связан с общим объектом, имеющим поле DT_SONAME, то при запуске исполняемого файла динамический компоновщик будет пытаться загрузить общий объект, указанный в поле DT_SONAME, а не с использованием имени файла, данного компоновщику.
Вот почему он работает с libjson..... так, а не с libre2.so.0. (libjson..... так что нет записи для SONAME).
И я наконец нашел точный вопрос для того, что я ищу:
Есть ли способ сказать компоновщику gcc игнорировать записи SONAME в файле общей библиотеки и вместо этого ссылаться на определенный путь к файлу?
4 ответа
Сначала я отвечу на вторую часть вашего вопроса, а именно: почему переименование libre2.so.0 не соответствует вашим ожиданиям.
Имя файла, которое вы передаете компоновщику, не имеет значения, когда вы запускаете исполняемый файл (если вы не указали -soname
при сборке библиотеки - см. второе редактирование ниже). Зависимость взята от того, что называют "soname". Если вы запустите readelf
По команде в вашей библиотеке вы можете определить его имя, например.
readelf -d libre2.so.0 | grep SONAME
Неважно, если вы переименуете файл. Результат вышеупомянутой команды все равно даст вам то же имя, поэтому программа не смогла найти "libre2.so.0".
Что касается оригинальной части вашего вопроса, то все зависит от того, есть ли в библиотеках RPATH
или же RUNPATH
встроенный в них и / или каково содержание вашего LD_LIBRARY_PATH
переменная окружения есть. Это то, что будет использовать компоновщик времени выполнения (ld.so) для поиска разделяемых библиотек. Пытаться
man ld.so
для дополнительной информации.
Поскольку вы сами создали библиотеки, вы будете знать, использовали ли они -rpath
или же -runpath
варианты на заключительном этапе связывания. В качестве альтернативы используйте readelf
опять например
readelf -d libre2.so.0 | grep RPATH
readelf -d libre2.so.0 | grep RUNPATH
Я подозреваю, что две вышеупомянутые команды ничего не вернут.
Я предполагаю, что у вас есть текущий каталог в вашем LD_LIBRARY_PATH
что позволило бы компоновщику во время выполнения найти lib/libjson_linux-gcc-4.4.6_libmt.so, но не libre2.so.0. Я заметил, что вы ответили на мой комментарий на ваш вопрос, чтобы сказать, что ваш LD_LIBRARY_PATH
пустой. Это странно.
Возможно, у вас как-то есть префикс "lib/" в сонаме для libjson? то есть сделал readelf
команда для возврата SONAME
lib/libjson_linux-gcc-4.4.6_libmt.so
а не просто
libjson_linux-gcc-4.4.6_libmt.so
Кроме того, проверьте, что нужно программе с точки зрения soname, запустив
readelf -d my_progam | grep NEEDED
Возможно, префикс "lib/" находится там из-за того, как вы передали его в gcc. Если это так, то если вы используете команду gcc, заданную @enobayram, то она выровняет игровое поле, то есть не сможет найти libjson.
Первое, что нужно установить, это не то, почему он не находит libre2.so.0, а то, как ему удается найти libjson. Если вы попытаетесь запустить свой исполняемый файл из другого каталога, он все еще будет работать или теперь он не будет работать и для libjson? Или, если вы скопируете libre2.so.0 рядом с исполняемым файлом, это что-нибудь изменит?
редактировать
Публикация на форуме Fedora предполагает, что версия ld.so для Fedora содержит текущий каталог в качестве встроенного пути поиска. Хотя я не смог проверить это, но это объяснило бы, почему вы вообще берете какие-либо библиотеки, учитывая, что все остальные вещи, используемые ld.so, отсутствуют в вашем случае.
Редактировать 2
Со страницы man ld в моей системе
-soname= имя
При создании общего объекта ELF установите для внутреннего поля DT_SONAME указанное имя. Когда исполняемый файл связан с общим объектом, имеющим поле DT_SONAME, то при запуске исполняемого файла динамический компоновщик будет пытаться загрузить общий объект, указанный в поле DT_SONAME, а не с использованием имени файла, данного компоновщику.
Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
Разрешается копировать, распространять и / или изменять этот документ в соответствии с условиями лицензии GNU Free Documentation License, версии 1.3 или любой более поздней версии, опубликованной Free Software Foundation; без инвариантных секций, без текстов на передней обложке и без текстов на задней обложке. Копия лицензии включена в раздел "Лицензия на бесплатную документацию GNU".
Итак, теория в вашем комментарии верна. Если нет -soname
явно указывается при сборке библиотеки, тогда в общем объекте SONAME не существует, а поле NEEDED в исполняемом файле просто имеет имя файла, присвоенное компоновщику, который, в вашем случае, содержал ведущую "lib/".
Когда вы ссылаетесь на разделяемые библиотеки (.so), они должны существовать в пути загрузки библиотеки при запуске программы. Местоположение, которое вы указали во время ссылки, не запоминается в исполняемом файле.
Альтернативные решения:
- поместите файлы.so в тот же каталог, что и исполняемый файл
- бежать с
LD_LIBRARY_PATH=lib ./program
- установить.so файлы в папку в пути к библиотеке, например
/usr/local/lib
перед запуском программы. - ссылка статически
Вы должны сказать, где находится общая библиотека в двух ситуациях.
Один на связывание времени.
Есть несколько способов:
- скопировать библиотеку в какой-нибудь стандартный каталог, например
/usr/local/lib
и установите его черезldconfig
, Таким образом, никакая другая опция / операция не требуется. - использование
-L
сказать gcc, где найти библиотеку
Один во время выполнения.
Также несколько способов:
- как время соединения, установите его через
ldconfig
тогда никаких других действий не требуется. с помощью
LD_LIBRARY_PATH
,LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./a.out
с помощью
rpath
записать путь поиска в исполняемый файлg++ main.cpp -lxxx -L/a/b/c -Wl,-rpath,/a/b/c
Указание местоположения общей библиотеки только на этапе компоновки, например, через -L
, не означает, что при запуске программы загрузчик системы найдет нужную библиотеку.
Правильная командная строка компилятора должна быть:
g++ ...... stuff ........ my_program.cc -Llib -ljson_linux-gcc-4.4.6_libmt -lre2
И вы должны либо поместить общие объекты в стандартный каталог (например, /usr/lib), либо добавить их путь к LD_LIBRARY_PATH
чтобы иметь возможность запустить свой исполняемый файл.