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
相关推荐
lengjingzju6 小时前
编译与调试完全指南—第10章 网络分析工具
linux
星野川崎2066 小时前
电商多店运维:云机24小时挂机频繁掉线、账号无故风控深度原因分析及解决方案
大数据·运维·服务器·云计算·电商
ltl6 小时前
新硬件对存储的影响:ZNS、CXL 与计算存储
linux
RisunJan7 小时前
Linux命令-xauth(X11 认证授权管理)
linux·服务器·microsoft
又见情义7 小时前
RK3568 Android 13 板载驱动适配-USB
android·arm开发·驱动开发
不灭的程序员阿澄8 小时前
在飞牛 NAS 上用 Docker 运行 DeepSeek Harness
运维·docker·容器
Elastic 中国社区官方博客9 小时前
让大模型思考,让小模型执行:在 Elastic Workflows 中拆分 LLM 成本
大数据·运维·数据库·人工智能·elasticsearch·ai
艾伦_耶格宇9 小时前
【AI】-4 OpenCode Go 接入 Obsidian 完整指南
运维·开发语言·人工智能·agent·opencode
SXkehuirongsheng9 小时前
APP开发定制和模板开发哪个更实用?
大数据·运维·人工智能