Linux 动态库 soname 实践

2023-11-08

xredis

因为项目中使用到了 xredis (C++开发的redis客户端,是对hiredis的C++封装),在 makefile 中发现使用到了 -Wl,-soname 这个语法,之前没怎么了解过,特此记录

makefile 节选如下:

XREDIS_MAJOR=1
XREDIS_MINOR=10.1

# Fallback to gcc when $CC is not in $PATH.
CC:=g++
OPTIMIZATION?=-O3
WARNINGS=-Wall -W -Wwrite-strings
DEBUG?= -g -ggdb
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CFLAGS) $(WARNINGS) $(DEBUG) $(ARCH)
REAL_LDFLAGS=$(LDFLAGS) $(ARCH)

DYLIBSUFFIX=so
STLIBSUFFIX=a
DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(XREDIS_MAJOR).$(XREDIS_MINOR)
DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(XREDIS_MAJOR)
DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
STLIB_MAKE_CMD=ar rcs $(STLIBNAME)

尝试 make 一下

[root@localhost xredis-1.10.1]# make
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient.cpp -o src/xRedisClient.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_keys.cpp -o src/xRedisClient_keys.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_sets.cpp -o src/xRedisClient_sets.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_strings.cpp -o src/xRedisClient_strings.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_connection.cpp -o src/xRedisClient_connection.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_hashs.cpp -o src/xRedisClient_hashs.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_lists.cpp -o src/xRedisClient_lists.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisClient_sortedsets.cpp -o src/xRedisClient_sortedsets.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisPool.cpp -o src/xRedisPool.o
g++ -O3 -fPIC  -Wall -W -Wwrite-strings -g -ggdb  -c src/xRedisFunc.cpp -o src/xRedisFunc.o
g++ -shared -Wl,-soname,libxredis.so.1.10.1 -o libxredis.so  src/xRedisClient.o src/xRedisClient_keys.o src/xRedisClient_sets.o src/xRedisClient_strings.o src/xRedisClient_connection.o src/xRedisClient_hashs.o src/xRedisClient_lists.o src/xRedisClient_sortedsets.o src/xRedisPool.o src/xRedisFunc.o
ar rcs libxredis.a src/xRedisClient.o src/xRedisClient_keys.o src/xRedisClient_sets.o src/xRedisClient_strings.o src/xRedisClient_connection.o src/xRedisClient_hashs.o src/xRedisClient_lists.o src/xRedisClient_sortedsets.o src/xRedisPool.o src/xRedisFunc.o

关键信息为

g++ -shared -Wl,-soname,libxredis.so.1.10.1 -o libxredis.so

这里编译生成的对象是 libxredis.so 这个动态库,其 soname 为 libxredis.so.1.10.1

可以看看编译后生成的产物:

# 有一个 libxredis.a 静态库和一个 libxredis.so 动态库
[root@localhost xredis-1.10.1]# ll libxredis.*
-rw-r--r--. 1 root root 3405976 Oct  5 17:40 libxredis.a
-rwxr-xr-x. 1 root root 1319008 Oct  5 17:40 libxredis.so

可以使用 readelf 命令查看动态库的 soname

# 为了简洁些,使用了 grep 作过滤
[root@localhost xredis-1.10.1]# readelf -d libxredis.so  | grep soname
 0x000000000000000e (SONAME)             Library soname: [libxredis.so.1.10.1]

soname

soname 是 Short for shared object name 的缩写,直译就是共享库(动态库)的缩写。

-Wl,-soname -Wl 告诉编译器将后面的参数传递到连接器。而 -soname 指定了共享库的 soname。

那 soname 是怎么怎么产生作用的呢?

使用 ldd 命令或者 readelf 都可以查看应用程序依赖的动态库

# 为了简洁些,使用了 grep 作过滤
[root@localhost lib]# ldd /usr/xxx/bin/yyy | grep xredis
	libxredis.so.1.10.1 => /usr/xxx/lib/libxredis.so.1.10.1 (0x00007f2c4ea68000)
# 在 ldd 命令打印的结果中,“=>”左边的表示该程序需要连接的共享库之 so 名称,右边表示由 Linux 的共享库系统找到的对应的共享库在文件系统中的具体位置。
# 为了简洁些,使用了 grep 作过滤
[root@localhost lib]# readelf -d /usr/xxx/bin/yyy | grep xredis
 0x0000000000000001 (NEEDED)             Shared library: [libxredis.so.1.10.1]

可以看到 yyy 这个模块是依赖于 libxredis.so.1.10.1 这个动态库,但是还记得我们编译生成的产物么?我们编译生成的可是 libxredis.so 这个库啊~

# 将编译生成的 libxredis.so 放到 /home/wuxt/lib 下
[root@localhost lib]# pwd
/home/wuxt/lib
[root@localhost lib]# ls
libxredis.so

# 将 /home/wuxt/lib 加入动态库查找路径
[root@localhost lib]# cat /etc/ld.so.conf
include ld.so.conf.d/*.conf
/home/wuxt/lib

执行 ldconfig 命令,将动态库加入缓存中

[root@localhost lib]# ldconfig -v
/home/wuxt/lib:
	libxredis.so.1.10.1 -> libxredis.so (changed)

我们发现这里做了个软链接

[root@localhost lib]# ll
total 1292
-rwxr-xr-x. 1 root root 1319008 Oct  5 18:00 libxredis.so
lrwxrwxrwx. 1 root root      12 Oct  5 19:35 libxredis.so.1.10.1 -> libxredis.so

将动态库的 soname 指向动态库本身,这样当 yyy 模块需要依赖 libxredis.so.1.10 时,就会找到具体的 libxredis.so 这个库。

问题

但我认为这个 xredis 的 makefile 编写有问题,回到 xredis 的编译

g++ -shared -Wl,-soname,libxredis.so.1.10.1 -o libxredis.so

soname 应该是 libxredis.so.1 或者是 libxredis.so,生成的对象是带完整版本的 libxredis.so.1.10.1,这样当 xredis 升级至 1.10.2 时,还是由 libxredis.so.1 或者是 libxredis.so 指向 libxredis.so.1.10.2。

举例几个常用的库的 soname

cJSON

cJSON

看下 makefile 编写

# 节选 cjson 的 makefile
LIBVERSION = 1.7.15
CJSON_SOVERSION = 1
UTILS_SOVERSION = 1

CJSON_SO_LDFLAG=-Wl,-soname=$(CJSON_LIBNAME).so.$(CJSON_SOVERSION)
UTILS_SO_LDFLAG=-Wl,-soname=$(UTILS_LIBNAME).so.$(UTILS_SOVERSION)

编译 cJSON

[root@localhost cJSON]# ll libcjson.so*
lrwxrwxrwx. 1 root root    13 Oct  5 21:10 libcjson.so -> libcjson.so.1
lrwxrwxrwx. 1 root root    18 Oct  5 21:10 libcjson.so.1 -> libcjson.so.1.7.15
-rwxr-xr-x. 1 root root 46731 Oct  5 21:10 libcjson.so.1.7.15

查看 libcjson.so.1.7.15 的 soname

[root@localhost cJSON]# readelf -d libcjson.so.1.7.15 | grep soname
 0x000000000000000e (SONAME)             Library soname: [libcjson.so.1]

系统自带的库:

libz.zo

[root@localhost lib]# ll libz.so*
lrwxrwxrwx. 1 root root     13 Jan 19  2021 libz.so -> libz.so.1.2.7
lrwxrwxrwx. 1 root root     13 Jan 19  2021 libz.so.1 -> libz.so.1.2.7
-rwxr-xr-x. 1 root root 109093 Sep 27  2020 libz.so.1.2.7
[root@localhost lib]# 
[root@localhost lib]# readelf -d libz.so.1.2.7 | grep soname
 0x000000000000000e (SONAME)             Library soname: [libz.so.1]

libc.so 不过这个有点奇怪,libc-2.17.so 的 soname 是 libc.so.6?

[root@localhost lib64]# ll libc.so*
-rw-r--r--. 1 root root 253 Jan 19  2015 libc.so
lrwxrwxrwx. 1 root root  12 May 19  2017 libc.so.6 -> libc-2.17.so
[root@localhost lib64]# 
[root@localhost lib64]# readelf -d libc-2.17.so | grep soname
 0x000000000000000e (SONAME)             Library soname: [libc.so.6]
[root@localhost lib64]# 

总结

Linux 系统的这种动态库管理方式值得我们在实际项目的动态库管理中使用,既保证了动态库的升级,又能得到方便地使用。

Linux 的动态库的命名格式是 libbar.so.x.y.z,最后一个 z 版本的变动一定是兼容的。y 版本升级一般向前兼容。所以这个 y 和 z 不能写死。x 版本变动一般是不兼容升级。所以使用 soname 是最为合理的。

参考:
linux下动态库中的soname

linux下动态库soname简介

Linux动态库soname的使用

Linux下动态链接库文件的realname、soname和linkname

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux 动态库 soname 实践 的相关文章

  • 如何在 Vim 中突出显示 Bash 脚本?

    我的 Vim 编辑器自动突出显示 PHP 文件 vim file php HTML 文件 vim file html 等等 但是当我输入 vim file在里面写一个Bash脚本 它不会突出显示它 我如何告诉 Vim 将其突出显示为 Bas
  • 应用程序中两个不同版本的库

    考虑一个场景 其中有两个不同版本的共享库 考虑 A 1 so 链接到 B so A 2 so 链接到 C so 现在 B so 和 C so 都链接到 d exe 当 B so 想要调用 A 1 so 中的函数时 它最终会调用 A 2 so
  • Linux中如何避免sleep调用因信号而中断?

    我在 Linux 中使用实时信号来通知串行端口中新数据的到达 不幸的是 这会导致睡眠呼叫在有信号时被中断 有人知道避免这种行为的方法吗 我尝试使用常规信号 SIGUSR1 但我不断得到相同的行为 来自 nanosleep 联机帮助页 nan
  • Linux 内核使用的设备树文件 (dtb) 可视化工具? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个可以图形化表示Linux内核中使用的硬件设备树的工具 我正在尝试了解特定 Arm 芯片组
  • 在ubuntu中打开spyder

    我想在ubuntu中打开spyder Python IDE 通常我会在 shell 中编写 spyder 它会打开spyder IDE 现在 当我在shell中编写spyder时 它只是换行 什么也没有发生 类似于按 enter 我如何找回
  • 在 Linux 中重新启动时,新创建的文件变为 0 kb(数据被覆盖为空)

    我遇到了一个奇怪的问题 这让我发疯 当前的任务是在 root 用户第一次登录时启动一组文件 并在同一用户第二次登录时启动另一组文件 我决定使用 profile 和 bashrc 文件 并在第一次登录期间发生的任务结束时重新加载 bashrc
  • 更新Linux中的包含路径

    我的 my path to file 文件夹中有几个头文件 我知道如何将这些文件包含在新的 C 程序中 但每次我都需要在包含它之前输入头文件的完整路径 我可以在linux中设置一些路径变量 以便它自动查找头文件吗 您可以创建一个 makef
  • MySQL 与 PHP 的连接无法正常工作

    这是我的情况 我正在尝试使用 Apache 服务器上的 PHP 文件连接到 MySQL 数据库 现在 当我从终端运行 PHP 时 我的 PHP 可以连接到 MySQL 数据库 使用 php f file php 但是当我从网页执行它时 它只
  • Ruby:在 Ubuntu 上安装 rmagick

    我正在尝试在 Ubuntu 10 04 上安装 RMagick 看起来here https stackoverflow com questions 1482823 is there an easy way to install rmagic
  • 链接错误:命令行中缺少 DSO

    我对 Linux 使用 Ubuntu 14 04 LTS 64 位 相当陌生 来自 Windows 并且正在尝试移植我现有的 CUDA 项目 当通过链接时 usr local cuda bin nvcc arch compute 30 co
  • Linux shell 从用户输入中获取设备 ID

    我正在为一个程序编写安装脚本 该程序需要在其配置中使用 lsusb 的设备 ID 因此我正在考虑执行以下操作 usblist lsusb put the list into a array for each line use the arr
  • “git add”返回“致命:外部存储库”错误

    我刚刚进入 git 的奇妙世界 我必须提交我对程序所做的一系列更改 位于名为的目录中 var www myapp 我创建了一个新目录 home mylogin gitclone 从这个目录中 我做了一个git clone针对公共回购 我能够
  • 如何并行执行4个shell脚本,我不能使用GNU并行?

    我有4个shell脚本dog sh bird sh cow sh和fox sh 每个文件使用 xargs 并行执行 4 个 wget 来派生一个单独的进程 现在我希望这些脚本本身能够并行执行 由于某些我不知道的可移植性原因 我无法使用 GN
  • /sys/device/ 和 dmidecode 报告的不同 CPU 缓存大小

    我正在尝试获取系统中不同缓存级别的大小 我尝试了两种技术 a 使用 sys device 中的信息 这是输出 cat sys devices system cpu cpu0 cache index1 size 32K cat sys dev
  • Linux 中 m 标志和 o 标志将存储在哪里

    我想知道最近收到的路由器通告的 m 标志和 o 标志的值 从内核源代码中我知道存储了 m 标志和 o 标志 Remember the managed otherconf flags from most recently received R
  • 有没有一种快速方法可以从 Jar/war 中删除文件,而无需提取 jar 并重新创建它?

    所以我需要从 jar war 文件中删除一个文件 我希望有类似 jar d myjar jar file I donot need txt 的内容 但现在我能看到从 Linux 命令行执行此操作的唯一方法 不使用 WinRAR Winzip
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • ALSA:snd_pcm_writei 调用时缓冲区不足

    当运行我最近从灰烬中带回来的旧程序时 我遇到了缓冲区不足的情况 该程序将原始声音文件完全加载到内存中 2100 字节长 525 帧 并准备 ALSA 进行输出 44 1khz 2 通道 有符号 16 位 if err snd pcm set
  • docker 非 root 绑定安装权限,WITH --userns-remap

    all 尝试让绑定安装权限正常工作 我的目标是在容器中绑定安装卷 以便 a 容器不以 root 用户身份运行入口点 二 docker daemon 配置了 userns remap 这样容器 主机上没有 root c 我可以绑定挂载和读 写
  • 通过 Visual Studio 2017 使用远程调试时 Linux 控制台输出在哪里?

    我的Visual Studio 2017 VS2017 成功连接Linux系统 代码如下 include

随机推荐

  • Data Structure (三)

    动态规划 1 区间调度问题 1 1无权区间调度问题 任务j开始于sj 结束于fj 如果两个任务没有重叠的时间 则两个任务互相兼容 目标 找到最多 最大互相兼容的任务集合 贪心算法总是做出当前最优的选择 贪心算法并不总能得到最优解 但是它是最
  • MATLAB绘图设置坐标轴标注

    绘图之后设置坐标轴标注 以下均为用例 自行按需更改 xlim 0 512 限制x轴坐标数值范围 ylim 0 512 限制y轴坐标数值范围 set gca XTick 0 510 4 512 设定x轴坐标刻度 0 512是数值范围 512
  • Qt开发,链接了数据库后,调用QSqlQuery::setQuery执行SQL语句获取我们想要的数据

    继上篇文章将数据库封装成一个类 链接为成员函数 而当我将查询做为另一个函数时 无法对已有的database进行操作 尽管db为类的成员 同样会报错误 QSqlQuery exec database not open QSqlQueryMod
  • C#判断是否是以管理员权限允许当前应用

    private static bool CheckForAdminRights string path Path Combine Environment GetFolderPath Environment SpecialFolder Win
  • 微信小程序防止后退,返回主页,30秒看完关闭

    防止后退 使用 wx redirectTo 代替wx navigateto 关闭返回主页 在onShow function 中调用wx hideHomeButton 讲完收工
  • 国产chatgpt:基于chatGLM微调nlp信息抽取任务

    文章目录 一 传统nlp做信息抽取 二 什么是零样本和少样本 1 零样本和少样本的概念 2 零样本和少样本的应用场景 3 零样本和少样本在大模型时代的优势和意义 4 相比传统NLP 零样本和少样本学习具有以下优势 三 大模型时代信息抽取 c
  • pip 使用国内镜像源及常用命令

    Python pip默认是从pypi org官网下载包 即使用的是国外的镜像源 https pypi python org simple 因此在下载安装包时速度非常慢 还经常出现连接超时 导致下载失败的情况 所以 一般在下载安装包的时候 都
  • 一文解决java.lang.UnsatisfiedLinkError

    首先大家先了解下 ABI和CPU 不同的 Android 手机使用不同的 CPU 而不同的 CPU 支持不同的指令集 CPU 与指令集的每种组合都有专属的应用二进制接口 即 ABI 每个 ABI 支持一个或多个指令集 每个 ABI 支持的指
  • Java语言中的重写(override)和重载(overload)

    Java语言中的重写 override 和重载 overload 重写 override 和重载 overload 是编程语言中的两个常见概念 用于描述函数或方法的特定行为 重写指的是在子类中重新定义 覆盖 父类中已经存在的同名方法 重写可
  • 习题2软件工程

    3 4 1 不是 通常所说的结构化程序 是按照狭义的结构程序的定义衡量 符合定义规定的程序 图示的程序的循环控劇结构有两个出口 显然不符合狭义的结构程序的定义 因此是非结构化的程序 2
  • aspose文档格式转换

    文章目录 Word转Pdf html转pdf pdf转word Word转Pdf public static void main String args throws Exception Document doc new Document
  • pikachu靶场CSRF之TOKEN绕过

    简介 Pikachu靶场中的CSRF漏洞环节里面有一关CSRF TOKEN 这个关卡和其余关卡稍微有点不一样 因为表单里面存在一个刷新就会变化的token 那么这个token是否能绕过呢 接下来我们来仔细分析分析 实战过程 简单尝试 先利用
  • 11月10日 生命值,减少生命值,创建生命值UI UE4斯坦福 学习笔记

    制作角色属性Comp 添加一个Actorcomp 在 h内添加生命值与减少血量的函数 protected 只在蓝图内可以编辑 在编辑器界面不能编辑 UPROPERTY EditDefaultsOnly BlueprintReadOnly C
  • Qt应用开发(基础篇)——颜色选择器 QColorDialog

    一 前言 QColorDialog类继承于QDialog 是一个设计用来选择颜色的对话框部件 对话框窗口 QDialog QColorDialog颜色选择器一般用来让用户选择颜色 比如画图工具中选择画笔的颜色 刷子的颜色等 你可以使用静态函
  • 彻底卸载MySQL8.0

    环境需求 win10 MySQL8 0 彻底卸载 1 停止MySQL服务 启动任务管理器 gt 选择服务 gt 找到MySQL gt 右键停止 如果有多个MySQL服务 也全部都要停掉 2 卸载MySQL相关所有组件 打开看控制面板 gt
  • 使用树莓派进行远程视频转播(内网穿透)

    一 准备材料 实体 树莓派摄像头 树莓派 虚拟 云服务器 二 先测试树莓派进行局域网转播 这里是需要安装的软件 sudo apt get install subversion libjpeg8 dev imagemagick libv4l
  • 线性代数系列讲解第七篇 正交向量及正交空间

    正交向量 orthogonal vector 毕达哥拉斯定理 勾股定理 Pythagoras 我们很容易得出 x 2 y 2 x y 2 x 2 y 2 x y 2 x 2 y 2 x y 2 这就是勾股定理 我们可以将一个向量的模的平方写
  • 服务器改配项目,网络服务器搭建(项目五)[xxxx1214修改].ppt

    网络服务器搭建 项目五 xxxx1214修改 4 查看启动信息 service named restart 如果named服务无法正常启动 可以查看提示信息 根据提示信息更改配置文件 5 查看端口 如果服务正常工作 则会开启TCP和UDP的
  • 自动化测试:python测试结果和报告自动发送邮件

    一 带有附件发送邮件 1 导入模块 MIMEMultipart from email mime multipart import MIMEMultipart 复制 2 先读取要发送文件的内容 file new 是测试报告路径的参数名 3 下
  • Linux 动态库 soname 实践

    xredis 因为项目中使用到了 xredis C 开发的redis客户端 是对hiredis的C 封装 在 makefile 中发现使用到了 Wl soname 这个语法 之前没怎么了解过 特此记录 makefile 节选如下 XREDI