一个C文件要变成可执行的文件,需要经过四个过程:
预处理(处理以“#”为开头的命令)
编译(将.c .i等文件翻译成汇编代码)
汇编(将汇编代码翻译成机器代码)
连接(将生成的多个目标文件(.o文件)连接起来,生成可执行文件)
主要用到的工具叫做arm-linux-gcc,arm-linux-ld,本次学习的是gcc工具。
几条常用命令选项:
-E:预处理后即停止
-S:编译后不汇编
-c:预处理+编译+汇编,但不连接
-o:指定输出文件为file。无论哪个环节,这个选项都可以使用
编译单个文件的语句,只需要一句话即可
gcc -o hello hello.c
这条指令,即直接将C文件生成成可执行文件"hello"
虽然这里是一条指令,但已经包含了几个步骤了。
gcc -E -o hello.i hello.c //对C文件做预处理
gcc -S -o hello.s hello.i //进行编译,生成汇编文件
gcc -c -o hello.o hello.s //对汇编代码编译成目标文件
gcc -o hello hello.o //生成可执行程序hello
对于多个文件,需要进行逐个编译,再做最后的连接。
gcc -c -o mian.o mian.c
gcc -c -o sub.o sub.c
gcc -o test mian.o sub.o
也可以直接连接编译生成
gcc -o test mian.c sub.c
基本格式:gcc [options] file1 file2... //若不加入参数,则按默认参数依次执行编译、汇编和连接操作,生成的可执行文件名为 a.out
常用参数:-E //只执行预处理操作
-S //只执行到编译操作完成,不进行汇编操作,生成的是汇编文件(.s 或 .asm),内容为汇编语言
-c //执行编译和汇编,但不进行链接,即只生成可重定位目标文件(.o),为二进制文件,不生成完整的可执行文件
-o filename //将操作后的内容输出到filename指定的文件中
-static //对于支持动态链接的系统,使用静态链接而不是动态链接进行链接操作
-g //编译时生成debug有关的程序信息(供gdb使用)
--save-temps //生成编译过程的中间结果文件(包括预处理文件(x.ii)、汇编代码(x.s)、目标文件(x.o)和最终的可执行文件)
-I PATH //在PATH指定的目录下寻找相关的include文件
-lxx //其中xx为指定函数库,对于Linux环境下的函数库,静态库后缀为.a,动态库后缀为.so,一般库名为libxx.a或libxx.so,如加入libm.so库,则使用参数-lm(去除lib和后缀.a\so)
-L PATH //在PATH指定的目录下寻找相关的库文件,即-lxx指定待链接的库,-L指定寻找该库的路径。不指定时搜索默认的库函数路径。
-std=xx //指定编译使用的语言标准
-x language //指定待编译文件的语言,而不是由编译器根据文件后缀自行判断。即默认情况下gcc根据文件后缀判断使用的编程语言。例如使用文件名hello作为源文件名是不合适的,应使用hello.c
-Wall //输出一些简单的错误以及一些可能存在问题的警告
-Wextra //输出-Wall不包含的警告等
-Werror //将警告视为错误输出
-D name=definition //加入宏定义,若不指定def,则默认为1
-O1、-O2 //规定编译器的优化等级,优化级数越高执行效率一般越好,但是优化会改变原有程序结构,使得其汇编不易理解
//一些进行缓冲区溢出实验时可能需要的选项
-fstack-protector\-fno-stack-protector //是否开启堆栈保护,这里的保护是在返回地址之前加入一个验证值来确保返回地址不被破坏
-z execstack //启用可执行栈,默认是禁用的
//(echo 0 >/proc/sys/kernel/randomize_va_space 关闭地址随机化,这是一个单独的命令,操作需要root权限)
gcc -static hello.o world.o -lm -L /usr/lib //以静态链接的方式,将hello.o、world.o以及libm.a库中的相关目标文件链接,在/usr/lib文件夹下寻找目标库