今天突发奇想,去研究研究这方面,假设各种体积优化都是有好处的,那MS为什么不这么做呢?
下面就是我的个人见解了,一个一个来看。
------------------------
#pragma comment(linker, "/ENTRY:xxx")
这一句的意思是指定入口函数,适用于非MFC程序(当然UNICODE的MFC程序也得有这句话,不考虑)。
不管是DLL还是EXE,加上这一句话之后生成的文件体积会瞬间减少几十KB。
减少的东西是什么?反汇编分析很明显,可自己动手尝试一下。
下文中的“伪入口”则为不指定入口点反汇编看到的入口函数。
------------
EXE程序:
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPCTSTR lpCmdLine, int nCmdShow);
这句话大家肯定熟悉,是否指定入口点对WinMain的参数有决定性的影响。
细心的人会发现,不指定入口点,nCmdShow总是为SW_SHOWDEFAULT,hPrevInstance总是为NULL。
不指定,则各个参数正常,指定之后,各个参数则全部异常。
那么真实情况是WinMain的参数是由“伪入口”传入的,“伪入口”调用了GetModuleHandle和GetCommandLine,并作为参数传递给WinMain,其余两个参数为固定值。WinMain是作为一个主线程的函数,也就是被系统调用的CreateThread所创建,CreateThread只支持一个PVOID参数,所以我感觉WinMain的参数纯粹就是忽悠人的,一旦指定了入口点,取参数的时候可能会引起异常,导致崩溃。参数是在堆栈中的,但是反汇编发现,堆栈里都是乱七八糟的,并不是真正的参数,而且栈顶指向的是ExitThread的地址。
如果指定了入口点,并且在WinMain中创建了其他线程,那么当函数使用return返回的时候,进程并不会退出。
所以,想使用这一句优化指令,必须受到以下限制:
1.不能使用参数,用函数获取。
2.不能使用return来退出进程,要使用ExitProcess(除非你并不那么要求)。
3.不能调用任何C运行库函数(原因后面讲)。
做到了以上几点,你就可以大胆放心的加上这一句优化指令了,享受体积缩小的乐趣吧。
------------
DLL程序:
BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID);
指定入口点之后,反汇编看到的代码确实是DllMain函数,而且堆栈中的参数都是正确的。
这就说明DllMain之前还有代码,但是并不在自身程序中(在哪里我们不必关心)。
那么,“伪入口”都做了什么?还是暂时不讲。
栈顶也不是ExitThread函数的地址了,而是一段系统代码,有兴趣的朋友可以跟踪看看。
同样的,想使用这一句优化指令,必须受到以下限制:
1.不能调用任何C运行库函数。
就一条限制,做到了就可以使用优化指令了。
------------
上面两种类型程序,在指定了入口点的时候都不能调用C运行库函数,这是因为“伪入口”做了初始化C运行库的操作,因为对于C运行库的函数来说,每一个线程都有一份独立的“全局变量”,没有“伪入口”去调用C运行库的函数肯能会发生异常。
至于所谓线程中独立的“全局变量”,去找找资料,我说不清楚。
------------------------
#pragma comment(lib, "msvcrt.lib")
这一句优化也能使程序缩小好多,这类似于MFC程序中“使用共享DLL”和“使用静态DLL”之间的关系。
不做优化的时候调用C运行库函数大部分是由自身代码实现的,典型的例子是memset函数,反汇编一看就明白。
做优化的时候函数全部强制调用外部函数,特点表现在import table上。
做不做优化无所谓,至于优缺点,个人没有看法,
不过我是不喜欢过于依赖外部函数,MS既然那么做了,还是为了程序自身的兼容性,毕竟C运行库函数目前为止有了好几个版本,什么msvcrt.dll/msvcrt80.dll/msvcrt100.dll他们之间肯定有差异,所以个人建议还是自己实现的好(不优化),又不要你自己敲代码,怕啥。
------------------------
#pragma comment(linker, "/OPT:NOWIN98")
这句话使得程序不再兼容Windows 98系统,对于目前的形式来讲,除非特定要求,这句话大可加上,体积也会缩小很多。
特点表现在PE结构中IMAGE_OPTIONAL_HEADER的SectionAlignment成员。
优化的时候SectionAlignment为0x200,不优化的时候SectionAlignment为0x1000。
SectionAlignment的意思是区段的数据对其粒度,98系统支持的粒度是0x1000的整数倍,NT以上系统支持的是0x200的整数倍。
也就是说如果一个区段使用了0x201个字节,那么这个区段占用的数据空间是0x400字节(优化后)。
所以,优化之后,可以减少很多不必要的空间,因为默认是支持Windows 98的,SectionAlignment为0x1000。
------------------------
至于其他优化指令,大多没有什么明显的效果,暂不考虑。