The GNU Compiler Collection,一般简称GCC,是一套由GNU开发的编译器集。为何会是一个集合,由于它不只支持C语言编译,还支持C++, Ada, bjective C等语言。另外GCC支持的硬件平台包括:X86处理器架构,ARM架构,MIPS架构。linux
GCC内部由Binutils, Gcc-core, Glibc等软件包组成。shell
1)Binutils:它是一组开发工具,包括链接器,汇编器和其余用于目标文件和档案的工具。这个软件包会依不一样的平台而不一样,由于不一样的架构指令集不一样。sass
2)Gcc-core:GCC的核心部分,可是这部分默认只包含C的编译器及其公共部分,而对其余语言(C++, Ada等)的支持包须要另外安装。架构
3)Glibc:包含经常使用到的一些C的函数库,这个库提供基本的函数,用于分配内存,搜素目录,读写文件,字符串处理等。函数
对GNU编译器来讲,程序的翻译要通过:预处理,汇编,编译,连接四个步骤。GCC常常把前三个合为一个步骤来操做,因此就有了编译模式(-c指定)和编译连接模式(-o指定输出)两种方式。工具
预处理阶段主要处理.h之类的头文件,#include,#ifdef,#define等。生成文件.i。开发工具
汇编阶段主要将.i的中间文件转换为汇编语言文件.s优化
gnu也有专门的汇编工具,as.net
编译阶段主要将汇编语言文件.s转换为2进制机器语言.o命令行
连接阶段主要是将不一样的机器语言文件连接到一块儿,生成最终的可执行文件。
gnu也有专门的连接工具,ld
ar 库文件操做命令,能够查看库文件中的详细状况,将多个对象问价生成一个库文件。
ar -t libname.a 显示全部对象文件的列表;
ar -rv libname.a objfile1.o .... objfile10.o 将objfile1.o ... objfile10.o 打包成一个库文件。
在linux下,库文件,都以lib开头,不然gcc没法识别。
GCC的基本用法:假设源文件程序名是test.c
-Wall,gcc选项,表示打开全部经常使用的警告。
1)无选项编译连接:gcc test.c 将test.c预处理,汇编,编译,连接成可执行文件,未指定输出文件,默认输出a.out。
2)-o指定输出文件:gcc test.c 将test.c预处理,汇编,编译,连接成可执行文件,指定输出文件,test.out。
3)-E只进行预处理:gcc -E test.c -o test.i 将test.c进行预处理操做,生成test.i文件。生成文件后缀.i,只是简单的将define和include展开;
4)-S只进行汇编:gcc -S test.i -o test.s 将test.i进行汇编操做,生成test.s文件。
5)-c只进行编译:gcc -c test.s 将test.s进行编译操做,生成test.o文件。
6)对编译后的文件进行无选项操做:gcc test.o -o test 将test.o连接生成可执行文件test。
7)-fPIC ,产生位置无关的目标代码;
8)-L dir,将dir加到编译器,进行连接的搜索列表中;
-lname,连接名为libname.a,libname.so的库文件。
9) -g,用在debug调试。
10) -Wl, 表示将后面的参数,传递给连接器。
11) --whole-achive --no-whole-archive,是ld的专用命令行参数,gcc并不认识,只能经过加Wl,来传递给gcc。
将在其以后的全部静态库包括的函数和变量输出给动态库,--no-whole-achive,表示关掉这个特性。
--whole-achive做用IA的库中不能有函数重名。
12) -shared,表示生成动态库文件,进行动态编译时,尽可能使用动态库。
13) -static,表示尽可能使用静态库。
多个文件处理:
gcc test1.c test2.c -o test 将test1和test2分别预处理,汇编。编译后连接为test可执行文件输出。
gcc test1.o test2.o -o test 将test1编译后文件和test2编译后文件,连接为test可执行文件输出。
gcc -o输出以后的文件,能够直接在shell中运行。
gcc -I +dir:来指明.h文件的路径,而后在C的.h文件中,能够直接包含该路径中的.h文件,不须要写入路径。
-D +define:来指明gcc编译过程当中,添加的define(与ifdef...配套使用)
gcc在编译的过程当中,会对代码进行多方面的优化,主要有这几类:
1) 精简操做指令,
2) 尽可能知足CPU的流水线操做,
3) 从新调整代码的执行顺序,
4) 函数的展开调度,
gcc提供了o0-o3的几种优化级别供用户设置,
-O0:不作优化,默认的编译选项,
-O1:对程序作部分优化,gcc尝试减少生成代码的尺寸,缩短执行时间,
-O2:gcc将执行几乎全部的空间时间的优化,具体的优化项目:https://blog.csdn.net/qq_31108501/article/details/51842166
-O3:再次打开一些优化选项,
-Os:主要是针对程序的尺寸的优化。
优化代码带来的两个问题:
1) 调试问题,由于不少分支的合并,公用表达式的消除,等优化
2) 内存操做顺序的影响,能够经过选项关闭,asm __ __violatile()。
Linux下的gdb和gcc默认输出的汇编格式都是AT&T格式,可是他们都有方式来转换为intel格式;
-masm=[intel|att] 选择intel或者att的汇编语法
gcc -S -masm=intel test.c
gdb则是设置环境变量, set disassembly-flavor intel
对于预处理宏, -Dmacro=string,等价于在头文件中加定义,#define macro string
-Dmacro,等价于在头文件中加定义,#define macro 1或者#define macro
-D中能够加空格,也能够不加空格。
gcc取消预约义,使用-U来作,能够中间加空格,也能够不加空格。
交叉编译:在一种机器结构下编译的软件将在另外一种彻底不一样的机器结构下执行。一个常见的例子是在PC机上编译运行在ARM,MIPS上的软件。因为GCC能够在命令行显式调用编译器,从而适合交叉编译。 arm-linux-gcc -o test.c。
arm-linux-gcc:基于ARM目标机的交叉编译软件,跟GCC所需的安装包不一样,X86和ARM的指令集不一样,因此Binutils不同,gcc-core依赖于Binutils,因此gcc-core也不相同,glibc库不一样。
arm-elf-gcc:也是基于ARM目标机的交叉编译软件。二者区别主要在glibc的不一样。arm-linux-gcc使用GNU的glibc,而arm-elf-gc通常使用uClibc等专门为嵌入式系统开发的C库。Glibc针对PC开发,uClibc小型C语言库,实现Glibc的部分功能。
汇编中的几个字符:
.text 部分表示该部分,须要放在代码段中;是arm_gcc的编译关键字;(通常会在以后设置为空间只读)
.data 部分表示该部分,已初始化的全局变量的一块内存区域,须要放在数据段中;(属于静态内存分配)
.bss 部分表示该部分,未初始化的全局变量的一块内存区域,(属于静态内存分配)
.rodata 部分表示该部分,存放C中的字符串和#define定义的变量;
.heap堆 进程动态运行中被动态分配的内存段,大小不固定,malloc,free来进行分配与回收;
.stack栈 存放程序临时的局部变量,函数被调用时,参数也会被压入该栈中。
.global 是一个全局变量,能够实现汇编和C之间传递信息,能够是main函数的函数地址;
.global _start
_start:
.......
常见的gnu的伪指令:
.global _start @给_start外部连接属性
.section
咱们经常须要将一些公用函数制做成函数库,供其余函数使用,函数库分为静态库和动态库,
静态库,在程序编译是,被连接到目标代码中,程序运行时,不须要该库,
动态库,在程序运行时,直接载入,程序运行时,还须要动态库存在。
生成静态库: gcc -c hello.c
ar rv libmyhello.a hello.o
生成可执行文件时,gcc main.c libmyhello.a -o main
生成动态库,有三种方法,ld -G
gcc -shared
libtool
使用ld命令最复杂,gcc -shared最简单,可是并非在任何平台下均可以使用,因此GNU提供了一个更好的工具,libtool。
使用gcc -shared来生成,gcc -shared -o libmyjob.so myjob.o 将myjob.o生成动态库。
file命令:查看linux下文件格式;
size命令:查看linux下elf文件的各个段大小;
nm命令:查看linux下elf文件的符号表;
strip命令:用来去掉ELF文件中的调试信息;
ldd命令:用来查看一个程序主模块或者共享库的依赖;
linux下默认的连接脚本在/usr/lib/ldscripts/,可使用ld --verbose来查看;
objdump -x example.o:详细显示目标文件的内容;
-h example.o:显示目标文件的各个段的信息打印;
-s example.o:将全部段的内容以16进制的方式打印;
-d example.o:将全部包含指令的段反汇编;
-r example.o:显示目标文件中的重定位表;
readelf -h example.o:显示elf文件头信息(格式定义在/usr/include/elf.h);32bit的elf格式头文件占52byte,64bit的elf格式头文件占64byte。
-S example.o:显示elf文件的head头文件(比objdump显示的符号表要全)(格式定义在/usr/include/elf.h);
-s example.o:显示elf文件中的符号表信息;
objcopy,将二进制文件copy到目标文件中的某个段,或者将某个目标文件的一部分copy到另外一个目标文件中。
objcopy -I binary -O elf32-i386 -B i386 image.jpg images.o
gcc提供方法,将某些变量和函数能够存放在指定的段中去:
__attribute__((section("Foo"))) int global = 42;
指定弱符号和强符号:
__attribute__(("Weak")) weak2 = 2;
指定强引用,弱引用:
__attribute__(("weakref")) void foo();
未初始化的全局变量通常当作弱符号来处理,放在common块中,也能够显示声明不放在common块中,此时做为强符号处理;
int global __attribute__((nocommon)) a;