浅谈如何绕过glibc的版本不兼容限制,制造c版本的"python"

@Ta 06-07 09:24 338点击
"It is require glibc 28.0, but your linux only has glibc 8.0... "
通常遇到这种情况,就是上面要你装最新版的linux了,最新版又大又不好用,限制又多,为什么要升级?



最近我在写自己的python,用c语言。

感觉没有那么难,就是需要仔细思考。




我突然领悟到一个事儿,python代码可以被实时转为c action,那么c代码也可以被实时转为c action.

你甚至可以写一个远控c语言。像执行python一样,执行c代码。这样你就可以绕过垃圾glibc的版本不兼容限制,只需要编译一次c语言解释器,然后一直用那个二进制解释器执行新的c代码。

举个例子,

```
int i = 999;
i = i + 1;
```

在c解释器里面,可能会被转为:

```
int *i = (int *)malloc(sizeof(int)); 
*i = 999;
*i = *i + 1; 
```

这相当于实时创建变量,实时执行c代码,不需要编译。

通过这样做可以实现一个带指针内存操作的python,但不是用的python代码,而是c代码。
回复列表(16|隐藏机器人聊天)
  • @Ta / 06-07 14:25 / /

    @yingshaoxo,像 Go 一样,直接用 Linux syscall,就不用管 glibc 啥版本了吧。。

  • @Ta / 06-08 12:16 / /
    @无名啊

    你没写过c程序,你不知道。就是因为那个glibc,导致旧电脑和系统用不了别人最近编译的东西

    这是在强制普通用户升级新系统

    而那些开发者还不知道,他们发布的c程序,只有和他们同版本的linux系统才能运行。

    比如ubuntu20编译出来的,ubuntu19就用不了,但实际上只差了一年。
  • @Ta / 06-08 13:04 / /

    @yingshaoxo,你都自创语言了,何必还要依赖 glibc 呢。。

    像 Go 那样,直接用更底层的 Linux syscall 不就好了吗。。

  • @Ta / 06-08 16:13 / /

    帖子质量太低了,移到超级灌水

  • @Ta / 06-08 19:29 / /
    @老虎会游泳,我看了一下你原来的软件开发分区,里面帖子的内容,基本上2年就会过期。但是我发的内容10年都不会过期。

    你想想,我突然发现所有编译语言都可以变成类似于python或者php的远控且不需要再次编译,这还不够震撼吗?

    市面上可没有现成的思路哦!你有见过有人实时运行解析c语言并运行c语言的吗?

    难不成要我补充一个从0用c写的python的代码项目,这篇帖子才能看起来有价值?

    算法、思路本身没有价值吗?有时候一句话可以点醒一个十几年经验的软件工程师,让他觉得自己做了十几年无用功…

    举个例子,写tinycc的那个作者,因为linux的gcc一直不稳定,所以只选择了开发xp的二进制编译器,我要是给他讲,原来他可以不编译c,直接执行c,那他这十几年没准都写出了完整版的c_interpreter
  • @Ta / 06-08 21:31 / /

    @yingshaoxo,你是想,任意一台电脑,免编译运行 C/C++ 源码吗?

    就像能直接跑下列脚本一样?

    • python 运行 .py
    • php-cli 运行 .php
    • lua / luajit 运行 .lua
    • nodejs / quickjs 运行 .js
    • ……

    另外,各个 Linux 发行版都能安装 TinyCC 并直接运行 C 源码吧。。

    比如,在我 4 年前安卓上,Termux 里就能直接安装 TinyCC,

    并跑我在林子里 3 年前测试的《Shell 脚本里直接跑嵌入的 C 代码》

    Screenshot_2025-06-08-21-14-12-654_com.termux.webp(58.33 KB)

  • @Ta / 06-09 16:48 / /

    @yingshaoxo,Linux内核创始人林纳斯·托瓦兹(Linus Torvalds)曾说:“Talk is cheap. Show me the code.”(空谈无用,给我看代码。)
    我对虎绿林编程区发帖也采取相同的态度。
    所以,等你把功能写好了再给我们看。如果没写好,或者只有一些伪代码,没有任何可以运行的东西,那不好意思,只能移到超级灌水。

  • @Ta / 06-09 17:04 / /
    @无名啊,对,就像直接跑代码
  • @Ta / 06-09 17:05 / /
    @老虎会游泳,没事,先移进灌水区吧。我只是想表明具体的代码失效得快。
  • @Ta / 06-09 17:35 / /
    @无名啊,不联网的情况下装不上tinycc,更装不上gcc。目前只有xp在离线情况下,能用tinycc。(恕我直言,我们离科技化的1984很接近了)
  • @Ta / 06-09 18:52 / /

    @yingshaoxo,那你自己写的类 python 解释器,打算如何解决这个《能在各个 Linux 上离线使用》问题呢?

    • 用 musl libc 静态编译,直接用 Linux syscall,不依赖 glibc?
    • 低版本 Ubuntu 编译出二进制,指望 glibc 能完美向下兼容?
    • 打包所有依赖库,并修改 ELF 中的 rpath 库目录、interpreter 库加载器?
    • 还是……?
  • @Ta / 06-09 23:01 / /

    这个问题我还真不知道,帮你问问Gemini 2.5 pro

    好的,这是一个非常经典且常见的Linux系统运维和开发问题。当你在一个低版本的Linux系统上运行一个在高版本系统上编译的程序时,经常会遇到 GLIBC_2.XX not found 的错误。

    这是因为程序在编译时,动态链接到了新版本的Glibc(GNU C Library)。Glibc为了向后兼容,使用了符号版本(Symbol Versioning)机制。你的程序需要一个新版本Glibc才提供的函数符号,而系统里的旧Glibc没有,所以无法运行。

    在开始之前,必须强调:

    警告:直接替换或升级系统底层的Glibc是一个极其危险的操作,99%的情况下会导致系统崩溃且无法启动。除非你完全明白你在做什么,否则绝对不要尝试全局替换 libc.so.6

    下面我将从最推荐到最不推荐的顺序列出几种绕过或解决Glibc版本不兼容问题的方法。


    方法一:最安全、最推荐的方法

    1. 升级你的操作系统

    这是最根本、最稳定、最安全的解决方案。如果条件允许,将你的操作系统(例如 CentOS 6/7 升级到 CentOS 8/Stream 或更高版本,Ubuntu 16.04 升级到 20.04/22.04)升级到一个支持所需Glibc版本的发行版。

    • 优点:一劳永逸,系统环境统一,安全补丁和功能都是最新的。
    • 缺点:在生产环境中可能难以实施,需要停机和大量测试。

    2. 使用容器技术(Docker, Podman)

    这是现代化的标准解决方案,也是我个人最推荐的“绕过”方法。它将应用及其所有依赖(包括需要的Glibc)打包到一个隔离的环境中运行,不影响宿主机系统。

    • 操作步骤

      1. 在你的旧系统上安装Docker或Podman。
      2. 创建一个 Dockerfile,选择一个包含新版Glibc的基础镜像(如 ubuntu:22.04debian:bullseye)。
      3. Dockerfile 中,将你的应用程序复制到镜像里。
      4. 构建并运行这个容器。
    • 示例 Dockerfile

      # 使用一个包含新版 Glibc 的基础镜像
      FROM ubuntu:22.04
      
      # 将你的二进制文件复制到容器中
      COPY ./your_program /usr/local/bin/your_program
      
      # 赋予执行权限
      RUN chmod +x /usr/local/bin/your_program
      
      # 设置容器启动时执行的命令
      CMD ["your_program"]
      
    • 优点

      • 完美隔离:完全不影响宿主机系统,零风险。
      • 环境一致性:确保开发、测试和生产环境完全一致。
      • 易于分发:打包成镜像后可以轻松部署到任何支持容器的机器上。
    • 缺点

      • 需要学习容器的基本知识。
      • 有一定的性能开销(尽管通常很小)。

    3. 在目标机器上重新编译

    如果你有应用程序的源代码,这是最直接的解决方法。

    • 操作步骤

      1. 在你的旧版本Linux系统上安装好编译工具链(gcc, g++, make 等)。
      2. 将源代码复制到这台机器上。
      3. 执行编译流程(./configure && make && make install)。
    • 优点:编译出的二进制文件会链接到系统自带的旧版Glibc,完美兼容。

    • 缺点

      • 必须拥有源代码。
      • 目标机器上可能缺少各种开发依赖库,安装它们可能很麻烦。

    方法二:高级“Hacker”方法(有风险,但可行)

    当你无法升级系统、无法使用容器、也没有源代码时,可以尝试以下方法。这些方法的核心思想是:不替换系统Glibc,而是让你的程序使用一个你单独提供的、新版本的Glibc。

    4. 使用 patchelf 工具修改程序的动态链接器和Rpath

    这是最常见的“黑科技”手段。patchelf 是一个可以修改ELF可执行文件动态链接信息的小工具。

    核心原理:

    1. 修改解释器(Interpreter):告诉操作系统,运行这个程序时,不要使用系统的加载器(如 /lib64/ld-linux-x86-64.so.2),而是使用我们指定的新版加载器。
    2. 修改运行时搜索路径(Rpath):告诉程序,运行时去我们指定的目录里寻找 .so 动态库文件,而不是只在系统默认路径(如 /lib64, /usr/lib64)里找。

    操作步骤:

    1. 准备新版Glibc文件

      • 从一个新版系统(或相应的 rpm/deb 包)中提取出Glibc相关文件。你需要 libc.so.6, ld-linux-x86-64.so.2 以及其他可能依赖的库(如 libpthread.so.0, libdl.so.2 等)。

      • 例如,在Debian/Ubuntu系统上,你可以下载libc6包并解压:

        # 找一个新版的 libc6 deb 包,比如从 packages.debian.org
        apt-get download libc6
        dpkg -x libc6_*.deb ./new_glibc_files
        
      • 将解压出来的 libetc 目录放到一个地方,例如 /opt/myapp/glibc/

      • 最终你的目录结构可能像这样:

        /opt/myapp/
        ├── your_program
        └── glibc/
            └── lib/
                └── x86_64-linux-gnu/
                    ├── ld-2.31.so  # 这是 ld-linux-x86-64.so.2 的实际文件
                    ├── libc-2.31.so # 这是 libc.so.6 的实际文件
                    ├── libc.so.6 -> libc-2.31.so (符号链接)
                    └── ... (其他 so 文件)
        
      • 找到新版加载器的确切路径,例如是 /opt/myapp/glibc/lib/x86_64-linux-gnu/ld-2.31.so

      • 找到新版库文件的目录,例如是 /opt/myapp/glibc/lib/x86_64-linux-gnu

    2. 安装 patchelf

      # บน CentOS/RHEL
      sudo yum install patchelf
      # บน Debian/Ubuntu
      sudo apt-get install patchelf
      
    3. 修改你的程序

      # 1. 修改 Interpreter
      patchelf --set-interpreter /opt/myapp/glibc/lib/x86_64-linux-gnu/ld-2.31.so your_program
      
      # 2. 修改 Rpath
      patchelf --set-rpath /opt/myapp/glibc/lib/x86_64-linux-gnu your_program
      

      注意ld-2.31.so 和路径要换成你自己的实际路径。

    4. 运行测试
      现在,直接运行 ./your_program,它应该就会使用你指定的Glibc版本了。

    • 优点:精确控制,只影响目标程序,不影响系统。
    • 缺点
      • 操作复杂,容易出错。
      • 你需要手动管理所有相关的Glibc库文件,如果依赖链很长,可能会很麻烦。
      • Glibc的一些功能(如NSS)可能因为环境变化而出现意想不到的问题。

    5. 使用 chrootproot

    chroot 是一个传统的工具,可以创建一个隔离的文件系统根目录。你可以在这个“伪根”里安装一套新版Linux系统的基本文件(包括Glibc),然后在其中运行你的程序。

    • 优点:比 patchelf 隔离性更好。
    • 缺点:设置比 patchelf 更复杂,通常需要root权限。proot 是一个用户态的 chroot 实现,不需要root,但性能更低。

    方法三:不推荐,但理论上可行

    6. 编译时静态链接

    如果在编译时,你就预见到了这个问题,可以尝试将程序静态链接。

    gcc -static my_program.c -o my_program
    
    • 优点:生成一个不依赖外部动态库的独立可执行文件,可移植性极高。
    • 缺点
      • Glibc官方不推荐:完全静态链接Glibc可能会导致DNS解析、用户/组查询等NSS(Name Service Switch)功能失效或行为异常。
      • 文件巨大:所有库都被打包进了可执行文件。
      • 安全更新困难:库有安全漏洞时,必须重新编译整个程序。

    总结与决策树

    方法 难度 风险 推荐度 适用场景
    升级操作系统 ★★★★★ 条件允许的任何情况,尤其是生产环境。
    使用容器 (Docker) 极低 ★★★★★ 现代应用部署的标准实践,强烈推荐。
    在目标机重新编译 ★★★★☆ 有源代码,且目标机可以安装编译环境。
    使用 patchelf ★★★☆☆ 没有源码、不能用容器时的“最后一招”。
    使用 chroot ★★☆☆☆ 需要比patchelf更强隔离性,且不介意复杂度的场景。
    静态链接 低(编译时) ★☆☆☆☆ 仅适用于非常简单、不涉及网络或用户查询的小工具。

    决策建议

    1. 首选方案:优先考虑 使用Docker容器。这是解决此类依赖问题的最佳实践。
    2. 次选方案:如果不能用容器,但有源代码,选择 在目标机重新编译
    3. 最后手段:如果以上都不行,再谨慎尝试 patchelf。动手前一定要备份好你的程序,并在测试环境中充分验证。

    避免直接操作系统的 /lib/lib64 目录,这是通往系统崩溃的捷径。

  • @Ta / 06-10 07:19 / /
    @无名啊

    1. 我魔改了一个alpine i32 system,自带musl-gcc,自带永不升级的static compiled python
    2. 然而没有毛用,要是我找不到能用的硬件,就安装不上那个系统
    3. 这就是为什么我在hu60还发了另一个帖子,一个关于如何制造实体计算机的帖子
  • @Ta / 06-10 09:49 / /

    @yingshaoxo,打包成docker镜像呢
    一加ace2Pro(灰|24+1024)

  • @Ta / 06-10 10:45 / /
    @上善若水

    docker也不靠谱,举个例子,linux amd64还分成 v1,v2,v3,新版本的docker打包出来是v3,老docker用不了。同时最新版docker也用不了7年前打包的v1镜像。docker兼容性也差得太离谱了,新的竟然不兼容旧的。

    另外它后台功耗大,且不支持离线安装

    具体有多难用,你可以看:

    https://github.com/xxnuo/MTranServer/issues/32#issuecomment-2926206601

    我其实是docker的老用户了,但我现在后悔了,有那时间还不如学习下如何魔改virtualbox (modifying, compile and so on)
  • @Ta / 06-10 11:31 / /

    @yingshaoxo,还要其他电脑,再安装一个完整 Linux,才能用你的类 Python 解释器吗。。

    11 楼那三个方案可行性如何?

添加新回复
回复需要登录