c ++带有运行路径的二次依赖解析
c++ secondary dependency resolution with runpath
我有以下问题:
我正在尝试使用默认的 gnu 编译器 (gcc-7) 和可用的 linker 版本在 Ubuntu18 上构建软件。
现在我们有这样的情况,一个可执行文件可能 link 一个共享库,这又是 link 另一个共享库。因此,可执行文件具有次要依赖性。但是次要依赖项只能从 rpath 而不是从 runpath 中获取。所以即使二级依赖放在可执行文件的runpath文件夹中也不会被找到。
问题是在将 googletest 与 cmake 结合使用时发现的。由于未找到辅助依赖项,因此无法编译测试。所以设置 LD_LIBRARY_PATH
可能不是一个选项,或者至少会使一切复杂化。
这是一个例子,说明在 Ubuntu18 系统上 gcc-7 出了什么问题(但在 Ubuntu16 上用 gcc-5 工作正常):
secondary.hpp
#include <string>
class World{
public:
std::string world();
};
secondary.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
应用程序的运行路径是(by 运行 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
没有从 app
的运行路径中获取 libsecondary.so
? libprimary.so
和libsecondary.so
都放在同一个文件夹里,但只找到libprimary.so
。
该问题的一个解决方案是使用 --disable-new-dtags
进行编译,它将使用 rpath 而不是 runpath,并且是 Ubuntu16 上 gcc-5 的默认设置。然而,rpath 的使用似乎已从 ld.
中弃用
那么将运行路径与次级依赖项一起使用的正确方法是什么?
libprimary
负责加载libsecondary
,而不是app
。这意味着 app
的 DT_RUNPATH 条目不适用。
由于您可以控制所有库,因此您可以将 -Wl,-rpath=...
添加到 libprimary
和 libsecondary
的编译语句中。
我有以下问题:
我正在尝试使用默认的 gnu 编译器 (gcc-7) 和可用的 linker 版本在 Ubuntu18 上构建软件。
现在我们有这样的情况,一个可执行文件可能 link 一个共享库,这又是 link 另一个共享库。因此,可执行文件具有次要依赖性。但是次要依赖项只能从 rpath 而不是从 runpath 中获取。所以即使二级依赖放在可执行文件的runpath文件夹中也不会被找到。
问题是在将 googletest 与 cmake 结合使用时发现的。由于未找到辅助依赖项,因此无法编译测试。所以设置 LD_LIBRARY_PATH
可能不是一个选项,或者至少会使一切复杂化。
这是一个例子,说明在 Ubuntu18 系统上 gcc-7 出了什么问题(但在 Ubuntu16 上用 gcc-5 工作正常):
secondary.hpp
#include <string>
class World{
public:
std::string world();
};
secondary.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
应用程序的运行路径是(by 运行 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
没有从 app
的运行路径中获取 libsecondary.so
? libprimary.so
和libsecondary.so
都放在同一个文件夹里,但只找到libprimary.so
。
该问题的一个解决方案是使用 --disable-new-dtags
进行编译,它将使用 rpath 而不是 runpath,并且是 Ubuntu16 上 gcc-5 的默认设置。然而,rpath 的使用似乎已从 ld.
那么将运行路径与次级依赖项一起使用的正确方法是什么?
libprimary
负责加载libsecondary
,而不是app
。这意味着 app
的 DT_RUNPATH 条目不适用。
由于您可以控制所有库,因此您可以将 -Wl,-rpath=...
添加到 libprimary
和 libsecondary
的编译语句中。