• VC++下编译 程序“减肥”


    在vc6 和 vs 2008下 编译 以下代码,不更改任何编译设置(vc6  40k , s2008 7k)。

    一、vc6下,Release 模式 编译处理。

    1、去掉不必要的 链接库  工程(Project)-->设置(Settings)-->链接(link)属性页-->对象库/模块(object/library modules) 去掉所有的lib。

      选择使用 MSVCRT.LIB kernel32.lib user32.lib。 可以忽略不必要的警告,比如 LINK:warning LNK4098: default  lib  "LIBC "  conflicts  with use of other libs; use /NODEFAULTLIB:library

    2、工程(Project)-->设置(Settings)-->链接(link)属性页-->在Project Options(工程 选项)

      下面的编辑框里加上一句: /ALIGN:2的n次方  这样做之后指定了程序不是驱动程序,系统可以设定的最值不同。

      

      按照处理之后,生成程序  /ALIGN:4096 (3k)  /ALIGN:16 (1.59k)  /ALIGN:128 (1.87k)。


    二、在代码中添加相应的编译设置,达到缩小程序体积的目的。

    1、无优化

     #include <windows.h>
    int main()
    {
        MessageBox(NULL,TEXT("hello!"),TEXT("hi"),MB_OK);
        return 0;
    }

    2、代码中设置

      

    #include <windows.h>
    
    //自定义加载的库
    
    #pragma comment(lib,"kernel32.lib")
    
    #pragma comment(lib,"shell32.lib")
    
    #pragma comment(lib,"msvcrt.lib")
    
    //自定义函数入口
    
    #pragma comment(linker,"/ENTRY:EntryPoint")
    
    //自定义对齐方式
    
    #pragma comment (linker,"/ALIGN:512")
    
    #pragma comment(linker,"/FILEALIGN:512")
    
    
    // 优化选项
    
    #pragma comment(linker,"/opt:nowin98")
    
    #pragma comment(linker,"/opt:ref") //清除从未引用的函数和/或数据
    
    #pragma comment (linker, "/OPT:ICF")//从链接器输出中删除冗余COMDAT
    
    // 合并区段
    
    #pragma comment(linker,"/MERGE:.rdata=.data")
    
    #pragma comment(linker,"/MERGE:.text=.data")
    
    #pragma comment(linker,"/MERGE:.reloc=.data")
    
    int WINAPI WinMain( HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int iCmdShow ) ;
    
    void EntryPoint()
    {
        ExitProcess(WinMain(GetModuleHandle(NULL), NULL,GetCommandLine(), SW_SHOWNORMAL));    
    }
    
    int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int iShowCmd )
    {
        MessageBox(NULL,TEXT("hello!"),TEXT("hi"),MB_OK);
        return 0;
    }

    经过 上述代码优化,可以将 40k的程序 缩小 为 1k 的程序。


    三、

    最简单的窗口程序:

    #include <windows.h>
    
    int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE,hPrevInstance,LPSTR szCmdLine,int iCmdShow){
    
       MessageBox(NULL,"Hello World","Hello",0);
       return 0;
    }

    在VC6.0下以Release方式编译,大小为36,864字节(36.0KB)。用CFF Explorer工具查看可执行文件,

      

    可以看出,文件导入了2个动态库,其中user32.dll仅导入了程序中使用的函数MessageBoxA,kernel32.dll导入了38个函数,显然是WinMain中调用的。

    2、把WinMain去掉,自己给定程序入口函数,重写代码如下:

     #include <windows.h>
    
    #pragma comment(linker,"/ENTRY:EntryPoint")//自定义入口函数
    
    void EntryPoint()
    
    {
    
       MessageBox(NULL,"Hello World","Hello",0);
    
       ExitProcess(0);
    
    }
    

     重新编译,文件大小为16384字节(16.0KB)。再用CFF Explorer工具查看可执行文件,结果如下图所示。

    可以看出,虽然仍导入了2个动态库,但kernel32.dll只导入了一个上述代码中调用的函数ExitProcess。导入函数的减少使文件大小减小了约20KB。

    3、在记事本中打开可执行文件,可以发现文件中存在大量的空字符。

      用CFF Explorer工具查看文件节表信息。文件的节表信息如下图所示。

    可以看出,文件中存在3个节(.text、.rdada、.data),每个节在文件中对齐后的大小和映射到内存后的大小(实际大小)分别由Raw Size和Virtual Size指出。其中.data节实际大小为0x12(18)字节,与"Hello World"和"Hello"这两个字符串的大小(含结束符)一致,却占用文件0x1000(4096)字节大小,故文件中存在大量的空字符(数值为0)。

    4、是否可以修改节的对齐方式,更改文件的大小呢?

      使用链接器/FILEALIGN和/ALIGN选项可以帮我们做到这一点。

      其中/FILEALIGN用来指定节在文件中的对齐方式,/ALIGN用来指定节加载到内存后的对齐方式,对齐字节数必须是2的幂。

      修改后代码如下:

     #include <windows.h>
    
    #pragma comment(linker,"/ENTRY:EntryPoint")//自定义入口函数
    
    #pragma comment(linker, "/FILEALIGN:16")//指定节的文件对齐方式
    
    #pragma comment(linker, "/ALIGN:16")//指定节的内存对齐方式
    
    void EntryPoint()
    
    {
       MessageBox(NULL,"Hello World","Hello",0);
    
       ExitProcess(0);
    }

      重新编译,文件大小为800字节,程序仍能正常运行。进一步该小/FILEALIGN和/ALIGN的参数,将能编译但无法运行或编译通不过。

    5、 还能减小文件的体积吗?是的。

      上述说到该文件存在三个节表和相应的三个节,使用链接器/MERGE选项可以把三个节合并,从而进一步压缩文件。

      修改后代码如下:

     #include <windows.h>
    
    #pragma comment(linker,"/ENTRY:EntryPoint")//自定义入口函数
    
    #pragma comment(linker, "/FILEALIGN:16")//指定节的文件对齐方式
    
    #pragma comment(linker, "/ALIGN:16")//指定节的内存对齐方式
    
    #pragma comment(linker, "/SECTION:.text,ERW")//指定节属性
    
    #pragma comment(linker, "/merge:.rdata=.text")//合并节.rdata到.text
    
    #pragma comment(linker, "/merge:.data=.text")//合并节.rdata到.text
    
    #pragma comment(linker, "/IGNORE:4078")//忽略4078错误
    
    void EntryPoint()
    {
       MessageBox(NULL,"Hello World","Hello",0);
    
       ExitProcess(0);
    }

    重新编译,文件大小为704字节,相对初始大小(36KB)已有了明显的变化。   

      Section信息 如下:

     6、 另外,程序优化有时也能减小文件体积:

    ‍#pragma comment(linker, "/OPT:REF")//清除从未引用的函数和/或数据

    #pragma comment(linker, "/OPT:ICF")//从链接器输出中删除冗余COMDAT

    除上述方式外,工程中的一些设置也可以很大程度减小文件体积。


    结束!

      

  • 相关阅读:
    static,const,extern,以及全局常量
    ios开发之级联菜单(两个tableView实现)
    ios开发零散知识点总结
    ios开发static关键字的理解
    ios开发清除SDWebImage图片缓存
    python
    Scapy 伪造网络数据包
    LeetCode-73. Set Matrix Zeroes
    排序算法系列:Shell 排序算法
    Android中级第十一讲之MotionEvent的分发、拦截机制分析
  • 原文地址:https://www.cnblogs.com/Bachelor/p/3873733.html
Copyright © 2020-2023  润新知