多个gcc/glibc版本的共存及指定gcc版本的编译

文章目录

    • 需求
    • 背景知识
      • 什么是glibc,libc,glib
      • 依赖关系
      • 方法
        • 查看glibc版本
          • 查找glibc库的位置
          • 查看glibc API的版本
        • libstdc++
          • 查找```libstdc++.so```的位置
          • 查看```libstdc++```的版本
          • 查看系统```libstdc++```API的版本
        • libstdc++,glibc的关系
        • 确定程序需要的```glibc/libstdc++```的版本
    • 解决步骤
      • 编译安装
      • 多个gcc/glibc版本共存
      • 指定gcc/g++,glibc的版本进行编译
      • 程序运行机器上的依赖
      • 总结

这篇文章描述解决的方法,是针对这样的场景, 怎么使多个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 运行

  • 限制:
  1. 源码必须使用gcc 5.5.0编译,所以得在目标编译环境安装对应版本的gcc和glibc
  2. 目标编译环境账号没有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及库的依赖路径,包括以下几点:

  1. 指定gcc/g++的版本
export CC=gcc的路径
export CXX=g++的路径
  1. 指定连接器的版本

将连接器的路径,放在LD_LIBRARY_PATH的最前面

  1. 指定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的一些库打包到程序安装包中,并且指定库的路径

  1. 依赖的库
    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 ->其实是个执行程序,为动态连接器
  1. 指定依赖库的路径

在编译是通过gcc的编译参数-Wl,-rpath=程序的依赖库路径

这些库最好都放在指定的,固定的目录中,在编译时通过这个编译选项指定该路径

3.将动态连接器ld-linux-x86-64.so.2的路径配置到PATH

总结

基本原理,就是千方百计的,将程序链接/运行时的依赖路径指向期望的版本,手段包括:

  • -Wl,-rpath=编译参数
  • -Wl,--dynamic-linker编译参数
  • 设置LD_LIBRARY_PATH
  • 设置PATH

吐个槽,C++的库管理太麻烦了。

你可能感兴趣的:(C++编译系统,c++,编译器)