linux so库(arm)对比分析

摘要: 本文对比了两个 OpenCV 库(/home/quectel/day1/test/lib//home/quectel/My_Snpe_Project/test/lib/)的差异。第一个库动态链接 libz.so.1libtbb.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.1libtbb.so.2 不需要 libz.so.1libtbb.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_NOWNOW 没有 FLAGS 字段
PLTRELSZ 5304 bytes 22872 bytes
依赖数量 9 个依赖库 7 个依赖库

关键发现

1. 第一个库(day1/test/lib)的问题

  • 依赖 libz.so.1libtbb.so.2:这就是导致编译错误的原因

  • 没有 RPATH:链接器不知道去哪里找这些依赖库

  • 依赖 ld-linux-aarch64.so.1:动态链接器依赖

2. 第二个库(My_Snpe_Project/test/lib)的优势

  • 没有 libzlibtbb 依赖:这些库可能被静态链接了

  • 有 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 兼容性:完全相同

⚠️ 可能影响的方面:
  1. 内存使用

    复制代码
    # 查看库大小差异
    ls -lh /home/day1/test/lib/libopencv_core.so
    ls -lh /home/My_Snpe_Project/test/lib/libopencv_core.so

    如果第二个库更大,运行时内存占用可能略高。

  2. 多线程性能

    • 第二个库静态链接了 TBB,可能与系统 TBB 版本不同

    • 可能影响并行处理的性能表现

  3. 调试和兼容性

    • 第二个库硬编码了 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"

推荐做法

如果需要最大化兼容性,建议:

  1. 使用第二个库(更自包含):

    复制代码
    cp /home/My_Snpe_Project/test/lib/libopencv_*.so /home/day1/test/lib/
  2. 但要注意修复 RPATH

    复制代码
    # 移除硬编码的 RPATH 或修改为相对路径
    patchelf --remove-rpath /home/day1/test/lib/libopencv_core.so
    # 或设置新的 RPATH
    patchelf --set-rpath '$ORIGIN' /home/day1/test/lib/libopencv_core.so
  3. 测试验证

    复制代码
    # 验证库是否可加载
    LD_LIBRARY_PATH=/home/day1/test/lib aarch64-linux-gnu-ldd /home/day1/test/lib/libopencv_core.so

在目标设备上的运行测试

在 ARM 开发板上运行推理程序,观察:

  • 程序是否正常启动

  • 推理速度是否有变化

  • 内存使用是否正常

  • 是否有任何异常日志

结论

使用第二个库替换第一个库是安全的,对推理环境的主要影响是:

  1. 功能完全正常

  2. 部署更简单(依赖更少)

  3. 内存占用可能略高(静态链接)

  4. 性能可能略有提升(减少外部调用)

  5. 兼容性更好(不依赖系统特定版本)

建议在目标设备上实际测试验证性能差异,但一般来说不会有问题。

解决方案

方案 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
相关推荐
H_oRIZoN_25 分钟前
Linux入门DAY41(51 单片机 串口与通信协议)
linux·运维·单片机
小马同学-2 小时前
OpenStack 使用实战:Web 界面与 CLI 命令行实验
运维·云计算·openstack
GeW2 小时前
RHCE备考别瞎学!按这个计划走,30天提高通过率,快速拿证
linux
Escalating_xu2 小时前
【System V 信号量】从 P/V 原语到 Builder 封装:写出可控、可清理的进程互斥组件
java·linux·开发语言·jvm
傲世仙尊2 小时前
System V 进程间通信详解:共享内存、消息队列与信号量(CSDN博客)
linux·开发语言·c++
码农小韩3 小时前
Linux应用开发(六)——进程间通信
linux·linux驱动·嵌入式软件开发·嵌入式操作系统·linux应用
网安老伯3 小时前
都2026年了,还在问网络安全怎么入门?看完这一篇你就懂了
运维·计算机网络·web安全·网络安全·wireshark·密码学·网络攻击模型
小祺先生3 小时前
mt7921 debian系统 如何安装驱动
运维·网络·debian
笨笨饿3 小时前
#138_解决Codex要五次回复的问题
linux·stm32·单片机·嵌入式硬件·mcu·物联网·嵌入式实时数据库
m0_459872923 小时前
Linux 系统基础
linux·运维·服务器