我正在与openCV库合作进行计算机视觉研究,在编译过程中,我遇到了一些问题,这些问题使我试图了解操作系统如何将库与源代码联系起来。在网上寻找了一段时间,以获得一个良好的概览和阅读手册g++/gcc,ld.我有一些结论,我希望有人有更多的经验来解释我。
首先是我使用的编译行。这是:
-Input:g++pkg-config --c威-libs opencvimage-conversion.cpp -o image-conversion
pkg-config --cflags --libs opencv-I/opt/local/include/opencv -I/opt/local/include -L/opt/local/lib -lopencv_shape -lopencv_stitching -lopencv_objdetect -lopencv_superres -lopencv_videostab -lopencv_calib3d -lopencv_features2d -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_video -lopencv_photo -lopencv_ml -lopencv_imgproc -lopencv_flann -lopencv_core我需要的代码库是-lopenhighgui,但是我更喜欢用这种方式编译,因为这个库依赖于其他库。问题是,当我去/opt/local/lib查看库时,我有三个文件:
-libopencv_highgui.3.1.0.dylib
-libopencv_highgui.3.1.dylib
-libopencv_highgui.dylib我不知道-lopenhighgui引用哪个库。我在g++手册中发现,-l标志指定了库名,避免了lib前缀和*.a *.so (linux)/*.dylib (mac)后缀。在执行otool -L可执行文件之后,我得到了输出:
/opt/local/lib/libopencv_highgui.3.1.dylib (compatibility version 3.1.0, current version 3.1.0)那么,为什么它要使用这个而不是其他一个,它是如何使用的呢?这三个图书馆有什么区别?
另一个问题是关于链接和执行过程。我理解了使用静态库时的链接过程。我的问题是在编译动态库时。在下一个例子中:
-Input:g++ -I/opt/local/include/opencv -I/opt/local/include -L/opt/local/lib -lopencv_highgui image-conversion.cpp -o image-conversion
我发现从编译到执行程序的过程可以分为三个部分。
-I标志进行代码解析。-lflag指定的库链接到路径-L或标准路径下。这是通过链接器(ld)完成的。问题是下一个问题:我在互联网上发现一些人说,如果他们不将$LD_LIBRARY_PATH (在linux中)或$DYLD_LIBRARY_PATH (在mac OSx中)设置为库的非标准目录(在我的例子中是export LD_LIBRARY_PATH="/opt/local/lib"),动态链接器就找不到库,程序执行失败。我发现我的程序不会崩溃,如果我执行otool (用于查看链接的内容),.I就会得到这个结果(这是链接的所有库的摘要):
/opt/local/lib/libopencv_shape.3.1.dylib (compatibility version 3.1.0, current version 3.1.0) /opt/local/lib/libopencv_imgproc.3.1.dylib (compatibility version 3.1.0, current version 3.1.0) ... /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1226.10.1)
这是怎么回事?我发现LD_LIBRARY_PATH可以用来测试新的库,但是为什么这个人说他们只需要在执行程序时设置这个变量呢?最后这部分是如何工作的?我没有设定它,它工作得很完美。
感谢任何人。
发布于 2016-03-08 18:32:58
实际上有两个问题(这并不是一个很好的练习,顺便说一句),所以我会独立回答它们。
另外,我将重点关注Linux --这是我熟悉的领域。从问题的文本来看,我认为在Mac世界中的情况是非常相似的。
图书馆命名约定
在Linux环境中使用.so链接时,人们经常会看到动态库通常是以三胞胎表示的。例如,库foo可能存在于3个文件中: libfoo.so、libfoo.so.6、libfoo.so.6.5.4。如果你仔细观察,你会发现它们都是同一个文件--通常其中的两个只是指向第三个文件的符号链接。为了进一步讨论,libfoo.so将被称为不版本化库,ibfoo.so.6为主版本和libfoo.so.6.5.4 *全版本。你为什么需要这些?以获得更好的版本控制。
链接应用程序时,总是使用链接器规则进行不版本化的livbary --考虑到链接器将lib和.so添加到规则的事实,它看起来如下所示
g++ ... -lfoo ...当您的应用程序被链接时,链接器将打开libfoo.so并检查它的几个方面。它检查的内容中有一个所谓的SONAME头。这个标题是在链接.so库时创建的,它可以有一个与当前正在查看的链接器不同的文件名。例如,它可能有一个主要版本的文件,链接器会看到它: SONAME = libfoo.so.6。
当链接器看到该SONAME时,它会将结果应用程序文件标记为需要libfoo.so.6 --即使您实际请求libfoo.so。
通过这样做,链接器保留了库的某些版本。您的应用程序最初是用版本6编译和链接的,所以每当运行应用程序时,都需要版本6。
如果稍后系统升级(或应用程序运行在不同的系统上),其中foo的版本不同(例如,7),则.so文件将有所不同: libfoo.so、libfoo.so.7、libfoo.so.7.6.5。由于您的应用程序需要libfoo.so.6,它将无法启动-这是一件好事,因为谁知道版本7仍然兼容?如果没有这种保护,应用程序将启动和使用不同的库版本,其效果可能是毁灭性的。
LD_LIBRARY_PATH搜索
第二个问题是关于LD_LIBRARY_PATH的。这是真的,运行时链接器在寻找动态库时会参考这个变量.然而,这并不是它唯一咨询的事情。除此之外,还有默认的搜索路径,还有一个每个应用程序的动态库路径,通常由链接器的rpath参数控制,当它被链接时,会记录在应用程序中,例如:
g++ ... -Wl,rpath,/path/to/so/library当像这样记录路径时,运行时链接器将在加载应用程序时将这些路径添加到搜索路径列表中。
可以在不使用LD_LIBRARY_PATH的情况下为应用程序找到库这一事实意味着两件事之一: rpath是在应用程序链接时记录的,或者/opt/local/lib实际上包括在平台上的默认搜索路径中。
https://stackoverflow.com/questions/35874768
复制相似问题