Linux软件发布时的glibc not found问题

发布于2024-08-05

在使用和发布Linux软件时,最痛苦的莫过于提示glibc错误。

比如

version GLIBC 2.xx not found

这是一个使用Linux软件/环境中很容易遇到的问题。

它代表的是,用户系统里的glibc版本,和软件需要的glibc版本不匹配(一半是用户系统比编译发布的系统旧)。

这篇文章会向您介绍,glibc是什么,发布Linux软件时怎么避免 GLIBC错误。

什么是glibc

glibc是常用的GUN发布的libc库。

glibc有一下几个特点:

  • 大部分应用软件会需要libc库与内核交互,加载so库,以及调用各种系统功能。
  • 大部分Linux系统会使用glibc作为标准的系统库。
  • glibc的版本和内核没有强匹配关系。
  • glibc库采用了LGPL,即修改需要GPL开源,静态链接需要GPL开源,有一定的传染性。

这几个特点会影响到我们实际使用glibc和避免glibc错误,划重点,要考.

Linux和glibc是什么关系

从理论上,linux和glibc没什么直接关系。

从现实来说,linux和glibc是长期合作相互成就的革命伙伴关系。

首先,我们先要确认linux是什么。一般语境下,linux有两个含义,linux内核及基于linux内核的操作系统,以及GNU/LINUX发行版。

对于linux内核来说,gnu不是必需品,比如目前占有率追高的Android设备,就是采用了linux内核,然后为了避免glibc的传染性,采用了bionic作为自己的libc库,一样占据了很高的占有率。

对于gnu来说,linux内核也不是必需品,gnu还有gnu/bsd和gnu/hurd的发行版,只是只有Linux系统比较成功。

所以,从某种角度来说,GNU比Linux更依赖于对方。某些情况下我们能绕开GNU/GLIBC,这也是重要的Linux软件发布策略。

事实上,GNU背后的自由软件基金会(FSF)和Linux对于开源协议的态度也不完全一致。

GNU 对于广泛使用的GPLV2协议觉得不够严格,还推出了更严格的GPLV3协议甚至AGPL协议

Linux作者的Linus本人坚守更宽松的GPLV2,并对商业化表示了一定的善意

作为作者,我们只能说,我们敬佩GNU的坚持,我们感恩Linus的慷慨。

我们为什么要解决Linux下的glibc问题

因为GNU/LINUX是一个十分成熟和重要的生态。Linux的生态本质上分为Android/服务器端/桌面端3块。后两者是GNU/LINUX的天下,除了极少数发行版,比如Docker容器中插件的Alpine,都是使用glibc的。

Linux软件开发,本质上是 为服务器端和桌面端的Linux发行版开发软件,所以无法避开事实标准glibc。

怎么解决glibc错误

解决glibc错误其实很简单,主要方法是避开glibc,打包glibc,锁定glibc。

具体实施时,有多种具体的方案,可以选择一个或多个策略。

不依赖任何libc

部分编程语言,在不使用到高级系统功能时,可以完全避开libc的时候。

典型就是golang,在golang的常见使用场景服务编程中,可以通过禁用CGO,用纯golang实现,使用golang自己的net库的方式,不依赖任何libc。

当然,这个方案有很大的局限性,只有在不依赖系统库的服务编程,不引入各种连接库的形式才能实行。一旦需要使用第三方库和各种系统高级功能,比如做GUI软件,则基本无法使用。

使用协议更轻量更宽松的libc

Linux有个轻量级,Mit协议的libc库musl

虽然在性能和功能上和glibc有一定的差距,但是它具有易于静态编译和更宽松的协议,所以很适合做为glibc的替代品。

当然,使用musl也会有一定的限制。

主要是同时加载glibc编译出的so库文件会有冲突,以及编译时必须有musl版的头文件。

静态编译

静态编译对于解决版本冲突可以说是黄金方案,只可惜,glibc的协议是LGPL。

所以,除非你是内部使用,不公开发布,或者源程序本身就是GPL协议,只要你公开发布了,都会被传染。

哪怕你只是为了规避glibc本身的版本冲突问题,而不是为了复用它的代码。但传染就是传染,不听你的解释。

容器发布

之前说过,内核和glibc并不需要匹配。

所以,Docker中共用内核,能部署不同版本的glibc的发布形式显得特别的好用。某种角度来说,Docker是一种另类的静态发布,只是不把程序静态编译,而是把整个运行环境都静态打包了。

当然,docker程序还是有各问题,就是和系统环境太过隔离,作为服务应用非常强打,对于用户系统日常使用的软件发布十分不便。

旧系统编译

这个是最传统,也是最无可奈何的选择了。

一般来说,需要公开发布的linux程序,最合适的方式就是找主够老旧的,还在支持的发行版进行打包(发文时为止,我们使用的打包发行版是Debian 10)

一般来说,我们可以通过虚拟机和Docker来实现这个需求。对,由于内核和glibc版本无关,Docker是非常适合打包的一个环境,写一个脚本 docker exec -ti /bin.bash,可以通过来挂载各种库和头文件,通过-u可以指定编译的用户。

而虚拟机可以用来验证打包成功后程序是否能正常运行。当然,特别复杂的环境,或者不愿意安装docker,使用虚拟机编译打包也是各很好的选择。

写在最后

写了这么长一篇文,很明显,作者是觉得割裂的glibc版本在很多时候带来了糟糕的体验。

希望Linux环境以后能更统一,但在以自由为主流的Linux环境,似乎这一切有点不切实际。

又或者,希望能有个标准的打包系统,足够轻,被大部分语言良好支持,打包出来的程序能被同一时代所有的还在维护期主流发行版支持,可能更有希望一点。