您的位置:首页 > 运维架构 > Linux

如何解决linux下编译环境,运行环境不同的问题

2010-10-21 19:52 781 查看


如何解决linux下编译环境,运行环境不同的问题

很多情况下编译是在一台机子上运行,而执行环境却不同,在网上找了一个方法,Novell的官方网站提供英
文:http://www.novell.com/coolsolutions/feature/11775.html
中文翻译http://www.forwind.cn/2007/10/03/linux-binary-only/
如何在不同版本Linux上运行Binary-Only应用包
在发布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 << EOF
#! /bin/bash
prefix=/opt/compat-env/zoo
LD_LIBRARY_PATH=”$prefix/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}”
export LD_LIBRARY_PATH
$prefix/lib/ld-linux.so.2 ${0}.exec “$@”
EOF
% chmod 755 zoo
现在,zoo程序就能正常启动了,而且glibc, libgcc和libstdc++的版本也可以按照自己的需要规定了。
附加问题
在你安装上述步骤建立起兼容环境后,应用程序仍然有可能无法被执行,比如下面这个错误信息所代表的例子:
symbol __libc_wait, version GLIBC_2.0 not defined in file libc.so.6 with
link time reference
更新版本的GNU
C库不仅仅是对库碑身分配版本号,对单独的symbols也会分配版本号,这种情况通常发生于需要遵循特定的语言标准时。在这个例子中,带版本号
GLIBC_2.0的全局变量__libc_wait不再被define定义。如果想解决这个问题,或者使用较旧版本的C语言库,或者使用glibc的另
外的特性以及对应的运行时链接器(runtime linker’)。假设正常的共享对象是在runtime
linker处于解决依赖问题的过程时被加载的,由于我们可以通过重新加载共享对象来覆盖在原先共享对象中定义的变量,因此这也可以用于定义那些之前没有
定义的变量,例如这个例子中的__libc_wait。
关于这个问题合适的修补办法,应该是创建一个小的,包含有所需要的代码实现的共享对象,并添加到脚本中:
% cd $prefix/lib
% cat > libcwait.c << EOF
#include
#include
#include
#include
pid_t __libc_wait (int *status)
{
int res;
asm volatile (”pushl %%ebx/n/t”
“movl %2, %%ebx/n/t”
“movl %1, %%eax/n/t”
“int $0×80/n/t”
“popl %%ebx”
: “=a” (res)
: “i” (__NR_wait4), “0″ (WAIT_ANY), “c” (status), “d” (0),
“S” (0));
return res;
}
EOF
% gcc -fpic -O2 -shared libcwait.c -o libcwait.so
现在对刚才那个脚本进行对应的修改:
% cd $prefix/bin
% cat > zoo << EOF
#! /bin/bash
prefix=/opt/compat-env/zoo
LD_LIBRARY_PATH=”$prefix/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}”
export LD_LIBRARY_PATH
LD_PRELOAD=$prefix/lib/libcwait.so
export LD_PRELOAD
$prefix/lib/ld-linux.so.2 ${0}.exec “$@”
EOF
% chmod 755 zoo

结论
通过这篇文章所提出的建议,当遇到那些你的零售商并没有在特定版本的Linux中引进或者构建的程序时,你就可以自己创建一个兼容环境来执行它了。例如,
通过建立兼容环境,你可以很轻松得在你的Ubuntu操作系统中执行来自Red Hat商业版本Linux系统的程序了,反之亦然。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐