DLL hell 是指 Windows 系统上动态库的新版本覆盖旧版本,且新版本不能兼容旧版本的问题。
例如:装新软件,但原有的软件运行不起来了。
Linux 系统下也同样面临着和 Windows 一样的动态库多版本的问题,其严重影响软件的升级和维护。
那么此问题该如何解决的呢?
Linux 系统为解决这个问题,引入了一套机制,如果遵守这个机制来做,就可以避免这个问题。
但是这只事一个约定,不是强制的。
但是建议遵守这个约定,否则同样也会出现 Linux 系统版的 DLL hell 问题。
下面来介绍一个这个机制。
这个机制是通过文件名,来控制共享库(Shared Library)的版本,它有三个名字,分别又有不同的目的。
1)第一个是共享库的实际文件名(Real Name),
它是编译器产生共享库时或人为修改名字后的文件名,该实际文件名就是为了直观地控制共享库版本。
其格式为:lib + math + .so + 主版本号 + 小版本号 + 制作号
如:libmath.so.1.1.1234。
lib 是 Linux 系统上的库的约定前缀名,
math 是库自已的名字,
so 是共享库的后缀名,
1.1.1234 是共享库的版本号,
格式:主版本号 + 小版本号 + 制作(build)号。
主版本号 - 代表当前共享库的版本,
如果共享库提供的接口函数有变化的话,那么这个版本号就要加壹(1);
小版本号 - 如果引入了新的特性(Feature)的话,那么这个版本号就要加壹(1);
制作号 - 一般仅表示修正了Bug。
2)第二个是共享库的简短文件名(soname - Short for shared object name),
它是可执行程序加载它时,要寻找的文件名。
其格式为:lib + math + .so + 主版本号
如:libmath.so.1
注:在编译链接生成一个实际文件名的共享库时,同时也将简短文件名写进了共享库的文件头里面。
可以用此命令来查看:$readelf -d 共享库的实际文件名
3)第三个是共享库的连接文件名(Link Name),
是专门为可执行程序生成阶段链接共享库时用的名字,不带任何版本信息的。
其格式为:lib + math + .so
如:libmath.so。
在可执行程序链接共享库时:首先会用到共享库的连接文件名,通过连接文件名找到共享库; 然后会取出共享库的简短文件名,并写在共享库自己的文件头里面。
在可执行程序加载共享库时: 通过共享库的简短文件名在给定的路径下寻找共享库。
举例说明:
一 源代码
//hello.h
void hello();
//hello.c
#include "hello.h"
#include <stdio.h>
void hello() {
printf("libhello");
}
//main.c
#include "hello.h"
int main() {
hello();
}
二 生成
1.生成动态链接库
# gcc hello.c -shared -fPIC -Wl,-soname,libhello.so.0 -o libhello.so.0.0.0
2.运行readelf -d libhello.so.0.0.0显示:
[[email protected] tst]$ readelf -d libhello.so.0.0.0
Dynamic section at offset 0xe08 contains 25 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000e (SONAME) Library soname: [libhello.so.0]
0x000000000000000c (INIT) 0x588
0x000000000000000d (FINI) 0x708
0x0000000000000019 (INIT_ARRAY) 0x200de8
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x200df0
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x1f0
0x0000000000000005 (STRTAB) 0x380
0x0000000000000006 (SYMTAB) 0x230
0x000000000000000a (STRSZ) 190 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0x201000
0x0000000000000002 (PLTRELSZ) 72 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x540
0x0000000000000007 (RELA) 0x480
0x0000000000000008 (RELASZ) 192 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffe (VERNEED) 0x460
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x43e
0x000000006ffffff9 (RELACOUNT) 3
0x0000000000000000 (NULL) 0x0
3. 执行ldconfig -vn . (有点),在当前目录县生成soname链接到 libhello.so.0.0.0,此时如下:
运行前:
[[email protected] tst]$ ll
total 20
-rw-rw-r--. 1 qingze qingze 79 Nov 29 13:47 hello.c
-rw-rw-r--. 1 qingze qingze 14 Nov 29 12:44 hello.h
-rwxrwxr-x. 1 qingze qingze 8005 Nov 29 15:19 libhello.so.0.0.0
-rwxrwxr-x. 1 qingze qingze 48 Nov 29 15:26 main.c
运行后:
[[email protected] tst]$ ll
total 20
-rw-rw-r--. 1 qingze qingze 79 Nov 29 13:47 hello.c
-rw-rw-r--. 1 qingze qingze 14 Nov 29 12:44 hello.h
lrwxrwxrwx. 1 qingze qingze 17 Nov 29 15:30 libhello.so.0 -> libhello.so.0.0.0
-rwxrwxr-x. 1 qingze qingze 8005 Nov 29 15:19 libhello.so.0.0.0
-rwxrwxr-x. 1 qingze qingze 48 Nov 29 15:26 main.c
4.运行 ln -s libhello.so.0 libhello.so:
[[email protected] tst]$ ll
total 20
-rw-rw-r--. 1 qingze qingze 79 Nov 29 13:47 hello.c
-rw-rw-r--. 1 qingze qingze 14 Nov 29 12:44 hello.h
lrwxrwxrwx. 1 qingze qingze 13 Nov 29 15:33 libhello.so -> libhello.so.0
lrwxrwxrwx. 1 qingze qingze 17 Nov 29 15:30 libhello.so.0 -> libhello.so.0.0.0
-rwxrwxr-x. 1 qingze qingze 8005 Nov 29 15:19 libhello.so.0.0.0
-rwxrwxr-x. 1 qingze qingze 48 Nov 29 15:26 main.c
5.运行gcc main.c -o main -Wl,-rpath=./ -L. -lhello
[[email protected] tst]$ ll
total 32
-rw-rw-r--. 1 qingze qingze 79 Nov 29 13:47 hello.c
-rw-rw-r--. 1 qingze qingze 14 Nov 29 12:44 hello.h
lrwxrwxrwx. 1 qingze qingze 13 Nov 29 15:33 libhello.so -> libhello.so.0
lrwxrwxrwx. 1 qingze qingze 17 Nov 29 15:30 libhello.so.0 -> libhello.so.0.0.0
-rwxrwxr-x. 1 qingze qingze 8005 Nov 29 15:19 libhello.so.0.0.0
-rwxrwxr-x. 1 qingze qingze 8538 Nov 29 15:36 main
-rwxrwxr-x. 1 qingze qingze 48 Nov 29 15:26 main.c
gcc编译链接动态库时,很有可能编译通过,但是执行时,找不到动态链接库,那是
因为-L选项指定的路径只在编译时有效,编译出来的可执行文件不知道-L选项后面的值,
当然找不到。可以用ldd <your_execute>看看是不有 ‘not found’在你链接的库后面,
解决方法是通过-Wl,rpath=<your_lib_dir>,使得execute记住链接库的位置
个人理解:
参考了网上一些资料,一步步执行时会发生错误,我按照以上方法成功了,所以我认为当动态链接库版本发生变化时只要重新执行第4步,将libhello.so链接到新版本即可!
有待求证!!!