makefile

工具参考

跟我一起写Makefile | Make 命令教程 | CMake官方教程

Linux升级安装GCC G++ 6.2 | 详解三大编译器:gcc、llvm 和 clang | libiconv库链接问题一则

C++的标准库

glibc下载链接:
https://sourceware.org/git/?p=glibc.git

wget http://ftp.gnu.org/gnu/libc/glibc-2.31.tar.gz
tar -zxvf glibc-2.31.tar.gz
cd glibc-2.31
./configure -prefix=/opt/glibc-2.31
make && make install

编译Nginx和ATS应用时指定glibc版本使用已安装的2.31版本:
CFLAGS="-I/opt/glibc-2.31/include"  \
LDFLAGS="-L/opt/glibc-2.31/lib/     \
         -Wl,-rpath=/opt/glibc-2.31/lib/ \
         -Wl,--dynamic-linker=/opt/glibc-2.31/lib/ld-2.31.so"

---------------------------------------------------------
The GNU C Library(glibc) project provides the core libraries for the GNU system and GNU/Linux systems, 
as well as many other systems that use Linux as the kernel. 
These libraries provide critical APIs including ISO C11, POSIX.1-2008, BSD, OS-specific APIs and more. 
These APIs include such foundational facilities 
as open, read, write, malloc, printf, getaddrinfo, dlopen, pthread_create, crypt, login, exit and more.


libc是LINUX下的标准C库,后来被glibc(GNU C LIBRARY)替代
glibc是LINUX最低层的API,为上层函数提供基础的功能,如string,malloc,stdlib,linuxthread,locale,signal等
eglibc是embedded glibc, 是为了更好支持嵌入式架构的, 是二进制兼容glibc, 可以替换glibc(如ubuntu)
glib是一个基础库,对数据进行统一的封装,和glibc没有关联(GTK和GNOME调用了glib库)
libc++是clang的C++标准库
libstdc++是gcc的C++标准库, libstdC++经glibc与内核交互通信

linux-vdso.so1: 是一个虚拟文件,是内核和glibc之间的中间协调层
ld动态链接器(/lib64/ld-linux-x86-64.so.2, 可以看出操作系统和CPU类型)
libm:   Math routines
librt:  异步IO routines
libpthread: 线程routines
----------------------------------------------------------

查看glibc版本
ldd --version

安装make
wget http://ftp.gnu.org/gnu/make/make-4.3.tar.gz
cd make-4.3
./configure
make
make install
ln -s -f /usr/local/bin/make /usr/bin/make

        
安装glibc
wget http://ftp.gnu.org/gnu/glibc/glibc-2.18.tar.gz
tar zxvf glibc-2.18.tar.gz
cd glibc-2.18
mkdir build
cd build
../configure --prefix=/opt/glibc-2.18
make -j4
sudo make install
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/glibc-2.18/lib

-------------------------------------------------------------------
查找iconv相关符号表
nm /lib64/libstdc++.so | grep iconv

查找依赖库
ldd test_cond

verbose查看详细编译信息, 分析库文件查找过程
gcc -g -o test test.c -liconv -v

查看环境变量
env  #显示所有环境变量
env | grep LD_LIBRARY_PATH
echo $HOME

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib64
readelf 

https://stackoverflow.com/questions/13334300/how-to-build-and-install-gcc-with-built-in-rpath


查看头文件目录查找顺序
gcc test.cpp -v
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/lib/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0
 /usr/local/lib/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/x86_64-pc-linux-gnu
 /usr/local/lib/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/backward
 /usr/local/lib/gcc/x86_64-pc-linux-gnu/11.2.0/include
 /usr/local/include
 /usr/local/lib/gcc/x86_64-pc-linux-gnu/11.2.0/include-fixed
 /usr/include
End of search list.

gcc默认链接的库:
-lstdc++ -lm -lgcc_s -lgcc -lc /usr/local/lib/gcc/x86_64-pc-linux-gnu/11.2.0/crtend.o /lib/../lib64/crtn.o

查看目标文件与库的链接选项及顺序:
ld -verbose | grep SEARCH
SEARCH_DIR("=/usr/x86_64-redhat-linux/lib64"); 
SEARCH_DIR("=/usr/lib64"); 
SEARCH_DIR("=/usr/local/lib64"); 
SEARCH_DIR("=/lib64"); 
SEARCH_DIR("=/usr/x86_64-redhat-linux/lib"); 
SEARCH_DIR("=/usr/local/lib"); 
SEARCH_DIR("=/lib"); 
SEARCH_DIR("=/usr/lib");

查看依赖的库文件:
#ldd a.out
        linux-vdso.so.1 =>  (0x00007ffea8bc0000)
        libiconv.so.2 => /usr/local/lib64/libiconv.so.2 (0x00007f9651097000)
        libstdc++.so.6 => /usr/local/lib64/libstdc++.so.6 (0x00007f9650c8b000)
        libm.so.6 => /usr/lib64/libm.so.6 (0x00007f9650989000)
        libgcc_s.so.1 => /usr/local/lib64/libgcc_s.so.1 (0x00007f9650770000)
        libc.so.6 => /usr/lib64/libc.so.6 (0x00007f96503a2000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f965137d000)

查看多余的库文件
#ldd -u a.out
Unused direct dependencies:
        /usr/local/lib64/libiconv.so.2
        /usr/local/lib64/libstdc++.so.6
        /usr/lib64/libm.so.6
        /usr/local/lib64/libgcc_s.so.1
        /usr/lib64/libc.so.6
        
只编译需要的库,过滤无用的库:
g++ -Wl,--as_needed main.cpp

查看启动时加载的库:
strace ./a.out

库操作工具-nm、ar、ldd、ldconfig和ld.so

1、nm [options] file    列出file中的所有符号
    [option]
     -c   将符号转化为用户级的名字
     -s   当用于.a文件即静态库时,输出把符号名映射到定义该符号的模块或成员名的索引
     -u   显示在file外定义的符号或没有定义的符号
     -l   显示每个符号的行号,或为定义符号的重定义项

2、ar {dmpqrtx} [member] archive file    用于操作高度结构化的存档文件(.a)
    [options]
    -c    创建存档文件
    -s    创建或升级从符号到定义他们的成员之间的交叉索引映射表
    -r    替换archive中的同名文件或添加新文件
    -q    不检查而直接添加文件到存档文件的末尾
ranlib [-v|-V] file 的作用跟ar -s file相同

3、ldd [options] file    列出file运行所需的共享库
    [options]
    -d    执行重定位并报告所有丢失的函数
    -r    执行对函数和对象的重定位并报告丢失的任何函数或对象

4、 ldconfig [options] [libs]    决定位于目录/usr/lib和/lib下的共享库所需的运行的链接,这些链接由[libs]指定并被保存到/etc/ld.so.conf中
    [options]
    -p    打印文件/etc/ld.so.conf的内容
    -v    更新/etc/ld.so.conf

5、 ld.so    动态链接/加载器
    ld.so使用的两个环境变量
    $LD_LIBRARY_PATH 告诉ld.so去哪里查找保存在非标准目录下的共享库,冒号分隔,对应文件/etc/ld.so.conf
    $LD_PRELOAD告诉ld.so用户指定的在所有库加载之前加载的库所在的目录,空格分隔,对应文件/etc/ld.so.preload

gcc的头文件、库文件默认搜索路径

1.头文件(编译时)
gcc在编译时如何去寻找所需要的头文件:

1、先搜索-I指定的目录
2、然后找gcc的环境变量C_INCLUDE_PATH,CPLUS_INCLUDE_PATH,OBJC_INCLUDE_PATH可以通过设置这些环境变量来添加系统include的路径
3、最后搜索gcc的内定目录(编译时可以通过-nostdinc++选项屏蔽对内定目录搜索头文件)
/usr/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/4.8/include

在安装g++时,指定了prefix,那么内定搜索目录就是:
prefix/include
prefix/local/include
prefix/lib/gcc/--host/--version/include

2.库文件(分为:编译时+运行时)
(1) 编译时(gcc的选项-print-search-dirs,显示编译器的搜索路径)
1、gcc会去找-L
2、再找gcc的环境变量LIBRARY_PATH
   LIBRARY_PATH和LD_LIBRARY_PATH是Linux下的两个环境变量,区别:LIBRARY_PATH环境变量用于在程序编译期间查找动态链接库时指定查找共享库的路径,而LD_LIBRARY_PATH环境变量用于在程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径
3、再找内定目录/lib:/usr/lib:/usr/local/lib这是当初compile gcc时写在程序内的

(2)  运行时
Linux动态库的默认搜索路径是/lib和/usr/lib,动态库被创建后,一般都复制到这两个目录中。在Linux 中,动态库的搜索路径除了默认的搜索路径外,还可以通过以下三种方法来指定。

方法一:在配置文件/etc/ld.so.conf中指定动态库搜索路径。但是每次编辑完该文件后,都必须运行命令ldconfig以达到刷新 /etc/ld.so.cache的效果,使修改后的配置生效。
注:ldconfig是一个动态链接库管理命令,为了让动态链接库为系统所共享,还需运行动态链接库的管理命令--ldconfig。 ldconfig 命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件,缓存文件默认为 /etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表。ldconfig做的这些东西都与运行程序时有关,跟编译时一点关系都没有。编译的时候还是该加-L就得加,不要混淆了;
方法二:通过环境变量LD_LIBRARY_PATH指定动态库搜索路径。
方法三:在编译目标代码时指定该程序的动态库搜索路径。

五种动态库运行时搜索路径搜索的先后顺序是:
1、在编译目标代码时指定该程序的动态库搜索路径
这是通过gcc的参数"-Wl,-rpath,"指定(-L只是编译时使用的搜索路径,而-Wl,-rpath=your_lib_dir是为程序添加一个运行时库文件搜索路径的命令,在使用gcc编译链接时添加即可)。当指定多个动态库搜索路径时,路径之间用冒号":"分隔
2、通过环境变量LD_LIBRARY_PATH指定动态库搜索路径。当通过该环境变量指定多个动态库搜索路径时,路径之间用冒号":"分隔
3、在配置文件/etc/ld.so.conf中指定动态库搜索路径
4、默认的动态库搜索路径/lib
5、默认的动态库搜索路径/usr/lib

Makefile选项CFLAGS,LDFLAGS,LIBS

CFLAGS:  表示用于 C 编译器的选项,
CXXFLAGS:表示用于 C++ 编译器的选项。
这两个变量实际上涵盖了编译和汇编两个步骤。

CFLAGS: 指定头文件(.h文件)的路径,如:CFLAGS=-I/usr/include -I/path/include。

LDFLAGS:gcc 等编译器会用到的一些优化参数,也可以在里面指定库文件的位置。
用法:LDFLAGS=-L/usr/lib -L/path/to/your/lib。

LIBS:   告诉链接器要链接哪些库文件,如LIBS = -lpthread -liconv

简单地说,LDFLAGS是告诉链接器从哪里寻找库文件,而LIBS是告诉链接器要链接哪些库文件。不过使用时链接阶段这两个参数都会加上,所以你即使将这两个的值互换,也没有问题。

有时候LDFLAGS指定-L虽然能让链接器找到库进行链接,但是运行时链接器却找不到这个库,如果要让软件运行时库文件的路径也得到扩展,那么我们需要增加这两个库给"-Wl,R":

LDFLAGS = -L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib

如果在执行./configure以前设置环境变量
export LDFLAGS="-L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib" ,
注意设置环境变量等号两边不可以有空格,而且要加上引号(shell的用法)。
那么执行configure以后,Makefile将会设置这个选项,链接时会有这个参数,编译出来的可执行程序的库文件搜索路径就得到扩展了。

升级gcc/g++

centOS7默认版本为gcc4

yum install centos-release-scl
yum install devtoolset-8-gcc*

激活devtoolset
scl enable devtoolset-8 bash

升级为2019版本
gcc -v

切换不同版本
source /opt/rh/devtoolset-8/enable
source /opt/rh/devtoolset-7/enable

永久替换
mv /usr/bin/gcc /usr/bin/gcc-4.8.5
mv /usr/bin/g++ /usr/bin/g++-4.8.5
ln -s /opt/rh/devtoolset-8/root/bin/gcc /usr/bin/gcc
ln -s /opt/rh/devtoolset-8/root/bin/g++ /usr/bin/g++