本文不考虑静态链接方式,很多库在静态链接的时候会有问题,比如libunwind,它的异常处理API会和gcc原有的冲突。还有一个显著的问题就是nss。它根据配置文件/etc/nsswitch.conf来动态决定加载哪个so,然后用这个so执行名称解析服务等等。(nssglibc的一部分,是系统很基本的东西)。还有,jniso,想要静态链接很难。意思就是说,我要编译一个so,但是这个so所依赖的其它库又都必须是静态链接的,很难,而且也许会引入很多BUG。出于种种原因,我完全放弃了静态链接。(程序采用静态链接完美世界的传统)
即便你的程序简单到只是一个hello world,那么也需要链接到libc.so。很明显,不同的glibc版本之间,差别很大,经常不兼容。那么我能不能在低版本的Linux上使用高版本的Linuxlibc.so呢?
于是我做了一个测试,我这边主要有两种Linux系统:CentOS 5CentOS 6
CentOS 5ld-linux-x86-64.so.2指向的是ld-2.5.so
CentOS 6ld-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放的是CentOS6libc.so
那么会报告:error while loading shared libraries: /home/changming/apps/lib64/libc.so.6: ELF file OS ABI invalid
file查看一下:
CentOS 6libc.so.6:            ELF 64-bit LSB shared object, AMD x86-64, version 1 (GNU/Linux), for GNU/Linux 2.6.18, not stripped
CentOS 6libc.so.6:            ELF 64-bit LSB shared object, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.6.9, not stripped
CentOS 6libstdc++.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 6ld.so执行CentOS 6bash
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
这个问题在于,bashfork新进程,而新进程采用哪个ld.so,是我无法控制的。
综上所述:用老的ld.so配合新的libc.so,只有两种结果:"ELF file OS ABI invalid” “unexpected reloc type 0×25”
结论:ld.so的版本必须和glibc的版本匹配。
但是,大多数情况下,这不是一个问题。因为大部分程序(我写的)是不会fork的。
我想说一个什么事情呢? 我想说,Linux的这套动态链接库命名机制(sonamelinker namerealname)并未能解决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