链接时如何跟踪库的需求?

How to trace the need of a library when linking?

快速版本:为什么 gcc 要求特定的库?如何在执行 gcc 时跟踪其依赖关系?

长版:我正在尝试编译使用 opencv 的 c++ 代码,它使用 ffmpeg。编译时出现错误:

//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_alloc@LIBSWRESAMPLE_2' sin definir
//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_free@LIBSWRESAMPLE_2' sin definir
//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_is_initialized@LIBSWRESAMPLE_2' sin definir
//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_close@LIBSWRESAMPLE_2' sin definir
//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_init@LIBSWRESAMPLE_2' sin definir
//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_convert@LIBSWRESAMPLE_2' sin definir

我理解 libavcodec.so 需要 libswresample.so,但我希望 link 反对 libavcodec.so.58,而不是 57。我想追踪这种依赖关系:我想知道为什么 gcc 要求那个文件。

我知道系统中只有 libavcodec.so 会更好,但现在我无法摆脱 libavcodec.so.57。在我的系统中:

$ ldconfig -p | grep libavcodec
libavcodec.so.58 (libc6,x86-64) => /usr/local/lib/libavcodec.so.58
libavcodec.so.57 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libavcodec.so.57
libavcodec.so (libc6,x86-64) => /usr/local/lib/libavcodec.so  <== This is a link to 58)

如果你想知道,gcc命令行是这样的,注意那里没有-lavcodec:

g++ -Wl,--trace-symbol=libavcodec -L/usr/local/lib -L"/home/alejandro/Desarrollo eclipse/os1/Thirdparty" -o "os1"  ./src/Converter.o ./src/Frame.o ./src/FrameDrawer.o ./src/Initializer.o ./src/KeyFrame.o ./src/KeyFrameDatabase.o ./src/KeyFrameTriangulacion.o ./src/LocalMapping.o ./src/LoopClosing.o ./src/Map.o ./src/MapDrawer.o ./src/MapPoint.o ./src/ORBextractor.o ./src/ORBmatcher.o ./src/Optimizer.o ./src/PnPsolver.o ./src/PuntoLejano.o ./src/PuntosLejanosDB.o ./src/Sim3Solver.o ./src/System.o ./src/Tracking.o ./src/Video.o ./src/Viewer.o ./src/main.o ./src/osmap.o ./src/osmap.pb.o   -lopencv_core -lopencv_features2d -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_imgproc -lopencv_calib3d -lpthread -lpangolin -lGL -lGLU -lDBoW2 -lg2o -lprotobuf

此命令要求 libopencv_videoio.so,后者应查找 libavcodec.so.58。扫描所有 opencv 共享对象:

& ldd -r /usr/local/lib/libopencv_*.so | grep libavcodec
libavcodec.so.58 => /usr/local/lib/libavcodec.so.58 (0x00007f0f03c11000)
libavcodec.so.58 => /usr/local/lib/libavcodec.so.58 (0x00007fb00e3fa000)
libavcodec.so.58 => /usr/local/lib/libavcodec.so.58 (0x00007f916be48000)

奖金:为什么我不能删除 libavcodec.so.57

使用 Synaptic,此文件属于 libavcodec-extra57 包。当我删除它时,Synaptic 会自动安装 libavcodec57 包。如果我将其删除,则会重新安装第一个。因为很多依赖项需要这个库。如果有人想分享一个快速修复它的方法,我将不胜感激。

感谢您的宝贵时间。

如果您想知道,这里是 gcc --trace 的长输出。我有另一个更长的 -v -Wl,-v ,与此非常相似但没有任何新的 libavcodec 提及。

$ g++ -Wl,--trace -L/usr/local/lib -L"/home/alejandro/Desarrollo eclipse/os1/Thirdparty" -o "os1"  ./src/Converter.o ./src/Frame.o ./src/FrameDrawer.o ./src/Initializer.o ./src/KeyFrame.o ./src/KeyFrameDatabase.o ./src/KeyFrameTriangulacion.o ./src/LocalMapping.o ./src/LoopClosing.o ./src/Map.o ./src/MapDrawer.o ./src/MapPoint.o ./src/ORBextractor.o ./src/ORBmatcher.o ./src/Optimizer.o ./src/PnPsolver.o ./src/PuntoLejano.o ./src/PuntosLejanosDB.o ./src/Sim3Solver.o ./src/System.o ./src/Tracking.o ./src/Video.o ./src/Viewer.o ./src/main.o ./src/osmap.o ./src/osmap.pb.o   -lopencv_core -lopencv_features2d -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_imgproc -lopencv_calib3d -lpthread -lpangolin -lGL -lGLU -lDBoW2 -lg2o -lprotobuf
/usr/bin/ld: modo elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
./src/Converter.o
./src/Frame.o
./src/FrameDrawer.o
./src/Initializer.o
./src/KeyFrame.o
./src/KeyFrameDatabase.o
./src/KeyFrameTriangulacion.o
./src/LocalMapping.o
./src/LoopClosing.o
./src/Map.o
./src/MapDrawer.o
./src/MapPoint.o
./src/ORBextractor.o
./src/ORBmatcher.o
./src/Optimizer.o
./src/PnPsolver.o
./src/PuntoLejano.o
./src/PuntosLejanosDB.o
./src/Sim3Solver.o
./src/System.o
./src/Tracking.o
./src/Video.o
./src/Viewer.o
./src/main.o
./src/osmap.o
./src/osmap.pb.o
-lopencv_core (/usr/local/lib/libopencv_core.so)
-lopencv_features2d (/usr/local/lib/libopencv_features2d.so)
-lopencv_highgui (/usr/local/lib/libopencv_highgui.so)
-lopencv_videoio (/usr/local/lib/libopencv_videoio.so)
-lopencv_imgcodecs (/usr/local/lib/libopencv_imgcodecs.so)
-lopencv_imgproc (/usr/local/lib/libopencv_imgproc.so)
-lopencv_calib3d (/usr/local/lib/libopencv_calib3d.so)
/lib/x86_64-linux-gnu/libpthread.so.0
-lpangolin (/usr/local/lib/libpangolin.so)
-lGL (/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libGL.so)
-lGLU (/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libGLU.so)
-lDBoW2 (/home/alejandro/Desarrollo eclipse/os1/Thirdparty/libDBoW2.so)
-lg2o (/home/alejandro/Desarrollo eclipse/os1/Thirdparty/libg2o.so)
-lprotobuf (/usr/local/lib/libprotobuf.so)
-lstdc++ (/usr/lib/gcc/x86_64-linux-gnu/7/libstdc++.so)
/lib/x86_64-linux-gnu/libm.so.6
/lib/x86_64-linux-gnu/libmvec.so.1
/lib/x86_64-linux-gnu/libmvec.so.1
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_alloc@LIBSWRESAMPLE_2' sin definir
//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_free@LIBSWRESAMPLE_2' sin definir
//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_is_initialized@LIBSWRESAMPLE_2' sin definir
//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_close@LIBSWRESAMPLE_2' sin definir
//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_init@LIBSWRESAMPLE_2' sin definir
//usr/lib/x86_64-linux-gnu/libavcodec.so.57: referencia a `swr_convert@LIBSWRESAMPLE_2' sin definir
/usr/bin/ld: se encontraron errores de enlace, se borra el ejecutable `os1'
collect2: error: ld returned 1 exit status

有两种方法可以解决这个问题:

  • 阅读手册;永远是个好主意。
  • 安装依赖管理器。我使用 MacPorts,但有一些 不同的在那里。他们有一个图书馆数据库 他们管理,当您安装或构建他们的其中一个时 经理还将安装或构建所有库的库 您正在安装的那个取决于它们是否已经存在,以及它们安装的那些 依赖等

几乎可以肯定的是,您正在加载的共享库之一需要另一个共享库。这被编码到 ELF header 中的共享库本身。在共享库上使用 readelf -d 可以看到这一点。查找 NEEDED 行。 ld 将处理它以这种方式找到的库,但它们不会出现在 --trace.

的输出中

我不知道有什么方法可以自动找到源代码,但请尝试对确实出现在您的跟踪中的库进行 readelf,您应该会找到一个需要 libavcodec 的库,或者需要一个库,而这个库又需要 libavcodec,等等

当您找到 NEEDED 行时,请仔细注意其中的具体内容。是libavcodec.so,还是libavcodec.so.57?它只是一个文件名还是有一个绝对路径?这会影响链接器在搜索时会找到什么。

有关链接器搜索路径,请查看 ld 的 rpath-link 选项的文档。该路径中名为 libavcodec.so 的第一个文件(可能是符号链接)是什么?请记住链接器搜索出现在 NEEDED 行中的内容。它不会将 libavcodec.so.57 缩短为 libavcodec.so 并搜索后者。

@TrentP 回答后,很明显没有简单的方法可以做到这一点,所以我按照他的建议做了,这是一种困难的方法,我循环遍历所有共享对象以递归方式寻找依赖项。我使用 ldd -r 而不是 readelf。 ldd -r 递归地列出依赖关系(直接和间接)。

在一个文本文件中,我放入了出现在 gcc 输出中的共享对象的完整路径列表。然后 运行:

while read -r line; do echo "Library $line has these dependencies:\n $(ldd -r $line)"; done <textfile.txt

我在列出的输出中找到 libavcodec.so.57,并检测到哪个共享对象需要它。