C/C++文件变成可执行文件的四步
【预处理】 处理#开头的; #include <>
将头文件在当前位置展开; #define a 1
会将a替换成实际的值1,#ifdef X #ifndef
判断X是否成立,成立就将其后语句加入当前位置
【编译】 将预处理完的文件进行语法和语义分析及优化后生成 <汇编源代码文件.s> ,同.c源码是文本可查看
【汇编】 将 <汇编源代码文件.s> 进行汇编,生成二进制的ELF文件,但由于没有链接会找不到printf等函数而无法执行
【链接】 将汇编生成的二进制文件和其引用函数所在的库文件、二进制文件合成可以在特定平台运行的文件
1 2 3 4 5 6 7 8 # 链接动态库时把 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编译器的相关知识
GCC的常用参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 -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 给链接程序
动静态库文件仅在链接的过程存在区别,运行时仅动态库会被调用。
本质上说一个静态库可看成是一组目标文件(.o/.obj文件)的集合,静态库与汇编生成的目标文件(.a/.o/.obj)一起链接为可执行文件
静态库.a和目标文件.o文件格式相似,都是很多目标文件经过压缩打包后形成的一个文件
GCC在编译时都是从源码上面往下编译,GCC在链接时对命令行的参数处理顺序是从左到右,默认都是动态链接
GCC中库的链接顺序是从右往左进行,所以要把最基础实现的库放在最后,这样左边的lib就可以调用右边的lib中的代码
当一个函数的实现代码在多个lib都存在时,最左边的lib代码最后link,所以也将最终保存下来
GCC库和MSVC库的辨认
GCC 的动态库一般是 .so
后缀的,静态库 .a
MSVC的动态库一般是 .dll
后缀的,静态库 .lib
库的搜索路径顺序
1 2 3 4 5 6 1) LD_LIBRARY_PATH 2) /lib 3) /usr/lib 4) /etc/ld.so.conf // 文件中添加的库搜索路径 5) /etc/ld.so.conf.d // 目录下的.conf文件可将不同软件的库搜索路径区分开来
如何查看库文件的相关信息
1 2 3 4 5 ar -t libz.a // 查看静态库中包含的 <目标文件.o> ldd libz.so // 查看动态库链接的其他库 nm -D libz.so // 查看动态库内的函数列表 objdump -t libz.so // 查看动静态库内的函数和源文件等信息
交叉编译工具链的命名规范
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 arch :架构的意思,如ARM,MIPSvendor :工具链的提供厂商,none表示vendor是none,也就是不属于某一公司的 os :支持的操作系统 eabi :嵌入式应用二进制接口(Embedded Application Binary Interface) arm-none-linux-gnueabi-gcc:用于基于ARM架构的Linux系统,可用于编译 ARM 架构的 u-boot、Linux内核、linux应用等。基于GCC使用Glibc库 arm-none-eabi-gcc:用于编译 ARM 架构的裸机系统(包括 ARM Linux 的 boot、kernel,不适用编译 Linux 应用 Application),一般适合 ARM7、Cortex-M 和 Cortex-R 内核的芯片使用,所以不支持那些跟操作系统关系密切的函数。 gcc-x86-64-linux-gnu gcc-aarch64-linux-gnu gcc-arm-linux-gnueabi gcc-arm-linux-gnueabihf
MAKE编译中常用的一些变量
gcc和make并不会主动从环境变量中读取CFLAGS/CXXFLAGS/LDFLAGS,但是可以传给Makefile。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 尽量与共享函数库连接
链接库和头文件搜索路径的先后顺序相似:GCC中-I和-L指定路径的顺序,GCC默认环境变量指定的路径
也可以更改gcc和g++默认的环境变量:
1 2 3 4 5 6 GCC使用:export C_INCLUDE_PATH=XXX:$C_INCLUDE_PATH G++使用:export CPLUS_INCLUDE_PATH=XXX:$CPLUS_INCLUDE_PATH # 也可以查询GCC默认的搜索的目录 gcc -print-search-dirs
实现静态链接库
参数-static和-shared可以改变GCC默认的链接方式,如果指定了 -static
这个选项在连接时对项目所有依赖库都尝试去搜索名为 lib<name>.a
的静态库文件,如果找不到就报错。它不仅搜索我们用的库还包括编译器自带的库,还要包括所有被间接引用的第三方库
编译器自带C/C++基础库的静态链接
1 2 3 4 5 6 -static // 会将所以有用到的外部库全部静态链接 # 静态链接基础库可便于到其他相似机器上运行 -static-gcc // 只将libgcc.so静态链接 -static-libstdc++ // 只将libstdc++.so静态链接
有选择的进行静态编译,连接程序ld提供了一个 -Bstatic
选项用于对跟在它后面的所有库执行静态连接
1 2 3 4 5 6 7 8 9 # 通过-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 # 要静态链接项目也可将-static添加到LDFLAGS变量中。即在 Makefile 中 LDFLAGS ?= -static
链接的优先级:
1 2 3 4 5 6 7 8 9 10 11 首先是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命令可以查看一个可执行程序依赖的共享库 越是被别人调用的越底层的库,就越放在后面;越是调用别人的越上层的库,就越放在前面 g++对于 .o 文件的链接并没有严格的顺序要求,只有对库文件的顺序要求严格
如果使用MinGW编译时有找不到Win32库的情况,可以查看需要包含的Windows库
例如:undefined reference to __imp_GetDeviceCaps
复制 GetDeviceCaps
到上面搜索可推断要加 -lgdi32
对于GCC,库的顺序也很重要。提供符号的库应在链接器命令行中引用该符号的对象之后提及。还需要确保您包含的每个库实际上是静态构建和使用的
使用CMAKE将GNU格式库转换为MSVC格式:在Win上使用MinGW库时,可以定义 CMAKE_GNUtoMS
变量来自动将GCC格式库 .dll和.a
转换为微软编译器支持的 .lib
格式。
解决MinGW64运行缺少 libstdc++-6.dll libgcc_s_seh-1.dll libwinpthread-1.dll
的问题,静态链接它们 -static-libgcc -lstdc++ -lgcc_eh -lpthread -static
Linux下可以通过类似 -l:libevent.a -l:libevent_pthreads.a
来静态链接指定库 -levent -levent_pthreads