xredis
因为项目中使用到了 xredis (C++开发的redis客户端,是对hiredis的C++封装),在 makefile 中发现使用到了 -Wl,-soname 这个语法,之前没怎么了解过,特此记录
makefile 节选如下:
XREDIS_MAJOR=1
XREDIS_MINOR=10.1
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]
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
可以看看编译后生成的产物:
[root@localhost xredis-1.10.1]
-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
[root@localhost xredis-1.10.1]
0x000000000000000e (SONAME) Library soname: [libxredis.so.1.10.1]
soname
soname 是 Short for shared object name 的缩写,直译就是共享库(动态库)的缩写。
-Wl,-soname -Wl 告诉编译器将后面的参数传递到连接器。而 -soname 指定了共享库的 soname。
那 soname 是怎么怎么产生作用的呢?
使用 ldd 命令或者 readelf 都可以查看应用程序依赖的动态库
[root@localhost lib]
libxredis.so.1.10.1 => /usr/xxx/lib/libxredis.so.1.10.1 (0x00007f2c4ea68000)
[root@localhost lib]
0x0000000000000001 (NEEDED) Shared library: [libxredis.so.1.10.1]
可以看到 yyy 这个模块是依赖于 libxredis.so.1.10.1 这个动态库,但是还记得我们编译生成的产物么?我们编译生成的可是 libxredis.so 这个库啊~
[root@localhost lib]
/home/wuxt/lib
[root@localhost lib]
libxredis.so
[root@localhost lib]
include ld.so.conf.d/*.conf
/home/wuxt/lib
执行 ldconfig 命令,将动态库加入缓存中
[root@localhost lib]
/home/wuxt/lib:
libxredis.so.1.10.1 -> libxredis.so (changed)
我们发现这里做了个软链接
[root@localhost lib]
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 或者是 libxreids 指向 libxredis.so.1.10.2。
举例几个常用的库的 soname
cJSON
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]
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]
0x000000000000000e (SONAME) Library soname: [libcjson.so.1]
系统自带的库:
libz.zo
[root@localhost lib]
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]
0x000000000000000e (SONAME) Library soname: [libz.so.1]
libc.so 不过这个有点奇怪,libc-2.17.so 的 soname 是 libc.so.6?
[root@localhost lib64]
-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]
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
|