链接一个可执行文件到一个共享库

3

我有一个基本问题,但找不到解决方案。假设我构建了一个由以下头文件和源文件组成的共享库toto:

toto.h

void print();

toto.cpp

#include <iostream>
#include "toto.h"
void print()
{
std::cout<<"WHAT A GREAT LIBRARY"<<std::endl;
}

基于这些文件,我使用以下命令创建了我的共享库:

g++ -fPIC -shared -I. -c toto.cpp -o libtoto.so

接下来,我想在测试文件中使用我刚刚创建的共享库:

test.cpp

#include "toto.h"

int main()
{
    print();
}

为了做到这一点,我使用以下命令将我的可执行文件与共享库链接起来:g++ -I. -L. -ltoto test.cpp。然后我得到了a.out可执行文件。当我运行ldd a.out时,我会得到以下输出:
linux-vdso.so.1 =>  (0x00007fff322f5000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fb7e7c24000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb7e7865000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fb7e7568000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fb7e7f4d000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fb7e7352000)

我没有看到我的可执行文件对 toto 共享库有任何依赖。在链接过程中,我的库似乎已经被静态绑定到了我的可执行文件上。这一点得到了证实,因为我可以从任何地方调用 a.out 而无需将 LD_LIBRARY_PATH 设置为存储 libtoto.so 的路径。如果是这样的话,那么我在前面的命令中错过或误解了什么?

顺便说一下,这个例子是在 ubuntu 12.04、g++ 4.6.3 上运行的。

编辑:

与此同时,我发现这段代码生成了一个“正确”的可执行文件,即 ldd 显示它依赖于 libtoto 共享库,并且未设置 LD_LIBRARY_PATH 变量时无法启动可执行文件:

g++ -I ./mydylib/ -fpic -c mydylib/toto.cpp -o mydylib/toto.o
g++ -shared -o mydylib/libtoto.so mydylib/toto.o
g++ -I ./mydylib/ -L ./mydylib/ test.cpp -ltoto -o ldd_correct

虽然这个版本仍然可以生成具有上述功能的“错误”可执行文件:
g++ -I ./mydylib -fpic -shared -c mydylib/toto.cpp -o mydylib/libtoto.so
g++ -I ./mydylib -L ./mydylib test.cpp -ltoto -o ldd_wrong

请看一下这个问题。提供的答案是否解决了您的问题? - Michael Foukarakis
1
g++ 的参数顺序非常重要。-ltoto 应该放在源文件(例如 toto.cpp)和目标文件之后。 - Basile Starynkevitch
1
可能是重复的问题:共享库未知地未被链接到应用程序 - Michael Foukarakis
谢谢大家的帮助,但不幸的是,你们的建议都没有解决问题。这个问题非常奇怪。 - Eurydice
1
那么,执行./a.out会打印消息吗?如果删除.so并重新运行,会发生什么?(对于 Redhat Enterprise Linux Server 5.8,ldd显示了预期的依赖项,这表明可能是对ld行为的其他更改,可能是在 Michael 提供的链接所做的更改之一)。 - Tony Delroy
显示剩余3条评论
2个回答

1
我的猜测是因为这个库非常简单,所以被优化掉了。对于更复杂的库,是否仍然会发生这种情况?您尝试过传递-O0标志吗?
我已经尝试过了,但我无法复现结果 - 我的ldd显示:
linux-vdso.so.1 =>  (0x00007fffb79fe000)
libtoto.so (0x00007f8a78316000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8a77f2e000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f8a77c2a000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8a7851a000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8a77924000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f8a7770d000)

在Ubuntu 14.04上,gcc版本为4.8.2。
然而,正如我所预料的那样,g++ -I. -L. -ltoto test.cpp没有起作用 - 我必须运行g++ -I. -L. test.cpp -ltoto

我尝试链接 boost-date-time 库,确实得到了 libboost_date_time.so.1.55.0,它出现在我的 ldd 调用中。然而,使用 -O0 标志编译对于我的个人库没有帮助。 - Eurydice
有趣。尝试让你自己的库更加复杂(将一些复杂的代码从其他地方复制粘贴到其中),看看它是否仍然不在“ldd”输出中。如果它不在那里,那么可能是由于你链接库的方式。如果它在那里,那么你当前的简单库只是太简单了,真的被优化掉了。 - texasflood

0

我终于找到了我的问题原因:

当我在单个GCC步骤中编译和链接我的共享库时,我为我的toto.cpp源文件添加了错误的-c标志。这似乎会破坏可执行文件的构建,您可以通过比较以下三种构建模式来看到:

两个GCC步骤用于构建共享库

g++ -I. -fpic -c toto.cpp -o toto.o
g++ -shared -o libtoto.so toto.o
g++ -I. -L. test.cpp -ltoto -o ldd_correct

构建共享库的单个GCC步骤

g++ -I. -fpic -shared -o libtoto.so toto.cpp
g++ -I. -L. test.cpp -ltoto -o ldd_also_correct

构建共享库的单个GCC步骤 -> -c会导致某些错误。
g++ -I. -fpic -shared -o libtoto.so -c toto.cpp
g++ -I. -L. test.cpp -ltoto -o ldd_wrong

1
我的看法是:-c 告诉编译器生成一个 .o 目标文件,而 -o libtoto.so 给它一个文件名,这个文件名有些误导,因为它在内部并不是 ".so" 共享库格式。当同时指定 -c 时,似乎会忽略 -shared 标志。所以,你的 -ltoto 最终找到了 libtoto.so 文件,但由于文件内容支持静态链接,因此将其静态链接。遗憾的是它没有打印错误消息并退出... - Tony Delroy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接