GCC编译器的相关知识

C/C++文件变成可执行文件的四步

  1. 【预处理】 处理#开头的; #include <> 将头文件在当前位置展开; #define a 1 会将a替换成实际的值1,#ifdef X #ifndef 判断X是否成立,成立就将其后语句加入当前位置

  2. 【编译】 将预处理完的文件进行语法和语义分析及优化后生成 <汇编源代码文件.s> ,同.c源码是文本可查看

  3. 【汇编】 将 <汇编源代码文件.s> 进行汇编,生成二进制的ELF文件,但由于没有链接会找不到printf等函数而无法执行

  4. 【链接】 将汇编生成的二进制文件和其引用函数所在的库文件、二进制文件合成可以在特定平台运行的文件

1
2
3
4
5
6
7
# 链接动态库时把 printf函数的位置信息 链接到 生成的二进制ELF文件 中去,等运行时根据 printf函数的位置信息 去系统中找相关运行库
# 链接静态库时把 printf函数 直接链接到 生成的二进制ELF文件 中去,等运行时就不需要去系统中找了

预处理(cpp) gcc -E test.c -o test.i 将所有包含的相关内容都弄到test.i对应的位置
编译(ccl) gcc -S test.i -o test.s 将源文件或预处理后文件编译成汇编文件
汇编(as) gcc -c test.s -o test.o 执行预处理,编译和汇编生成目标代码文件
链接(ld) gcc test.o -o test.exe 连接目标代码生成可执行程序

GCC编译器的相关知识

  1. GCC的常用参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-E			预处理后只输出到屏幕不生成文件,可接-o指定生成文件,接-C不删除注释信息
-S 把预处理后的文件进行语法分析、语义分析以及优化后生成 <汇编代码文件.s>
-c 执行预处理,编译和汇编,也就只把程序做成obj的 <目标文件.o> 而不链接
-C 在预处理的时候不删除注释信息,一般和-E使用分析程序用这个很方便的
-o FILE 生成的可执行文件名,默认:a.out a.exe
-g 生成调试信息。GNU调试器可利用该信息
-w 不生成任何警告信息
-Wall 生成所有警告信息
-I(DIR) 指定的额外头文件搜索路径(DIR)
-L(DIR) 指定的额外函数库搜索路径(DIR)
-l(LIBRARY) 连接时搜索指定的函数库(LIBRARY)
-O0 -O1 -O2 -O3 不进行优化处理0,优化生成代码的等级123
-shared 使用动态库生成需要额外库的执行程序
-static 使用静态库生成无需额外库的执行程序
-Wa,option 传递参数 option 给汇编程序
-Wl,option 传递参数 option 给链接程序
  1. 动静态库文件仅在链接的过程存在区别,运行时仅动态库会被调用。

  2. 本质上说一个静态库可看成是一组目标文件(.o/.obj文件)的集合,静态库与汇编生成的目标文件(.a/.o/.obj)一起链接为可执行文件

  3. 静态库.a和目标文件.o文件格式相似,都是很多目标文件经过压缩打包后形成的一个文件

  4. GCC在编译时都是从源码上面往下编译,GCC在链接时对命令行的参数处理顺序是从左到右,默认都是动态链接

  5. GCC中库的链接顺序是从右往左进行,所以要把最基础实现的库放在最后,这样左边的lib就可以调用右边的lib中的代码

  6. 当一个函数的实现代码在多个lib都存在时,最左边的lib代码最后link,所以也将最终保存下来

  7. GCC库和MSVC库的辨认

GCC 的动态库一般是 .so 后缀的,静态库 .a
MSVC的动态库一般是 .dll 后缀的,静态库 .lib

  1. 库的搜索路径顺序
1
2
3
4
5
1) LD_LIBRARY_PATH 
2) /lib
3) /usr/lib
4) /etc/ld.so.conf // 文件中添加的库搜索路径
5) /etc/ld.so.conf.d // 目录下的.conf文件可将不同软件的库搜索路径区分开来
  1. 如何查看库文件的相关信息
1
2
3
4
ar -t libz.a				// 查看静态库中包含的 <目标文件.o>
ldd libz.so // 查看动态库链接的其他库
nm -D libz.so // 查看动态库内的函数列表
objdump -t libz.so // 查看动静态库内的函数和源文件等信息

MAKE编译中常用的一些变量

  1. gcc和make并不会主动从环境变量中读取CFLAGS/CXXFLAGS/LDFLAGS,但是可以传给Makefile。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CFLAGS		用于 C 编译器的参数
LDFLAGS 链接器的命令行参数
CXXFLAGS 用于C++编译器的参数
CPPFLAGS C/C++预处理器的参数

CFLAGS 还可用于指定头文件的路径:CFLAGS="-I/opt/include"
LDFLAGS 还可用于指定库文件的路径:LDFLAGS="-L/opt/lib"
LIBS 用于指定要链接哪些库文件:LIBS="-lz -lpng16"

# 默认情况下,CFLAGS将被传给C预处理器,而CXXFLAGS将被传给C++编译器,CPPFLAGS传递给所有语言
# LDFLAGS是告诉链接器从哪里寻找库文件,而LIBS是告诉链接器要链接哪些库文件

-Idir 在-Idir选项指定的目录下和/include,/usr/lib目录下寻找库文件
-Ldir 在-Ldir选项指定的目录下和/lib,/usr/lib目录下寻找库文件
-lname 在连接时使用函数库libname.a,在没有使用-static选项时,如果发现共享函数库libname.so,则使用libname.so进行动态连接。
-static 禁止与共享函数库连接。
-shared 尽量与共享函数库连接
  1. 链接库和头文件搜索路径的先后顺序相似:GCC中-I和-L指定路径的顺序,GCC默认环境变量指定的路径

  2. 也可以更改gcc和g++默认的环境变量:

1
2
3
4
5
GCC使用:export C_INCLUDE_PATH=XXX:$C_INCLUDE_PATH
G++使用:export CPLUS_INCLUDE_PATH=XXX:$CPLUS_INCLUDE_PATH

# 也可以查询GCC默认的搜索的目录
gcc -print-search-dirs

实现静态链接库

  1. 参数-static和-shared可以改变GCC默认的链接方式,如果指定了 -static 这个选项在连接时对项目所有依赖库都尝试去搜索名为 lib<name>.a 的静态库文件,如果找不到就报错。它不仅搜索我们用的库还包括编译器自带的库,还要包括所有被间接引用的第三方库

  2. 编译器自带C/C++基础库的静态链接

1
2
3
4
5
-static			// 会将所以有用到的外部库全部静态链接

# 静态链接基础库可便于到其他相似机器上运行
-static-gcc // 只将libgcc.so静态链接
-static-libstdc++ // 只将libstdc++.so静态链接
  1. 有选择的进行静态编译,连接程序ld提供了一个 -Bstatic 选项用于对跟在它后面的所有库执行静态连接
1
2
3
4
5
6
# 通过-Wl将GCC的命令行参数传递给链接器ld,注意:-Wl后的 `,` 必不可少,如果传递多个参数之间用它分隔
-Wl,-Bstatic -lz -ly // 跟在后面的-lxx选项链接的都是静态库
-Wl,-Bdynamic -lz -lx // 跟在后面的-lxx选项链接的都是动态库

# 例子:将libstdc++和libpthread静态链接,zlib库动态链接。注意-lstdc++需在-lpthread之前
gcc test.c -static-libgcc -Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic -lz
  1. 链接的优先级:
1
2
3
4
5
6
7
首先是GCC的链接选项 -Wl,-Bstatic 和 -Wl,-Bdynamic 指定的链接动态库或者静态库
其次是默认情况下GCC/G++链接时优先链接动态库,如果没有动态库则链接相应的静态库。
如果参数里面没指定强制的连接方式标记,那么GCC将按照默认的优先级去链接,优先动态链接

如果你想静态连接libA.a同时动态连接libB.so 例子:gcc ... -Wl,-Bstatic -lA -Wl,-Bdynamic -lB ...

对于C语言之外的语言,MinGW使用标准的GNU运行库libc,如C++使用GNU libstdc++,ldd命令可以查看一个可执行程序依赖的共享库
  1. 如果使用MinGW编译时有找不到Win32库的情况,可以查看需要包含的Windows库

例如:undefined reference to __imp_GetDeviceCaps 复制 GetDeviceCaps 到上面搜索可推断要加 -lgdi32

  1. 对于GCC,库的顺序也很重要。提供符号的库应在链接器命令行中引用该符号的对象之后提及。还需要确保您包含的每个库实际上是静态构建和使用的
  1. 使用CMAKE将GNU格式库转换为MSVC格式:在Win上使用MinGW库时,可以定义 CMAKE_GNUtoMS 变量来自动将GCC格式库 .dll和.a 转换为微软编译器支持的 .lib 格式。

  2. 解决MinGW64运行缺少 libstdc++-6.dll libgcc_s_seh-1.dll libwinpthread-1.dll 的问题,静态链接它们 -static-libgcc -lstdc++ -lgcc_eh -lpthread -static

  3. Linux下可以通过类似 -l:libevent.a -l:libevent_pthreads.a 来静态链接指定库 -levent -levent_pthreads