文章目录
这篇文章描述解决的方法,是针对这样的场景, 怎么使多个gcc/glib版本共存,在多个gcc/glib版本共存的情况,怎么样指定一个版本进行编译,怎么在一个与编译环境不同(gcc/glibc版本不同)的机器上运行服务
需求
服务原本的编译环境 Ubuntu + gcc 5.5.0(libstdc++) + libc.so.2.17
需要在目标编译环境 centos 6.8 + gcc 4.8(libstdc++) + libc.so.2.14
需要在环境 centos 6.8 + libc.so.2.14 运行
- 限制:
- 源码必须使用gcc 5.5.0编译,所以得在
目标编译环境
安装对应版本的gcc和glibc 目标编译环境
账号没有root
权限
还有一点要注意,目标编译环境
与原编译环境
上的连接器的版本也需要一致(或高于),连接器是隶属于 binutils
包的。
这个需求中囊括了这样三个需求:怎么使多个gcc/glib版本共存,在多个gcc/glib版本共存的情况,怎么样指定一个版本进行编译,怎么在一个与编译环境不同(gcc/glibc版本不同)的机器上运行服务
背景知识
什么是glibc,libc,glib
-
glibc是linux的GUN C函数库,是linux系统中最底层的API,几乎其它任何运行时库都依赖于glibc。glibc除了封装linux操作系统所提供的系统服务,它本身也提供了许多其它必要功能服务的实现,例如:动态加载模块libdl、实时扩展接口librt。对应的动态库的名字
libc.so
-
libc是Linux下的ANSI C函数库,被glibc包含
libc是Linux下原来的标准C库,也就是当初写hello world时包含的头文件#include < stdio.h> 定义的地方。
后来逐渐被glibc取代,也就是传说中的GNU C Library,在此之前除了有libc,还有klibc,uclibc。现在只要知道用的最多的是glibc就行了,主流的一些linux操作系统如 Debian, Ubuntu,Redhat等用的都是glibc(或者其变种,下面会说到).
- glib是Linux下C的一些工具库,和glibc没有关系
依赖关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zzFJvF6H-1629517049109)(https://note.youdao.com/yws/api/personal/file/WEB1525e2b8f59e6aba4d5212f778140ddf?method=download&shareKey=69bafa9f7495f1a0f9ff03ffe6242795)]
构建程序,只要链接的libstdc++,glibc(libc.so,libpthread,动态连接器等系列库)版本正确,就不会出问题。在低版本的os上,安装高版本的gcc,glibc,只要可以顺利编译通过,则意味这高版本库是支持低版本OS的。
方法
查看glibc版本
- 通过ldd
ldd --version
ldd
是隶属于glibc
,它的版本就是glibc
的版本
- 通过
getconf GNU_LIBC_VERSION
pthread
也是一个非常重要的库,它被包含在glibc中,可以通过
getconf GNU_LIBPTHREAD_VERSION
查看它的版本,是与glibc的版本是一致的
查找glibc库的位置
- 通过
ldd
linux 上几乎所有的程序都依赖于glibc
,所以可以直接通过 ldd 某个程序 | grep "libc.so"
的方式查看
- 通过gcc
gcc -print-file-name=libc.so
可以获得libc.so的位置,这也是该gcc依赖的libc.so的位置
查看glibc API的版本
strings /lib64/libc.so.6 | grep GLIBC
libstdc++
libstdc++
是 gcc的标准C++库(libc++
是clang的标准C++库)
查找libstdc++.so
的位置
-
/sbin/ldconfig -p | grep stdc++
-
locate libstdc++.so
查看libstdc++
的版本
libstdc++
是被包含在gcc中的,对应为gcc的版本
gcc --version
查看系统libstdc++
API的版本
strings /usr/lib/libstdc++.so.6 | grep LIBCXX
libstdc++,glibc的关系
libstdc++
与gcc是捆绑在一起的,也就是说安装gcc的时候会把libstdc++装上。 那为什么glibc和gcc没有捆绑在一起呢?因为程序可以不依赖libstdc++
,但是必须依赖glibc
确定程序需要的glibc/libstdc++
的版本
readelf -s qt_cef_poc | grep -oP "GLIBC_[\d\.]*" | sort | uniq
解决步骤
编译安装
必须安装如下程序:
- gcc(这里需求的是版本 5.5.0) 包含了 gcc/g++ ,libstdc++
- binutils(这里需求的是版本2.27) 包含了 链接器(ld)
- glibc(这里需求的是版本2.17) 包含了 libc.so , ld(动态链接器,用于在程序运行时链接动态库),libpthread
多个gcc/glibc版本共存
多个gcc/glibc共存,新安装的gcc/glibc版本需要指定安装目录。特别是glibc作为基础库,如果直接替换了系统中原有的glibc,很可能造成任何命令/程序都无法运行的情况。切记,一定要指定安装目录。
指定gcc/g++,glibc的版本进行编译
在编译时需要指定gcc及库的依赖路径,包括以下几点:
- 指定gcc/g++的版本
export CC=gcc的路径
export CXX=g++的路径
- 指定连接器的版本
将连接器的路径,放在LD_LIBRARY_PATH
的最前面
- 指定glibc的版本
-
通过gcc 的-L参数指定glibc库(libc.so)的路径
-
在gcc的编译参数中指定 -Wl,–dynamic-linker=glibc中动态链接器的路径,如下:
-Wl,--dynamic-linker=/动态连接器的路径/ld-linux-x86-64.so.2
-
在gcc中链接libc.so(-lc)
-
将
glibc
的路径,引入LD_LIBRARY_PATH
中
程序运行机器上的依赖
如果编译环境与运行环境不同,则需要将gcc,glibc的一些库打包到程序安装包中,并且指定库的路径
- 依赖的库
libstdc++.so
,libc.so
库及它们的依赖库,动态连接器都需要放入程序的依赖库的目录中,基本是包含如下几个库
librt.so.1
libdl.so.2
libpthread.so.0
libstdc++.so.6
libm.so.6
libc.so.6 -> glibc库
libgcc_s.so.1
libresolv.so.2
libcrypt.so.1
ld-linux-x86-64.so.2 ->其实是个执行程序,为动态连接器
- 指定依赖库的路径
在编译是通过gcc的编译参数-Wl,-rpath=程序的依赖库路径
这些库最好都放在指定的,固定的目录中,在编译时通过这个编译选项指定该路径
3.将动态连接器ld-linux-x86-64.so.2
的路径配置到PATH
中
总结
基本原理,就是千方百计的,将程序链接/运行时的依赖路径指向期望的版本,手段包括:
-Wl,-rpath=
编译参数-Wl,--dynamic-linker
编译参数- 设置
LD_LIBRARY_PATH
- 设置
PATH
吐个槽,C++的库管理太麻烦了。