version `GLIBC_2.29‘ not found 的原因和怎么解决问题

version `GLIBC_2.29‘ not found 的原因和怎么解决问题_第1张图片

程序上经常有在这台Linux上编译,然后放到另一个Linux上运行的情况。

如果Linux版本差别不大或都是ubuntu或centos系列还好。

如果不是一个系列很容易出现GLIBC 找不到的情况。

尤其是ubuntu上编译,然后放到centos系列。因为centos为了追求所谓的稳定,基本用的都是N年前的东西,生怕用新的东西把它给搞的不安全了。

ImportError: /lib64/libm.so.6: version `GLIBC_2.29' not found (required by /home/ma-user/_openjar.so)

问题的本质

这个问题的本质就是,编译所用的操作系统中GLIBC的版本高,但是运行的机子上的GLIBC版本低(glibc版本低于2.29),没有这个函数接口。

比如这里 pow函数,存在于libm.so中,

目标系统上libm.so中pow函数是 pow@@GLIBC_2.17 ,但所用的是pow

# readelf -s /usr/lib64/libm.so.6 |grep pow
    37: 000000000003a398  2240 FUNC    GLOBAL DEFAULT   14 __pow_finite@@GLIBC_2.17
   113: 000000000004a250   864 FUNC    WEAK   DEFAULT   14 powf32@@GLIBC_2.27
   117: 0000000000031dd0    48 FUNC    WEAK   DEFAULT   14 cpowf64x@@GLIBC_2.27
   194: 0000000000010bb8   320 FUNC    WEAK   DEFAULT   14 powf64@@GLIBC_2.27
   291: 000000000000ef38   680 FUNC    WEAK   DEFAULT   14 powf128@@GLIBC_2.27
   306: 000000000004a250   864 FUNC    GLOBAL DEFAULT   14 __powf_finite@@GLIBC_2.17
   336: 0000000000010bb8   320 FUNC    WEAK   DEFAULT   14 pow@@GLIBC_2.17

但程序所需的是 pow@@GLIBC_2.29, 所以就会运行的时候找不到GLIBC_2.29的版本。

[ma-user tdf]$nm _openbbf.so |grep pow
000000000074a180 u _ZZN8nlohmann12json_v3_11_16detail9dtoa_impl36get_cached_power_for_binary_exponentEiE13kCachedPowers
                 U pow@@GLIBC_2.29

所以应该怎么办呢? 通过ldd可以看到程序所依赖的.so库

/tmp> ldd myapp
        linux-vdso.so.1 =>  (0x00007fff7a1ff000)
        libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f1f8a765000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f1f8a4e3000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f1f8a2cc000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1f89f45000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f1f8aaa9000)
/tmp>

可以看到有libstdc++, libm, libgcc, and libc

其中libstdc++是gcc的c++ 动态库。

libc.so 和libm.so 都是GLIBC的一部分。

通过objdump可以看到 我们的程序myapp所调用的一些接口函数的版本号。

/tmp> objdump -T myapp | grep GLIBC_
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 ungetc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.3   __ctype_toupper_loc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 fputc
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 free
…
/tmp>

查看程序中所用函数的GLIBC、GLIBC++版本号

ppl@dell:~/python$ nm --dynamic --undefined-only --with-symbol-versions _openbbf.so \
  | grep GLIBC | sed -e 's#.\+@##' | sort --unique
GLIBC_2.14
GLIBC_2.15
GLIBC_2.16
GLIBC_2.17
GLIBC_2.2.5
GLIBC_2.25
GLIBC_2.28
GLIBC_2.29
GLIBC_2.3
GLIBC_2.3.2
GLIBC_2.3.4
GLIBC_2.4
GLIBC_2.7
GLIBC_2.8
GLIBC_2.9
GLIBCXX_3.4
GLIBCXX_3.4.11
GLIBCXX_3.4.14
GLIBCXX_3.4.15
GLIBCXX_3.4.18
GLIBCXX_3.4.19
GLIBCXX_3.4.20
GLIBCXX_3.4.21
GLIBCXX_3.4.26
GLIBCXX_3.4.9

第一点可以做的是把gcc的 C++库/C库 给静态链接了。防止这些链接有问题。

-static-libstdc++ 选项可以让g++静态链接c++库,就是 libstdc++.so

-static-libgcc  可以静态链接gcc的C库  就是libgcc_s.so

g++ -static-libstdc++  -static-libgcc

第二点是 static 生成静态二进制,但这可能存在问题

-static 选项会把所有的库静态链接,但是这个可能存在问题,比如不兼容。

还有一个问题是License的问题,有些GPL的代码如果静态链接了,你的程序是不是也要GPL。

虽然GLIBC对静态链接是有豁免license的。但是其他代码可说不准。

虽然License有时候也不是问题。(原因你懂的)

g++ -static

解决办法

一、最根本的办法

搞一个跟目标机一样的系统,进行编译。 

如果目标机不确定,你又希望最好的兼容性,就尽量用低版本的系统进行编译。比如有人用ubuntu-16.04进行编译,编译出来的在ubuntu20.04上肯定是能用的。

现在docker技术已经非常发达了,都不需要装虚拟机。

直接在docker 镜像中进行编译就可以了。

比如:kroggen/ubuntu-16.04-gcc 这个镜像。

升级gcc

但是这个时候又会有另外一个问题。 就是旧版本系统上的编译器有点旧。

比如centos 7.7上的默认编译是gcc -v4.8.2,太老掉牙了。

现在谁不是c++14  c++17特性用的飞起。 

随便一个都要gcc v7.0 v9.3甚至更高。

当然自己编译一个高版本的gcc是可以的。但centos上有epel已经提供了可以yum的高版本gcc,直接进行yum就可以了。

# install gcc 9 on centos 7
# https://gist.github.com/superzscy/ea619f881c92b8cdae8faaf782d0f031
yum install -y centos-release-scl
yum install -y devtoolset-9
scl enable devtoolset-9 bash

ubuntu上也有提供一些低版本系统上用的gcc 9

#!/usr/bin/env sh

sudo apt-get update -y && \
sudo apt-get upgrade -y && \
sudo apt-get dist-upgrade -y && \
sudo apt-get install build-essential software-properties-common -y && \
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
sudo apt-get update -y && \
sudo apt-get install gcc-9 g++-9 -y && \
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-9 && \
sudo update-alternatives --config gcc

# select gcc-9

常见的系统所用的glibc的版本.

Distribution glibc version
Debian 7 2.13
Debian 8 2.19
Debian 9 2.24
CentOS 6 2.12
CentOS 7 2.17
Ubuntu 14.04 2.19
Ubuntu 16.04 2.23
Ubuntu 18.04 2.27

Ubuntu 20.04                                                        2.31

查看系统的glibc的版本可以用以下命令:

ubuntu 上 apt search glibc

apt search glibc


abicheck/focal,focal 1.2-5ubuntu1 all
  binary compatibility checking tool

clisp/focal 1:2.49.20180218+really2.49.92-3build3 amd64
  GNU CLISP, a Common Lisp implementation

fakeroot-ng/focal 0.18-4build2 amd64
  Gives a fake root environment

glibc-doc/focal-updates,focal-updates 2.31-0ubuntu9.12 all
  GNU C 库:文档

glibc-doc-reference/focal,focal 2.30-1ubuntu1 all
  GNU C 库:文档

glibc-source/focal-updates,focal-updates 2.31-0ubuntu9.12 all
  GNU C 库:源代码

 欧拉系统:

yum info glibc-devel

[root@host-13 ~]# yum info glibc-devel
Last metadata expiration check: 2:18:51 ago on Fri 15 Dec 2023 08:56:55 AM CST.
Installed Packages
Name         : glibc-devel
Version      : 2.28
Release      : 49.oe1
Architecture : aarch64
Size         : 9.4 M
Source       : glibc-2.28-49.oe1.src.rpm
Repository   : @System
From repo    : anaconda
Summary      : The devel for glibc
URL          : http://www.gnu.org/software/glibc/
License      : LGPLv2+ and LGPLv2+ with exceptions and GPLv2+ and GPLv2+ with exceptions and BSD and Inner-Net and ISC and Public Domain and GFDL
Description  : The glibc-devel package contains the object files necessary for developing
             : programs which use the standard C libraries. Besides, it contains the
             : headers. Thus, it is necessory to install glibc-devel if you ned develop programs.

二、patchelf 直接干

用patchelf把版本的信息删掉,自动链接系统上的相关函数。

patchelf位于:

GitHub - NixOS/patchelf: A small utility to modify the dynamic linker and RPATH of ELF executables

用法如下: 

先进行查看,然后去掉函数携带的版本号。

$ nm --dynamic --undefined-only --with-symbol-versions MyLib.so \
  | grep GLIBC | sed -e 's#.\+@##' | sort --unique
GLIBC_2.17
GLIBC_2.29
$ nm --dynamic --undefined-only --with-symbol-versions MyLib.so | grep GLIBC_2.29
                 U exp@GLIBC_2.29
                 U log@GLIBC_2.29
                 U log2@GLIBC_2.29
                 U pow@GLIBC_2.29
$ patchelf --clear-symbol-version exp   \
           --clear-symbol-version log   \
           --clear-symbol-version log2  \
           --clear-symbol-version pow   MyLib.so

但这种太猛了,不一定起作用。需要谨慎。

三、指定glibc的版本

还有一个办法,就是gcc提供了一种指定库函数版本号的方法。

__asm__(".symver SYM,SYM@GLIBC_VERSION");

详情参见这个项目:

GitHub - wheybags/glibc_version_header: Build portable Linux binaries without using an ancient distroBuild portable Linux binaries without using an ancient distro - GitHub - wheybags/glibc_version_header: Build portable Linux binaries without using an ancient distroicon-default.png?t=N7T8https://github.com/wheybags/glibc_version_header

这个项目有点老了,有些函数不一定行,但是思路是没有问题的。

像这样,在程序代码中指定链接的GLIBC的版本号。

#include 

__asm__(".symver pow,pow@GLIBC_2.17");
int fun(){
    return std::pow(2, 3);
}

参考:

linux - How can I link to a specific glibc version? - Stack Overflow

Florian Weimer - Re: how to compile a lower gcc/glibc version compatible binary?

 https://insanecoding.blogspot.com/2012/07/creating-portable-linux-binaries.html

你可能感兴趣的:(linux,glibc,linux)