在ubuntu22上编译的程序无法拿到centos5上去运行,想必大家都知道是因为gcc版本的原因。追究其本质,是因为编译出来的二进制的符号,与目标机器上libc.so里面符号的版本不同的原因。甚至连最简单的main函数__libc_start_main,也有2.2.5和2.34两个版本,所以gcc11编译出来的二进制拿到centos5上一运行,就会报

/lib64/libc.so.6: version `GLIBC_2.34' not found (required by ./main)

先看下网上一些人给出的方法(不靠谱的):

1、直接拷贝动态库,这个用脑子想想都知道是有问题的,libc.so里面的函数是在用户态下对内核调用的封装,版本跨度稍微大一点就对不齐,肯定跑不起来

2、编译成静态二进制,编译时指定-static,经测试无效,在源码中调用某些系统函数后,仍然有libc.so.6的链接

3、使用musl-gcc的静态编译二进制,这个只要代码能编译出来,就基本能拿到各个系统下去运行,但源码中应该不能包含系统调用,缺少<linux/version.h> <asm/signal.h> 之类的内核相关的头文件,猜测需要把buildroot放在一起编译才行

4、安装低版本gcc,这个办法太过愚笨和麻烦了,且不具有代表性,另外gcc11是没法编译gcc4.1的,要先安装gcc6以下的,才能去编译gcc4.1

以下是两种方案是合理、可行的

5、在源码中添加函数的实现,并在链接时通过wrap覆盖,指向自己的函数。个人认为这种方式可靠性是最高的,也是真正意义上的兼容,可以自行实现或替换版本,缺点就是非常麻烦,还有需要源码才能编译

https://blog.mewwoof.cn/tech/cs-os/1716/

6、直接修改elf中的版本信息,这个与上面方法互补,不需要源码,简单粗暴。网上有很多手动修改的方法,例如:

https://zohead.com/archives/mod-elf-glibc/

看一下原理,肯定有自动化的办法,找到了lief这个库。适用于不需要自行实现的情况,只是替换glibc的版本信息,调用这个sdk,可以快速实现替换。顺便说一句,替换了如果没有glibc没有符号,程序也是无法运行的,需要配合patchelf,把自己写的函数实现的so添加进来

https://lief-project.github.io/

#!/usr/bin/env python3
# coding=utf-8
 
import lief
 
 
if __name__ == "__main__":
 
    bi = lief.parse('main')
    print(bi)
 
    # 经测试,不改符号表里的版本信息也是可以运行的,可能是只在加载elf的时候判断libc.so的版本信息
    # for _ in bi.symbols:
    #     if _.symbol_version:
    #         if 'start_main' in _.name:
    #             # 修改高版本方法符号表对应的版本,
    #            _.symbol_version.value =  2
 
    glibc_2_2_5 = None
 
    # 查看 .gnu.version_r 信息
    for _ in bi.symbols_version_requirement:
        print(_.name, _.version)
        for _s in _.get_auxiliary_symbols():
            print('\t', _s)
            print('\t', _s.flags, _s.hash, _s.name, _s.other)
            if _s.name == 'GLIBC_2.2.5':
                glibc_2_2_5 = _s
				break
 
    for _ in bi.symbols_version_requirement:
        if 'libc.so.6' in _.name or "libm.so.6" in _.name:
            for _v in _.get_auxiliary_symbols():
                if "GLIBC_2.34" == _v.name:
                    _v.flags = glibc_2_2_5.flags
                    _v.hash = glibc_2_2_5.hash
                    _v.name = glibc_2_2_5.name
                    _v.other = glibc_2_2_5.other
 
    bi.write('main_new')

再回过头来总结一下,兼容所解决的问题,是具有局限性的。我们不能在低版本的系统上去运行只有高版本实现的功能,只能运行两者有相同签名实现且结果是相同的,才能做版本替换,否则是需要自行去实现功能的,无论是静态还是这些方法,都不能去绕开这个最基本的问题。

Logo

更多推荐