优化你程序的大小 - 宏观优化

3 篇文章 0 订阅
1 篇文章 0 订阅

原文:http://blogs.msdn.com/xiangfan/archive/2008/09/19/minimize-the-size-of-your-program-high-level.aspx

注:本文的主要目的在于最小化可执行文件的大小。里面提到的技巧并不适用于实际的应用。生成的PE文件是否有效依赖于特定的架构、操作系统和工具

现在,程序的大小已经不再是程序员需要担心的问题了。但是,编写一个非常精简的程序也是一件很有意思的事情。

为了简化分析,本文仅限于讨论WinXP,x86平台上的PE格式程序。这里选择VC9做为我们的编译器。目标程序见文末的清单。

如果你使用默认的编译选项编译这段代码,你将得到一个56320字节的可执行文件。

cl /c test.cpp
link test.obj

已经很小了,是吧?不过实际上,它里面的大部分代码并不是你写的。默认编译器会静态链接到CRT库,所以它们会成为你程序的一部分。我们最好去掉这些“累赘”。让我们给cl.exe加上/MD选项。嗯,test.exe现在只有5632字节了!不过不用着急,现在还只是刚刚开始,我们还能更进一步的减小它的大小。

如果你查看一下生成的文件,你仍然会发现许多不属于你的东西。比如说,文件的入口函数start是编译器“偷偷”加到文件中用来初始化CRT的(你可以在“Microsoft Visual Studio 9.0/VC/crt/src/crt0.c”中查看这个函数的源代码)。在我们的程序中,这些初始化代码可以被忽略。通过/entry:"main"参数,我们可以告诉链接器直接使用我们的main函数作为程序的入口点。不过很不幸,在VC9中如果你这样做的话会得到如下的错误:

MSVCRT.lib(gs_report.obj) : error LNK2019: unresolved external symbol __imp__TerminateProcess@8 referenced in function ___report_gsfailure
MSVCRT.lib(gs_report.obj) : error LNK2019: unresolved external symbol __imp__GetCurrentProcess@0 referenced in function ___report_gsfailure
MSVCRT.lib(gs_report.obj) : error LNK2019: unresolved external symbol __imp__UnhandledExceptionFilter@4 referenced in function ___report_gsfailure
MSVCRT.lib(gs_report.obj) : error LNK2019: unresolved external symbol __imp__SetUnhandledExceptionFilter@4 referenced in function ___report_gsfailure
MSVCRT.lib(gs_report.obj) : error LNK2019: unresolved external symbol __imp__IsDebuggerPresent@0 referenced in function ___report_gsfailure

不用担心,这些是/GS选项导致的,我们之后会讨论。现在我们直接将kernel32.lib传给link.exe来解决这个错误。现在test.exe只有3072字节了。

/GS用于探测潜在的缓冲区溢出的,默认是打开的。这个选项非常有用,不过这里我们的主要目的是优化程序的大小,并不考虑潜在的缓冲区溢出问题,因此只好割爱了。传递/GS-给cl.exe之后test.exe的大小减少到2560字节!

现在程序中已经没有多余的代码了,所以我们可以关注其它的问题。在PE格式中,不同类型的数据被放到不同的段之中。比如,VC会将代码存放到".text"段,而将数据存放到".data"段。默认每个段都要对齐到512字节的边界。这也是为什么我们前面得到的大小都是512的倍数。我们可以将这些段合并来节省空间。下面的选项可以让link.exe合并段:/MERGE:.rdata=.text /MERGE:.data=.text(注:对于实际的应用,使用上述开关可能会导致问题)。这样test.exe就只有1536字节了。

如果使用/O1选项(针对大小优化),我们可以将程序的大小减少到1024字节。如果我们将段对齐的要求放宽到4字节(使用连接器选项/ALIGN:4)(注:对于实际的应用,使用上述开关可能会导致问题),我们将得到896字节的程序。

经过这些宏观的优化之后,我们将exe整整减小了98%!

cl /O1 /MD /GS- /c test.cpp
link test.obj /entry:"main" /MERGE:.rdata=.text /MERGE:.data=.text /ALIGN:4

PS:如果main函数是空函数的话,结合上面的编译选项,能够生成仅有468字节的程序。这是宏观优化能够做到的极限了。如果还要再进一步的话,我们就需要微观的优化了。 微观优化:http://blog.csdn.net/nineforever/archive/2008/10/17/3092825.aspx

程序清单如下: 

#include <cstdio>
#include <cstring>

static unsigned char Tbl[9]={1,1,2,3,7,5,6,4,8};

int main()
{
    unsigned char BufT[0x401];
    int n;
    scanf("%d",&n);
    for (;n>0;--n) {
        scanf("%s",BufT);

        unsigned long keyT[4]={0xCBDCEDFE,0x8798A9BA,0x43546576,0x00102132};
        unsigned char *Buf=BufT;
        int tt;
        while (tt=*Buf++) {
            {
                int t=Tbl[tt%9];
                for (int k=0;k<4;++k) keyT[k]=keyT[k]*t+0xFBFC;
            }
            unsigned long Buf1[4];
            int t=tt&0xF;
            memcpy(Buf1,(char *)keyT+t,0x10-t);
            memcpy((char *)Buf1+0x10-t,keyT,t);
            memcpy(keyT,Buf1,16);
        }
       
        for (size_t k=0;k<4;++k) printf("%08X",keyT[k]);
        printf("/n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值