随笔 - 298  文章 - 377  trackbacks - 0
<2008年6月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

常用链接

留言簿(34)

随笔分类

随笔档案

文章档案

相册

收藏夹

搜索

  •  

最新评论

阅读排行榜

评论排行榜

在发布linux-fpga系列文章 和PowerPC平台Linux的移植系列文章后,很多朋友发mail来问我一些执行软件上面的问题,大部分都是和软件所使用的链接库相关的。Novell的官方网站上有一篇文章 对相关问题介绍得比较详细,我就参照该文章写出中文版,希望能对遇到此类问题的朋友有所帮助。

介绍

很多时候,诸如glibc这种基本库,由于有着不同的版本存在,如果正好你手上只有适用于某个版本的packages,就会遇到各种各样的问题。所以,如果你打算在不同版本上使用这些软件包,就需要寻找一个比较好的移植方法。而这篇文章正是介绍相应方法的。在往下看之前,请确保你有能力安装和配置相应的文件(因为文档中也会提供一些提示)。下面所有的示例都基于X86硬件平台,如果你使用的是其他体系,那么可能示例代码和相应的技巧都需要改变。本文是在实际工程中所使用步骤的总结性描述,欢迎提出意见和指正。

背景


一个Linux程序通常都包含有执行特定功能的机器代码,而很多功能通常都由库文件提供。程序员朋友都知道,库文件又分为静态版本和动态共享版本,当一个程序被创建时,开发者就会决定是使用动态库还是静态库。在使用静态库的程序中,可能出现不同版本或者在静态库的基础架构上产生变化而引起的二进制不兼容。由于C++的成熟但是未通用标准化,经常会出现后者的情况。详细点说,就是一些具体实现的细节,比如,如果用于创建库的编译器版本不同,派生继承格(derived inheritance lattices)中的虚拟函数进行地址映射的机制,就会使相同库拥有不同且互不兼容的版本。

共享库倒是有很多好处,机器代码只需要加载到内存一次;使用这个库的不同程序都在共享它,而且只有程序具体所需要的数据才会在内存中分配空间;另外,系统管理员可以很轻松得升级共享库,而无需接触那些使用这个库的程序,这一点对于安全特性来说特别重要。

说了半天,上面没有一个特点是Linux特性,因为这些都是各种现代操作系统所使用或者支持库的常规描述,所以就不废话了,具体的细节建议Google。

剧本

终于进入正题了。你遇没遇到过这种问题,辛辛苦苦down下来一个新版本程序,正准备在你新版本,最新推出的Linux系统上使用,却发现不能运行?恩,这种问题通常发生于libc4到libc5的转换或者libc5到libc6的转换,后者就是通常所说的glibc (the GNU C library)。

C的库基本上是所有UNIX/Linux系统中最重要的库文件,因为它们扮演了所有应用程序和内核之间的接口角色。如果这种核心库文件被改变,而这种改变又不向后兼容,那么,就可能导致整个系统都无法使用。

当然还有另外一种情况,一个程序已经在一个使用了较新版本的库的系统中被构建,而你如今想将它安装到你的当前系统中,当然,这种程序是不可能正常运行的,原因就是程序很可能会使用到只有较新版本的库文件中才会拥有的新的或者改动过的特性。正如所有人都知道的那样,包含有旧版本库所没有的功能的新版本库是不会被加载以及执行的。下面以glibc举例子:


GLIBC 版本 文件名称
libc5 /lib/libc.so.5
libc6 /lib/libc.so.6

有时版本号还有附加后缀,比如libc.so.6.3.3.

正如在背景中所描述的那样,由版本号大于3.3的GNU C++所编译的C++库的二进制不兼容性,会导致额外的复杂度,就算是在当前包括了一致的API(Application Programming Interface)和ABI(Application Binary Interface)定义的标准化环境中也同样如此。而且,未来C++ compilers和库的版本是否会保持完全兼容,也尚未得知。针对这个内容,LSB(Linux Standards Base)推荐所有的软件开发商用C++开发软件的时候,C++部分都使用静态链接,不幸的是没几个开发商是遵循了这个推荐的。更糟糕的是,Linux的发布厂商通常都会创建自己版本的C++编译器,却没有让它们的共享库和标准C++编译器的一致。

总的来说,如果你想运行一个程序,却失败了,错误信息和下面这条信息类似,那么这个问题可能能够通过本文“解决方案”部分的建议来解决。

运行程序时的错误信息

./a.out: relocation error: ./a.out: symbol errno, version GLIBC_2.0 not defined in file libc.so.6 with link time reference

如果你恰好遇到像上面这样的关于symbol errno的错误信息,就说明你的程序被链接到了某个版本号低于2.3的glibc上。为了保证其线程安全的特性(每个线程尽量只访问别的线程不访问的变量或内存,如果硬是要访问同一变量或内存的话,就要采用适当的互斥机制来避免由于线程切换而导致的不确定性),更新版本的glibc不再将errno作为全局变量来提供。在这种情况下,可能你不得不安装拥有较老版本(比如2.2.5版本)的兼容环境。

但是,如果你并没有看见上述信息,而程序还是没有按照你所想象那样的正常运行,那你可能就是遇到了上述介绍的C++相关问题。我们知道,运行时链接器(runtime linker)用于将你的程序加载到内存中,并负责解决任何和共享库相关的依赖,它还能够在你的系统中定位所需要的共享库,并将它们加载到内存中,但是,这个共享库不一定适合目前你希望运行的程序。这就是问题所在。

解决方案

等你确定了问题的源头后,就可以创建一个兼容环境,来提供正常执行程序所需要的功能特性。在这里强烈推荐一种方法,就是在标准系统文件路径,例如/lib和 /usr/lib外安装附加的库文件,如下所示 :


想移植的程序名 zoo
用于安装兼容文件的目录地址的前缀 /opt/compat-env/zoo
需要的库 glibc-2.3.2-95.27
libgcc-3.2.3-42 libstdc++-3.2.3-42
所需要文件最初的源目录 /root/zoo-src

第一步

在自行规定的目录下创建路径/bin和/lib:

% prefix=/opt/compat-env/zoo
% mkdir -p $prefix/bin $prefix/lib

第二步
将所需要的文件拷贝到相应的位置:

% srcdir=/root/zoo-src
% cd $prefix/bin
% cp -p $srcdir/zoo .
% cd $prefix/lib
% rpm2cpio $srcdir/glibc-2.3.2-95.27*.rpm | cpio -idvm ‘*libc*.so*’
% find . -name ‘*libc*.so*’ -exec mv -v ‘{}’ . \;
% rpm2cpio $srcdir/glibc-2.3.2-95.27*.rpm | cpio -idvm ‘*ld*.so*’
% find . -name ‘*ld*.so*’ -exec mv -v ‘{}’ . \;
% rpm2cpio $srcdir/libgcc-3.2.3-42*.rpm | cpio -idvm ‘*libgcc_so*.so*’
% find . -name ‘*libgcc_so*.so*’ -exec mv -v ‘{}’ . \;
% rpm2cpio $srcdir/libstdc++-3.2.3-42*.rpm | cpio -idvm ‘*libstdc*.so*’
% find . -name ‘*libstdc*.so*’ -exec mv -v ‘{}’ . \;
% rm -rf lib usr

第三步
创建一个脚本,用于建立合适的环境变量和启动程序:

% cd $prefix/bin
% mv zoo zoo.exec
% cat > zoo libcwait.c zoo
posted on 2008-06-09 05:18 聂文龙 阅读(709) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理