最近在做exe体积优化的事情,于是把vc10编译器连接器有关的选项都过了一遍
一般情况下,使用/O1做最小体积优化
根据msdn介绍,/O1等于/Og /Os /Oy /Ob2 /Gs /GF /Gy,也就是说
/O1 会自动开启 <Global Optimization>/Og、<Favor small code>/Os、<Omit Frame Pointers> /Oy、<string pooling>/GF、/Gs(Control Stack Checking Calls)、<Enable Function-Level Linking> /Gy
并且/O1 会自动关闭 <basic runtime checks>、<smaller type checks>
这些参数作用分别是
/Og表达式合并,变量寄存器化,循环优化,nrvo
/Os优化以体积优先
/Oy令编译器编译函数时,prologue不产生push ebp; mov ebp esp,epilogue不产生pop ebp代码,函数体内不产生基于ebp寻址的代码,对调试程序有一定影响,特别是当程序使用动态栈内存分配时
/Ob2,展开inline函数
/GF,不用说了
/Gs,默认是4k,当函数栈变量大于4k时,调用_chkstk函数分配更多的栈空间
/Gy,合并相同的函数,例如不同命名空间内相同的函数、模板类的非模板函数;去除没有使用的函数
接下来需要设置以下选项,使exe体积更小
关闭<inline fuction expansion> /Ob0,禁止编译器展开标记为inline的函数,个人认为,人为的inline不可取,应更相信编译器及连接器的能力
开启<whole program optimization> /GL,用于支撑连接器/LTCG
禁止<enable c++ exceptions> no/EH,编译参数不要加上任何/EH相关的参数
禁止<buffer security check> /GS- 不插入缓冲区检查代码
缓冲区溢出检查原理,在函数栈内,返回地址与栈变量间插入4字节标记,进入函数时设置值,函数返回时检查该值是否被修改,如果被修改,就证明函数栈溢出
可参考msdn http://msdn.microsoft.com/library/aa290051.aspx
开启/Gy后,需要配合连接器 /OPT:REF /OPT:ICF
禁止<Enable Run-Time Type Information> /GR- 不使用C++ RTTI,对极少数使用dynamic_cast有影响
连接器选项
开启<merge section> /MERGE .rdata=.text 合并相同属性PE段,减少PE文件体积;不能合并不同属性段,.rsrc段必须单独存在
禁止<randomized base address> /DYNAMICBASE:NO 注意,randomized base address是vista引入的一种防御缓冲区溢出攻击的手段
根据msdn,vista开始,dll加载的地址不再是固定不变,在256个可能的地址内随机选择一个,这样做使缓冲区溢出攻击的目标地址变得不可确定。
注意,若开启这个选项,就表明这个模块支持rba,加载地址必然是随机;但不开启,并不代表加载地址不变,windows加载dll时如果地址冲突,会选择新的地址加载,这个能力由下面这个选项决定是否开启
开启<fixed base address> /FIXED 与上一个选项联合作用,使连接器不生成PE的.reloc段,减少PE文件体积;注意只针对exe,dll不应该这样设置;如果dll不支持可变基址,地址冲突时将导致dll无法加载
不支持<image has safe exception handlers> /SAFESEH:NO 不创建SEH表,safe exception handlers table是一种防御缓冲区溢出攻击的手段;注意,启用安全SEH处理表后,只有在表内登记的异常情况才允许执行异常处理例程,用于防止缓冲区溢出攻击改写异常处理例程地址。
开启<link-time code generation> <profile guided optimization> /LTCG LTCG:PGI PGO 连接器代码生成,可在模块间进行代码优化;应优先使用PGI PGO,如果条件不允许,单独使用LTCG也能得到比较好的效果