购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.1.5 gcc的常用选项

gcc在各个阶段常用的选项如表2.1所示。

表2.1 gcc在各个阶段常用的命令选项

(1)-D选项:定义预处理阶段使用的宏。通过“-Dname”可定义一个名为“name”的宏;通过“-Dname=value”可定义一个名为“name”的宏,其值为“value”。注意:当值中包含有空格时,需要用双引号引起来。

举例说明:

Listing 2.6 宏定义及编译选项-D测试源码MicroOptionTest.c

程序的运行如下:

方式1:

方式2:

(2)-I选项。通过“-Idir”可将“dir”增加到头文件的搜索路径,这个路径优先于系统的默认路径。

●当使用#include“file”时,gcc/g++会先在当前目录查找所指定的头文件,如果没有找到,则会到默认的头文件目录中查找。如果使用“-I”指定目录,则会先在所指定的目录中查找,然后按常规的顺序去查找。

●当使用#include<file>时,gcc/g++会到-I指定的目录中查找,若查找不到,则再到系统默认的头文件目录中查找。

例如:

会使用命令“cpp-v”查看默认的包含路径。

(3)-l选项。通过“-l”可指定链接的库文件的名字。如果库文件为test.so,则选项为“-ltest”。

(4)-L选项。通过“-Ldir”可增加“-l”(小写的l)选项指定库文件的搜索路径,即编译器会到“dir”路径下搜索由“-l”指定的库文件。这和设置环境变量“LIBRARY_PATH”有类似的效果。例如,-L.(“.”表示当前路径)和-L/usr/lib(“/usr/lib”为路径,这里的路径是绝对路径),如果没有提供“-L”选项,gcc将在默认的库文件路径下搜索链接的库文件。通过命令“gcc-v”可以查看系统使用的默认库路径和库。

(5)-O(大写的O)选项。我们在对程序进行反汇编时发现,有时调用的printf()函数被转化为call puts命令,而不是call printf命令,原因何在?这是编译器对printf()函数的一种优化。实践证明,对于printf()函数的参数如果是以“\n”结束的纯字符串,则printf()函数会被优化为puts()函数,字符串的结尾的“\n”符号被消除。除此之外,都会正常生成call printf命令。

gcc在编译优化时采取分级策略,gcc默认提供了5级优化选项的集合(-O0、-O1、-O2、-O3和-Os):

-O0:使用该选项时,优化是关闭的,这样编译所花费的时间最快,但目标文件最大,运行所花费的时间最长。

-O1:使用该选项能减少目标文件的大小以及执行时间,并且不会明显增加编译时间。该选项在编译大型程序时会显著增加编译时的内存使用量。

-O2:包含-O1选项的优化功能,并增加了不需要在目标文件大小和执行速度上进行折中的优化功能。使用该选项时,编译器不执行循环展开以及函数内联,该选项将增加编译时间和目标文件的执行性能。

-O3:该选项包含所有-O2选项的优化选项并且增加-finline-functions、-funswitch-loops、-fpredictive-commoning、-fgcse-after-reload和-ftree-vectorize优化选项。

-Os:专门优化目标文件大小,执行所有的不增加目标文件大小的-O2优化选项,并且执行专门减小目标文件大小的优化选项。

(6)-g调试选项。gcc在生成调试符号时,同样采用了分级的思路,开发人员可以通过在“-g”选项后附加数字0、1、2、3来指定在代码中加入调试信息的多少。

-g0:生成的可执行文件不包含调试信息。

-g1:生成的可执行文件包含最少的调试信息,不包含局部变量和与行号有关的调试信息,因此只能用于回溯跟踪和堆栈转储。回溯追踪指的是监视程序在运行过程中函数调用历史,堆栈转储是一种以原始的十六进制格式保存程序执行环境的方法。

-g:即-g2,生成的可执行文件包含默认的调试信息。调试信息包括扩展的符号表、行号、局部或外部变量信息。

-g3:生成的可执行文件包含最多的调试信息,包含-g2中的所有调试信息以及源代码中定义的宏。

(7)-W警告选项。包括以下两种选项:

-Wall选项:使gcc产生尽可能多的警告信息。警告信息很可能是错误的来源,特别是隐式编程错误,所以尽量保证零警告。

-Werror选项:要求gcc将所有的警告当成错误进行处理。

(8)安全保护选项。

①NX选项。NX即No-eXecute(不可执行)的意思,NX数据执行保护(Date Execute Protection, DEP)的基本原理是将数据所在的内存页标识为不可执行。例如,当栈缓冲区溢出攻击并且程序溢出成功转入shellcode时,程序会尝试在数据页面上执行命令,此时CPU就会抛出异常,而不会去执行shellcode的命令。

gcc编译器默认开启了NX选项,如果需要关闭NX选项,则可以向gcc编译器添加-z execstack参数。例如:

在链接时使用

可标记当前链接的目标文件是不需要executable stack的。使用

可标记当前链接的目标文件需要executable stack。

②Canary栈保护选项。-fno-stack-protector表示关闭,-fstack-protector表示开启,-fstack-protector-all表示全开启。例如:

③-fpic、-fPIC选项。-fPIC选项会在生成共享库时产生位置无关代码,一般可放在CFLAGS中。当运行程序时,动态装载器负责确定.got表的内容。动态装载器不是gcc的一部分,而是操作系统的一部分。

当使用-fpic选项时,宏__pic__和__PIC__定义为1;当使用-fPIC选项时,宏__pic__和__PIC__定义为2。

④-fpie、-fPIE选项。该选项的功能与-fpic、-fPIC类似,但只能用于编译代码,编译后的代码在链接阶段使用-pie选项只能生成可执行文件。

当使用-fpie选项时,宏__pie__和__PIE__定义为1;当使用-fPIE选项时,宏__pie__和__PIE__定义为2。

⑤-pie、-no-pie选项。-pie选项可生成一个与动态链接位置无关的可执行文件,一般放在LDFLAGS中。为了获得可预测的结果,还必须指定用于编译的相同选项集(-fpie、-fPIE或model suboptions)。

PIE主要负责的是代码节和数据节(.data节和.bss节)的地址随机化工作,ASLR则主要负责其他内存的地址随机化。

这里有个约定俗成的规定:64位系统中,pie是以7f开头的;32位系统中,pie则是以f7开头的。例如:

⑥RELRO(Read only Relocation)选项。只读重定位,即设置重定位的部分只读,常用于对.got表、.got.plt表的保护。-z norelro、-z lazy、-z now分别表示关闭、部分开启、完全开启保护。

-z,relro选项的作用是在目标文件中创建一个PT_GNU_RELRO段,这个重定位区域是只读的,用于防止目标文件被篡改,编译器一般都会默认带上此选项。

-z,now选项常和-z,relro一起使用,用于防止生成的目标文件被篡改。设置这个选项的目的是告诉动态链接器在程序开始运行或者以dlopen()函数打开动态库时,加载并绑定所有的动态符号,而不是延迟到首次调用动态符号时再加载。

⑦-fno-plt选项。若目标程序是与位置无关的代码,则在其被外部函数调用时不使用.plt表,而是直接从.got表中取出被调用函数地址。若使用-fno-plt选项,则所有的外部符号都会在程序装载时确定,这样代码会得到一定优化,因为在32位的x86系统上,.plt表用一个特定的寄存器存储.got表的指针,而在延迟绑定机制中,必须要使用.plt表。例如:

的结果为:

的结果为: EPdaz6MZvQ9qxWxq2JUof4/IIiqskDQPTOQUOTbAS50yYry7nyUIGqNKUaaNZ0bQ

点击中间区域
呼出菜单
上一章
目录
下一章
×