Ubuntu Linux---GNU libc库( 二 )


- 当对共享库提供的接口做了与以前版本不兼容的改变时,需要增大大版本号 。这个大的改变意味着依赖该库先前大版本的应用程序需要作相应修改才能使用大版本更新后的库 。
- 当共享库增加了新的接口同时也保留了原来的接口时,增大小版本号 。
- 当作了与以前兼容的修改又没有增加新接口时,增大发布号 。这通常是对一些实现做了改动以提高性能和扩展性 。
要在Linux上创建共享库,使用-shared编译参数;该参数告诉GNU ld创建一个共享库而不是应用程序 。下面是这样一个例子:
(代码)p58 最后一行 $ gcc o libfoo.so shared fpic foo.c
上一页;;[1];[2][3];[4];[5];下一页
3.4 库版本化
在共享库和应用程序之间维护二进制级的兼容性或ABI是很重要的 。共享库的ABI是应用程序依赖的运行时接口;如果每次发布时共享库的ABI都与以前的兼容,那么在其中某一个版本的共享库上编译的应用程序不需要任何改动就可以在后续版本上运行 。库版本化就是Linux以及同期的其他操作系统实现二进制兼容性的方法 。
我们以前移植过的一些应用程序需要库版本化的支持 。各UNIX平台也都实现了库版本化,但实现的方法不尽相同 。Linux提供了两种不同的技术来实现库版本化:外部库版本化和符号版本化 。
3.4.1 外部库版本化
链接过程中,链接器(ld)会查找以.so结尾的共享库文件 。以.so结尾的库文件叫链接器名称,这是由他们在Linux上的使用方式决定的 。当编译一个依赖某一共享库的应用程序时,仅仅是该共享库的soname(不是共享库的文件名)作为依赖关系被记录在应用程序的二进制代码中 。运行时链接器就是使用共享库的soname来查找和装载该库的 。共享库的soname只包含有大版本号(例如,libfoo.so.1)
当修改后的共享库与以前版本不兼容时,新的共享库必须有一个新的外部版本名称 。也就是说,该库的soname必须改变 。这些不兼容的修改包括:删除一个符号,去掉某函数的一个参数,改变了某函数的语义属性以致与以前的定义不再一致并且与老版本二进制不兼容等等 。我们来看下面的例子 。(见pdf附件 341.pdf)
 3.4.2 符号版本化
就像前面所提到的,当对共享库所作的修改能够向前兼容时,我们只增大小版本号 。这种修改包括增加一些新的接口同时又不改变已有的接口 。但是,即使只做这种小版本的修改,也会出现一个很重要的问题:一个在某一小版本的共享库上编译的应用程序并不一定能够在以前小版本的库上运行 。这是因为该应用程序可能使用了新增加的、以前小版本的库中没有的接口 。为了解决这个问题,引入了符号版本化 。符号版本化允许共享库记录下每个小版本都新增了什么内容 。
在Linux上,GNU ld可以使用-version-script连接器选项来创建符号版本化的共享库 。编译器选项-Wl,--version-script=mapfile告诉链接器哪些符号要从生成的共享库中输出出来 。每个符号分属global(被输出)和local(不被输出)两类中的一种 。来看下面的例子 。foo.c包含一个函数foo1,该文件用来创建1.1版本的共享库 。(见附件 示例代码.pdf)
可以看到,这次main只引用了版本化库的LX_1.1 。
GNU ld还允许在定义符号的源文件中把符号绑定到某一版本中,而不仅仅是在脚本文件中指定 。另外,GNU ld还允许同一函数的多个版本出现在同一个共享库中 。更多详细信息,请参考GNU ld手册(注释13)和Ulrich Drepper的文章“How to Write Shared Libraries 。
从2.1版本开始,glibc就已经实现了符号版本化 。符号版本化同时也是LSB规范1.2及更高版本的一部分 。

推荐阅读