代码::块和boost 1.55:当存在动态库时,不使用静态库

Code::Blocks and boost 1.55: static library is not used when dynamic library is present

本文关键字:静态 存在 boost 块和 代码 动态      更新时间:2023-10-16

我用以下命令构建了boost 1.55串行化库:

b2 --build-dir=build toolset=gcc --with-serialization --layout=tagged link=static threading=multi stage

并且在我的CCD_ 3目录中得到了CCD_ 1和CCD_。然后,我将boost_serialization添加到我的C::B项目的链接库列表中,并编译了boost序列化示例,它从命令行运行良好。然后,我使用额外构建了动态和单线程变体

b2 --build-dir=build toolset=gcc --with-serialization --layout=tagged link=static,shared threading=multi,single stage

并在我的stage/lib目录中获得了更多的库。令我困惑的是,每个库都有一个.so文件,即使是那些应该是静态的库。为什么它在那里?它需要什么?

当我现在编译项目时,可执行文件抱怨道:

error while loading shared libraries: libboost_serialization.so.1.55.0: cannot open shared object file: No such file or directory

这个库肯定在那里,我可能只需要将它的路径添加到LD_LIBRARY_PATH,但我现在想静态链接。我该怎么做?

我也不太理解库的命名:我的lib文件夹中有一些libboost_wserialization...库,而当前boost入门页面的库命名部分中没有描述libboost_serialization-mt.a1的w前缀。


你的回答让我更好地了解了发生了什么——现在我知道了boost_wserialization库的来源。我发现,在进行第二次构建后,所有当前库都被共享,并且静态库被覆盖。这就是为什么我对那些以前确实是静态的库的"额外".so文件感到困惑。

好的,第一个问题:

为什么有boost_serializationboost_wserialization库?

wserialization库是面向wchar_t的。放入一个单独的库中,因为实际上可能并不需要它。

为什么有多个共享/静态库?

您之所以看到所有这些额外的共享库,是因为您使用link=static,shared调用b2,它指示boost构建共享库和静态库。此外,在thread=multi中添加会导致mt库的构建,这些库在链接到多线程应用程序时应该使用。

为什么我收到关于libboost_wserialization-mt.a0的运行时链接错误?

默认情况下,大多数unix/linux系统在链接时更喜欢使用共享库而不是静态库,因此当您尝试链接时,它更喜欢使用分享库而不是静止库。如果你想强制静态库的链接而不是共享库的链接,你可以告诉编译时链接器这样做,使用:

-Wl,-Bstatic -lboost_serialization -Wl,-Bdynamic

这将导致链接器查找boost_serialization库的静态变体,而不是动态变体。

现在,因为您使用的是code::blocks,所以我必须逐个查找如何指定这些标志,但最明智的做法是使用./b2 clean清理提升构建,然后重新生成,只指定link=static,然后您应该只得到.a文件,这将再次生成独立的可执行文件。

如果要为code::blocks指定此选项,则需要将它们放入项目的Build Settings->Linker settings->Other Linker Options字段中。在这种情况下,简单地在库字段中指定库是不起作用的。此外,忘记传入stage/lib0选项会导致它尝试链接某些平台库的静态版本,如果有问题的库不存在,则可能导致构建失败。

如果您想避免必须将LD_LIBRARY_PATH设置为运行二进制文件,可以将选项-Wl,-rpath,/path/to/boost/libraries添加到链接器标志中,这将导致编译后的程序在尝试解析库的位置时搜索该目录。

让我困惑的是,每个库都有一个.so文件,甚至那些应该是静态的。为什么它在那里?它需要什么?

您显然在使用他人的make文件。我自己写的。我的构建命令不会创建".so"(共享对象库)。它只创建".a"(归档库)。链接器知道如何使用这两种方法。

参见手册ar。实用程序ar建立档案
见手册ld。实用程序ld可以构建共享对象。

您可以在构建序列中查找这些实用程序调用,或者询问某人它们在哪里,并注释ld的使用,因为您很可能不需要两者(同时构建两者会不必要地延长构建时间)。或者,您可以暂时重命名ld命令,然后尝试构建。当它找不到ld命令时,您可能会得到一个有用的提示,提示在哪里调用ld。

在我的make文件中,命令如下所示。注释字符是行开头的#。(字符串扩展$(AR)和$(LD)允许使用非标准实用程序。)

$(TARGET_ARCHIVE): $(OBJ)
@echo R10: $(TARGET_ARCHIVE) :from: $(OBJ)
$(AR) crs  $(TARGET_ARCHIVE)  $(OBJ)
#    $(TARGET_OLB) : $(OBJ)
#        @echo R00: $(TARGET_OLB) :from: $(OBJ)  
#        $(LD)   -o $(TARGET_OLB) -r  $(OBJ)

存档文件(.a)在使用时会直接链接到可执行文件并包含在可执行文件中。加载可执行文件时,.a的所有引用符号都已在其中。(未引用的符号和代码未链接在中)

共享对象(.so)不是直接链接的,而是您的可执行文件获得.so的句柄(或者可能是文件名)。我相信,当加载可执行文件时,.so不会立即加载。.so直到您的可执行文件第一次引用.so中的符号时才会加载。在加载时,您的应用程序会出现延迟,但对于大多数应用程序来说,这种延迟加载可能是合理的。

也有可能.so在您激活进程之前已经加载到系统内存中。在这种情况下,当你的可执行文件第一次引用.so中的一个符号时,一些系统代码会将内存中现有的.so"映射"到你的应用程序——可能比加载它更快,但我认为最大的好处是被许多进程使用/引用的.so只需要加载一次,节省了内存空间。加载的.so有它的所有符号,即使你的应用程序不需要所有符号。

在任何一种情况下,你的可执行文件都会随着.so而变小,随着.a而变大,但.so的每个文件都有一些小的性能打击。因此需要加载或映射。我的桌面上有4 GB,桌面从未感到"拥挤"。它的交换从未被使用过(afaik)。所以我通常使用.a的

注意:当链接器同时访问存档(.a)和共享对象(.so)文件时,链接器将使用.so(并忽略.a)。您可能可以覆盖该首选项,但我尚未尝试。我发现简单地将归档文件(.a)移到一个单独的(与.so的)目录中,并通过-L构建选项通知链接器更容易。