У меня есть следующая проблема:
Я пытаюсь создать программное обеспечение на Ubuntu18, используя компилятор gnu по умолчанию (gcc-7) и доступные версии компоновщика.
Теперь у нас есть случай, когда исполняемый файл может связать общую библиотеку, которая снова связывает другие общие библиотеки. Таким образом, исполняемый файл имеет вторичную зависимость. Но вторичные зависимости берутся только из rpath, а не из runpath. Таким образом, даже если вторичная зависимость будет помещена в папку пути выполнения исполняемого файла, она не будет найдена.
Проблема была выявлена при использовании googletest в сочетании с cmake. Там тесты не могут быть скомпилированы из-за ненайденных вторичных зависимостей. Так что установка LD_LIBRARY_PATH
может быть не вариантом или, по крайней мере, сильно все усложняет.
Вот пример того, что не так с gcc-7 в системе Ubuntu18 (но отлично работает с gcc-5 в Ubuntu16):
вторичный.hpp
#include <string>
class World{
public:
std::string world();
};
вторичный.cpp
#include "secondary.hpp"
std::string World::world(){
return "world";
}
primary.hpp
#include <string>
class Hello{
public:
std::string helloWorld();
};
primary.cpp
#include "primary.hpp"
#include "secondary.hpp"
#include <sstream>
std::string Hello::helloWorld(){
std::stringstream strm;
World world;
strm << "hello ";
strm << world.world();
strm << "!";
return strm.str();
}
и скомпилируйте обе библиотеки, как это
mkdir build
cd build
gcc -shared -o libsecondary.so -fPIC ../secondary.cpp
gcc -shared -o libprimary.so -fPIC ../primary.cpp -lsecondary -L$(pwd)
Если я сейчас соберу исполняемый файл из следующих файлов
main.cpp
#include "primary.hpp"
#include <iostream>
int main(int argc, char** argv){
Hello hello;
std::cout << hello.helloWorld() << std::endl;
}
выполнив следующие действия в новом каталоге сборки (параллельно предыдущему)
cd ..
mkdir build_app
cd build_app
gcc -o app ../main.cpp -L../build -lstdc++ -lsecondary -lprimary -Wl,-rpath=$(pwd)/../build
Если я сейчас сделаю ldd app
, то увижу, что libsecondary.so
не найдено
linux-vdso.so.1 (0x00007fff0cf8c000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f9e86fe7000)
libprimary.so => <some_path>/libprimary.so (0x00007f9e86de5000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f9e86bcd000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9e867dc000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f9e8643e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9e87573000)
libsecondary.so => not found
и путь выполнения приложения (запустив readelf -d app
)
Dynamic section at offset 0x1d40 contains 31 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libprimary.so]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath:[<some_path>/build]
Вопрос в том, почему ld
не берет libsecondary.so
с пути следования app
? И libprimary.so
, и libsecondary.so
находятся в одной папке, но найден только libprimary.so
.
Одним из решений проблемы является компиляция с --disable-new-dtags
, которая будет использовать rpath вместо runpath и является значением по умолчанию для gcc-5 в Ubuntu16. Однако использование rpath кажется устаревшим из ld.
Так как же правильно использовать runpath вместе со вторичными зависимостями?