本文不考虑静态链接方式,很多库在静态链接的时候会有问题,比如 libunwind ,它的异常处理 API 会和 gcc 原有的冲突。还有一个显著的问题就是 nss 。它根据配置文件 /etc/nsswitch.conf 来动态决定加载哪个 so ,然后用这个 so 执行名称解析服务等等。( nss glibc 的一部分,是系统很基本的东西)。还有, jni so ,想要静态链接很难。意思就是说,我要编译一个 so ,但是这个 so 所依赖的其它库又都必须是静态链接的,很难,而且也许会引入很多 BUG 。出于种种原因,我完全放弃了静态链接。(程序采用静态链接完美世界的传统)
即便你的程序简单到只是一个 hello world, 那么也需要链接到 libc.so 。很明显,不同的 glibc 版本之间,差别很大,经常不兼容。那么我能不能在低版本的 Linux 上使用高版本的 Linux libc.so 呢?
于是我做了一个测试,我这边主要有两种 Linux 系统: CentOS 5 CentOS 6
CentOS 5 ld-linux-x86-64.so.2 指向的是 ld-2.5.so
CentOS 6 ld-linux-x86-64.so.2 指向的是 ld-2.12.so
如果强行把 CentOS 5 的这个 so 替换成 CentOS6 的那个,那么会发现任何 elf 都执行不了,
relocation error: /lib64/libc.so.6: symbol _dl_tls_get_addr_soft, version GLIBC_PRIVATE not defined in file ld-linux-x86-64.so.2
系统基本是僵死状态。(还好我今天用 /lib64/ld-2.5.so ln  -s -f ld-2.5.so ld-linux-x86-64.so.2 的方式救回来了)
如果我们不替换 ld.so, 而只是替换 libc.so ,例如 :
LD_LIBRARY_PATH=/home/changming/apps/lib64 ls   (/home/changming/apps/lib64 放的是 CentOS6 libc.so
那么会报告: error while loading shared libraries: /home/changming/apps/lib64/libc.so.6: ELF file OS ABI invalid
file 查看一下:
CentOS 6 libc.so.6:            ELF 64-bit LSB shared object, AMD x86-64, version 1 (GNU/Linux), for GNU/Linux 2.6.18, not stripped
CentOS 6 libc.so.6:            ELF 64-bit LSB shared object, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.6.9, not stripped
CentOS 6 libstdc++.so.6:       ELF 64-bit LSB shared object, AMD x86-64, version 1 (SYSV), stripped
这个差别在于 elftype
于是我从 FreeBSD 9 下面,把 brandelf.c 复制到 Linux ,稍作修改后编译。然后用它更改 elftype
./brandelf -t SVR4 /home/changming/apps/lib64/libc.so.6 apps/lib64/libm.so.6
然后在 CentOS 5 下面用 CentOS 6 ld.so 执行 CentOS 6 bash
LD_LIBRARY_PATH=/home/changming/apps/lib64 /home/changming/apps/lib64/ld-linux-x86-64.so.2 ./bash
error while loading shared libraries: /home/changming/apps/lib64/libc.so.6: unexpected reloc type 0×25
这个问题在于, bash fork 新进程,而新进程采用哪个 ld.so ,是我无法控制的。
综上所述:用老的 ld.so 配合新的 libc.so ,只有两种结果: "ELF file OS ABI invalid” “unexpected reloc type 0×25”
结论: ld.so 的版本必须和 glibc 的版本匹配。
但是,大多数情况下,这不是一个问题。因为大部分程序(我写的)是不会 fork 的。
我想说一个什么事情呢? 我想说, Linux 的这套动态链接库命名机制( soname linker name realname )并未能解决 DLL hell 的问题。参见: http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
我现在写好一个程序,当把它扔到别的系统上运行时,它对 OS 的依赖应当越小越好,否则难道我为每个发行版的每个主版本都编译一次?虽然大部分开源项目都是这么做的,但是我实在是不想。我希望只编译一次,并且编译时所采用的 so 和运行时采用的 so 是完全一样的!综上所述,如果不 fork ,那么 you can
如果我就是非要 fork ,那么怎么办呢?
答:对于无源代码的程序,改 ELF 文件的 Program Headers 。将 PT_INTERP 的值设置为我自己的 ld.so 。(原来的默认值是 /lib64/ld-linux-x86-64.so.2 )。改完之后可以用 readelf 这个命令检查一下。对于自己有源代码的程序,可以重新编译,加上 –dynamic-linker ./ld-linux-x86-64.so.2 这样的参数。注意,对于 setuid 程序,这里一定要写绝对路径,否则就是一个安全漏洞哇。
在解决这些问题之后,只要我的程序没有用到 Linux  Kernel 的新特性,那么就可以在相当大范围内的 Linux 上,自由执行了。
show 几个脚本:
打包 so ,并扔到 http server 上:
#!/bin/bash 
rm -rf /tmp/lib64tar 
mkdir /tmp/lib64tar 
cp /lib64/ld-linux-x86-64.so.2 /tmp/lib64tar 
cp /lib64/libc.so.6 /tmp/lib64tar 
cp /lib64/libdl.so.2 /tmp/lib64tar 
cp /lib64/libgcc_s.so.1 /tmp/lib64tar 
cp /lib64/libm.so.6 /tmp/lib64tar 
cp /lib64/libpthread.so.0 /tmp/lib64tar 
cp /usr/lib64/libstdc++.so.6 /tmp/lib64tar 
tar -zcvf /tmp/lib64tar.tar.gz -C /tmp/ lib64tar 
scp /tmp/lib64tar.tar.gz 10.4.1.27:/home/changming/public_html/glu/
自动安装脚本 glu script ):
class LinuxLib64{ 
      def install = { 
          log.info "Installing…" 
          def skeleton =  shell.fetch(params.linuxlib64url) 
          def distribution = shell.untar(skeleton) 
          shell.rmdirs(mountPoint) 
          shell.mv(shell.ls(distribution)[0], mountPoint) 
          shell.toResource(mountPoint.path).list().each{ f -> 
                log.info f.path 
                shell.chmodPlusX(f) 
          } 
          log.info "Install complete." 
      }
      def createChild = { args -> 
        return args.script 
      } 
}
glu static model:

    "agent": "10.4.1.14", 
    "mountPoint": "/lib64", 
    "initParameters": { 
        "linuxlib64url": "http://10.4.1.27/~changming/glu/lib64tar.tar.gz",        
    }, 
    "entryState": "installed", 
    "parent": "/", 
    "metadata": {}, 
    "tags": [], 
    "script": "http://10.4.1.27/~changming/glu/linuxlib64.groovy"    
  }
今天试了一下,从 icu 的网站下载为 RHEL6 编译的二进制包(一个 tar 包),然后在 CentOS 5 上解压到任意目录,这么执行:  
“ /home/changming/apps/lib64/ld-linux-x86-64.so.2  –library-path ../lib:/home/changming/apps/lib64  ./uconv  –list”
All Things Works Fine