摘要: 本文对比了两个 OpenCV 库(/home/quectel/day1/test/lib/ 与 /home/quectel/My_Snpe_Project/test/lib/)的差异。第一个库动态链接 libz.so.1 和 libtbb.so.2 且没有 RPATH,容易导致编译错误;第二个库静态链接了 zlib 和 TBB,依赖更少、部署更简单。替换后功能完全一致,性能可能略有提升,但内存占用可能略高。文章还给出了推荐做法和多种解决方案,建议在目标设备上实际验证。
库1:opencv
readelf -d libopencv_core.so
Dynamic section at offset 0x295250 contains 35 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libdl.so.2]
0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0]
0x0000000000000001 (NEEDED) Shared library: [libz.so.1]
0x0000000000000001 (NEEDED) Shared library: [libtbb.so.2]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x0000000000000001 (NEEDED) Shared library: [ld-linux-aarch64.so.1]
0x000000000000000e (SONAME) Library soname: [libopencv_core.so.4.2]
0x000000000000000c (INIT) 0x44560
0x000000000000000d (FINI) 0x1fae4c
0x0000000000000019 (INIT_ARRAY) 0x29bbe0
0x000000000000001b (INIT_ARRAYSZ) 664 (bytes)
0x000000000000001a (FINI_ARRAY) 0x29be78
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x1f0
0x0000000000000005 (STRTAB) 0x12330
0x0000000000000006 (SYMTAB) 0x4a40
0x000000000000000a (STRSZ) 93981 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0x2a54c0
0x0000000000000002 (PLTRELSZ) 5304 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x430a8
0x0000000000000007 (RELA) 0x2a628
0x0000000000000008 (RELASZ) 100992 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW
0x000000006ffffffe (VERNEED) 0x2a468
0x000000006fffffff (VERNEEDNUM) 7
0x000000006ffffff0 (VERSYM) 0x2924e
0x000000006ffffff9 (RELACOUNT) 3780
0x0000000000000000 (NULL) 0x0
库2:
readelf -d libopencv_core.so
Dynamic section at offset 0x3617a0 contains 32 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libdl.so.2]
0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0]
0x0000000000000001 (NEEDED) Shared library: [librt.so.1]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000e (SONAME) Library soname: [libopencv_core.so.4.2]
0x000000000000000f (RPATH) Library rpath: [/home/maxin/Code/OpenSource/OpenCV/opencv-4.2.0/build_aarch64_Linux/install/lib]
0x000000000000000c (INIT) 0x4b8b8
0x000000000000000d (FINI) 0x2b45dc
0x0000000000000019 (INIT_ARRAY) 0x367c48
0x000000000000001b (INIT_ARRAYSZ) 160 (bytes)
0x000000000000001a (FINI_ARRAY) 0x367ce8
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x0000000000000004 (HASH) 0x1f0
0x0000000000000005 (STRTAB) 0x11e48
0x0000000000000006 (SYMTAB) 0x4618
0x000000000000000a (STRSZ) 89541 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0x371fe8
0x0000000000000002 (PLTRELSZ) 22872 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x45f60
0x0000000000000007 (RELA) 0x28f98
0x0000000000000008 (RELASZ) 118728 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffe (VERNEED) 0x28e18
0x000000006fffffff (VERNEEDNUM) 6
0x000000006ffffff0 (VERSYM) 0x27c0e
0x000000006ffffff9 (RELACOUNT) 4091
0x0000000000000000 (NULL) 0x0
主要区别对比
| 项目 | /home/quectel/day1/test/lib/ |
/home/quectel/My_Snpe_Project/test/lib/ |
|---|---|---|
| NEEDED 依赖 | 需要 libz.so.1 和 libtbb.so.2 |
不需要 libz.so.1 和 libtbb.so.2 |
| NEEDED 依赖 | 需要 ld-linux-aarch64.so.1 |
不需要 ld-linux-aarch64.so.1 |
| NEEDED 依赖 | 没有 librt.so.1 |
需要 librt.so.1 |
| RPATH | 没有 RPATH | 有 RPATH: /home/maxin/Code/OpenSource/OpenCV/opencv-4.2.0/build_aarch64_Linux/install/lib |
| FLAGS | 有 BIND_NOW 和 NOW |
没有 FLAGS 字段 |
| PLTRELSZ | 5304 bytes | 22872 bytes |
| 依赖数量 | 9 个依赖库 | 7 个依赖库 |
关键发现
1. 第一个库(day1/test/lib)的问题
-
依赖
libz.so.1和libtbb.so.2:这就是导致编译错误的原因 -
没有 RPATH:链接器不知道去哪里找这些依赖库
-
依赖
ld-linux-aarch64.so.1:动态链接器依赖
2. 第二个库(My_Snpe_Project/test/lib)的优势
-
没有
libz和libtbb依赖:这些库可能被静态链接了 -
有 RPATH:指向了 OpenCV 编译时的库路径
-
不需要额外的动态链接器依赖
库差异的根本原因
第一个库(day1/test/lib)
-
动态链接 zlib 和 TBB
-
依赖系统提供的这些库
-
库文件更小(动态链接)
第二个库(My_Snpe_Project/test/lib)
-
静态链接 zlib 和 TBB(或内部实现)
-
不依赖外部库
-
库文件更大(包含了 zlib/TBB 代码)
┌─────────────────────────────────────────────────────────────┐ │ 第一个 libopencv_core.so │ ├─────────────────────────────────────────────────────────────┤ │ 核心 OpenCV 代码 │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 需要动态加载 zlib 和 tbb │ │ │ └──────────────────────────────────────────────────────┘ │ │ NEEDED: libz.so.1 ← 运行时必须找到 │ │ NEEDED: libtbb.so.2 ← 运行时必须找到 │ │ NEEDED: ld-linux-aarch64.so.1 ← 需要动态链接器 │ └─────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────┐ │ 第二个 libopencv_core.so │ ├─────────────────────────────────────────────────────────────┤ │ 核心 OpenCV 代码 │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ zlib 代码 (已编译进库) │ │ │ │ tbb 代码 (已编译进库) │ │ │ └──────────────────────────────────────────────────────┘ │ │ 无 NEEDED: libz.so.1 ← 不需要 │ │ 无 NEEDED: libtbb.so.2 ← 不需要 │ │ 无 NEEDED: ld-linux-aarch64.so.1 ← 使用系统默认 │ └─────────────────────────────────────────────────────────────┘
替换库后的影响分析
1. 功能影响:无影响
# 两个库版本相同(都是 4.2.0)
Library soname: [libopencv_core.so.4.2] # 两者都是 4.2
OpenCV 核心功能完全一致,图像处理、矩阵运算等 API 相同。
2. 性能影响:可能有轻微差异
| 方面 | 第一个库(动态) | 第二个库(静态) | 影响 |
|---|---|---|---|
| 内存占用 | 共享库可复用 | 代码重复 | 第二个略高 |
| 启动速度 | 需加载外部库 | 直接加载 | 第二个略快 |
| 运行速度 | 调用外部函数 | 内部直接调用 | 第二个略快(无 PLT 跳转) |
| 多进程场景 | 共享内存节省 | 各自独立 | 第一个更优 |
3. 运行时依赖:重要区别
第一个库需要:
# 运行时必须存在这些库
libz.so.1 # 压缩库
libtbb.so.2 # 线程库
ld-linux-aarch64.so.1 # 动态链接器
第二个库:
# 不需要额外的依赖
# 所有代码已在库中
4. 部署影响:⚠️ 关键区别
使用第一个库:
# 部署时需要包含这些库
├── libopencv_core.so
├── libopencv_imgcodecs.so
├── libopencv_imgproc.so
├── libz.so.1 ← 需要额外提供
├── libtbb.so.2 ← 需要额外提供
└── ld-linux-aarch64.so.1 ← 系统提供
使用第二个库:
bash
# 只需要 OpenCV 库
├── libopencv_core.so
├── libopencv_imgcodecs.so
└── libopencv_imgproc.so
5. 在推理环境中的具体影响
✅ 无影响的部分:
-
模型推理精度:完全相同(OpenCV 核心算法一致)
-
图像处理结果:完全相同
-
API 兼容性:完全相同
⚠️ 可能影响的方面:
-
内存使用:
# 查看库大小差异 ls -lh /home/day1/test/lib/libopencv_core.so ls -lh /home/My_Snpe_Project/test/lib/libopencv_core.so如果第二个库更大,运行时内存占用可能略高。
-
多线程性能:
-
第二个库静态链接了 TBB,可能与系统 TBB 版本不同
-
可能影响并行处理的性能表现
-
-
调试和兼容性:
-
第二个库硬编码了 RPATH:
/home/maxin/Code/... -
这个路径在目标设备上可能不存在
-
但运行时通过
LD_LIBRARY_PATH可以覆盖
-
6. 如何检查第二个库是否真的静态链接了
# 检查第二个库的符号是否包含 zlib 函数
readelf -s /home/My_Snpe_Project/test/lib/libopencv_core.so | grep -E "gz|deflate|inflate|tbb"
查看库的大小对比
ls -lh /home/day1/test/lib/libopencv_core.so
ls -lh /home/My_Snpe_Project/test/lib/libopencv_core.so
查看库中是否包含未定义的 zlib 符号
aarch64-linux-gnu-nm -u /home/My_Snpe_Project/test/lib/libopencv_core.so | grep -E "gz|zlib"
推荐做法
如果需要最大化兼容性,建议:
-
使用第二个库(更自包含):
cp /home/My_Snpe_Project/test/lib/libopencv_*.so /home/day1/test/lib/ -
但要注意修复 RPATH:
# 移除硬编码的 RPATH 或修改为相对路径 patchelf --remove-rpath /home/day1/test/lib/libopencv_core.so # 或设置新的 RPATH patchelf --set-rpath '$ORIGIN' /home/day1/test/lib/libopencv_core.so -
测试验证:
# 验证库是否可加载 LD_LIBRARY_PATH=/home/day1/test/lib aarch64-linux-gnu-ldd /home/day1/test/lib/libopencv_core.so
在目标设备上的运行测试
在 ARM 开发板上运行推理程序,观察:
-
程序是否正常启动
-
推理速度是否有变化
-
内存使用是否正常
-
是否有任何异常日志
结论
使用第二个库替换第一个库是安全的,对推理环境的主要影响是:
-
功能完全正常
-
部署更简单(依赖更少)
-
内存占用可能略高(静态链接)
-
性能可能略有提升(减少外部调用)
-
兼容性更好(不依赖系统特定版本)
建议在目标设备上实际测试验证性能差异,但一般来说不会有问题。
解决方案
方案 1:使用第二个库替换第一个库
# 备份原来的库
mv /home/day1/test/lib/libopencv_core.so /home/day1/test/lib/libopencv_core.so.bak
mv /home/day1/test/lib/libopencv_imgcodecs.so /home/day1/test/lib/libopencv_imgcodecs.so.bak
mv /home/day1/test/lib/libopencv_imgproc.so /home/day1/test/lib/libopencv_imgproc.so.bak
复制 My_Snpe_Project 中的库
cp /home/My_Snpe_Project/test/lib/libopencv_core.so /home/day1/test/lib/
cp /home/My_Snpe_Project/test/lib/libopencv_imgcodecs.so /home/day1/test/lib/
cp /home/My_Snpe_Project/test/lib/libopencv_imgproc.so /home/day1/test/lib/
方案 2:为第一个库添加 RPATH
使用 patchelf 工具修改库的 RPATH:
# 安装 patchelf
sudo apt install patchelf
为库添加 RPATH
patchelf --set-rpath "/home/day1/test/lib:/usr/lib/aarch64-linux-gnu" /home/day1/test/lib/libopencv_core.so
patchelf --set-rpath "/home/day1/test/lib:/usr/lib/aarch64-linux-gnu" /home/day1/test/lib/libopencv_imgcodecs.so
方案 4:创建软链接到系统库(如果存在)
cd /home/day1/test/lib
检查系统是否有这些库
ls /usr/lib/aarch64-linux-gnu/libz.so* 2>/dev/null
ls /usr/lib/aarch64-linux-gnu/libtbb.so* 2>/dev/null
如果存在,创建软链接
ln -sf /usr/lib/aarch64-linux-gnu/libz.so.1 libz.so.1
ln -sf /usr/lib/aarch64-linux-gnu/libtbb.so.2 libtbb.so.2