VC++编译zlib

 

1简介    1

2版本1.2.3    2

2.1 编译汇编代码    2

2.1.1 32位汇编    2

2.1.2 64位汇编    5

2.2 Visual C++ 6.0    5

2.2.1 编译配置项    6

2.2.2 ASMVASMINF    6

2.2.3 排除编译    8

2.2.4 自定义编译    8

2.3 Visual C++ 2002    9

2.3.1 编译配置项    10

2.3.2 ASMVASMINF    11

2.3.3 排除编译    11

2.3.4 嵌入汇编obj文件    11

2.4 Visual C++ 2005    12

2.4.1 编译配置项    12

2.4.2 ASMVASMINF    13

2.4.3 排除编译    13

2.4.4 嵌入汇编obj文件    13

2.5 ZLIB_WINAPI    14

3版本1.2.8    15

3.1 编译汇编代码    15

3.1.1 编译    15

3.2 使用Visual C++编译    15

3.2.1 编译前事件    16

 

 

1简介

zlib是一个用于解压缩的开源C函数库。很多项目(如libpng)会用到它。

它的下载网址如下:

http://sourceforge.net/projects/libpng/files/zlib/

本文对zlib自带的Visual C++项目进行解析。

 

 

2版本1.2.3

2.1 编译汇编代码

版本1.2.3里,为了提高解压缩效率,函数inflate_fastlongest_match用汇编代码实现了。这两个函数有三个版本,如下表所示:

C

ASM x86

ASM x64

inflate_fast

inffast.c

inffas32.asm

inffas8664.c

inffasx64.asm

longest_match

deflate.c

gvmat32c.c

gvmat32.asm

gvmat64.asm

具体情况为:

函数inflate_fastinffast.c里有一份实现它的C代码;在inffas32.asm里有一份实现它的32位汇编代码;在inffas8664.c里会调用函数inffas8664fnc,而后者在文件inffasx64.asm里由64位汇编代码实现。

函数longest_matchdeflate.c里有一份实现它的C代码;在gvmat32c.c里会调用函数longest_match_7ffflongest_match_686,而这两个函数在文件gvmat32.asm里由32位汇编代码实现;在gvmat64.asm里有一份实现它的64位汇编代码。

简而言之就是:

不用汇编代码,需要编译inffast.cdeflate.c

使用32位汇编代码,需要编译inffas32.asmgvmat32c.cgvmat32.asm

使用64位汇编代码,需要编译inffas8664.cinffasx64.asmgvmat64.asm

*.c文件在Visual C++编译时会自行处理,*.asm文件就需要特别处理了。

2.1.1 32位汇编

contrib\masmx86目录下的inffas32.asmgvmat32.asm32位的汇编代码。编译后可运行在32位的Windows上,也可运行在兼容x86指令的64Windows上。不能运行在不兼容x86指令的64Windows上(如:CPUItaniumWindows)。

进入contrib\masmx86目录,运行bld_ml32.bat即可编译inffas32.asmgvmat32.asm。文件bld_ml32.bat的内容如下:

ml /coff /Zi /c /Flgvmat32.lst gvmat32.asm

ml /coff /Zi /c /Flinffas32.lst inffas32.asm

它的含义是调用ml.exe,将gvmat32.asminffas32.asm编译生成gvmat32.objinffas32.obj

ml.exe是微软的宏汇编程序,安装了VC++2002VC++2015ml.exe也同时被安装,它所在的目录如下表所示:

vc版本

2002

C:\Program Files\Microsoft Visual Studio .NET\Vc7\bin

 

2003

C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin

 

2005

C:\Program Files\Microsoft Visual Studio 8\VC\bin

amd64\ml64.exe

x86_amd64\ml64.exe

2008

C:\Program Files\Microsoft Visual Studio 9.0\VC\bin

amd64\ml64.exe

x86_amd64\ml64.exe

2010

C:\Program Files\Microsoft Visual Studio 10.0\VC\bin

amd64\ml64.exe

x86_amd64\ml64.exe

2012

C:\Program Files\Microsoft Visual Studio 11.0\VC\bin

amd64\ml64.exe

x86_amd64\ml64.exe

2013

C:\Program Files\Microsoft Visual Studio 12.0\VC\bin

amd64_x86\ml.exe

amd64\ml64.exe

x86_amd64\ml64.exe

2015

C:\Program Files\Microsoft Visual Studio 14.0\VC\bin

amd64_x86\ml.exe

amd64\ml64.exe

x86_amd64\ml64.exe

说明:

1、从vc2005开始提供了ml64.exe,它用来编译生成64位的汇编代码;

2、从vc2013开始又提供了amd64_x86\ml.exe

3、两个ml.exe和两个ml64.exe的区别:

ml.exe                32位程序,编译生成32位的汇编代码;

amd64_x86\ml.exe        64位程序,编译生成32位的汇编代码;

amd64\ml64.exe            64位程序,编译生成64位的汇编代码;

x86_amd64\ml64.exe    32位程序,编译生成64位的汇编代码。

上述文件夹命名都是有含义的:amd64_x86前面的amd64表示ml.exe64位的,后面的x86表示编译生成的汇编代码是32位的;amd64表示amd64_amd64,即ml.exe和生成的汇编代码都是64位的。

为了能够正常编译,有两种方法:

方法一:将ml.exe从相应的目录中复制到bld_ml32.bat所在目录,运行bld_ml32.bat即可;

方法二:在bld_ml32.bat所在目录创建vc2002.bat,其内容如下:

%VSCOMNTOOLS%\..\..\Vc7\bin\ml /coff /Zi /c /Flgvmat32.lst gvmat32.asm

%VSCOMNTOOLS%\..\..\Vc7\bin\ml /coff /Zi /c /Flinffas32.lst inffas32.asm

现在运行vc2002.bat即可调用C:\Program Files\Microsoft Visual Studio .NET\Vc7\bin\ml.exe进行编译。

VSCOMNTOOLS是一个环境变量,安装vc2002后它被自动添加到系统里,如下图所示:

同理,还可以创建vc2003.bat

"%VS71COMNTOOLS%..\..\Vc7\bin\ml" /coff /Zi /c /Flgvmat32.lst gvmat32.asm

"%VS71COMNTOOLS%..\..\Vc7\bin\ml" /coff /Zi /c /Flinffas32.lst inffas32.asm

还可以创建vc2005.bat

"%VS80COMNTOOLS%..\..\VC\bin\ml" /coff /Zi /c /Flgvmat32.lst gvmat32.asm

"%VS80COMNTOOLS%..\..\VC\bin\ml" /coff /Zi /c /Flinffas32.lst inffas32.asm

经实际测试,vc2002.batvc2003.bat能够正常运行,而vc2005.bat在编译时出错。也就是说ml.exe只能使用vc2002vc2003附带的版本,再高的版本就不行了。

2.1.2 64位汇编

contrib\masmx64目录下的gvmat64.asminffasx64.asm64位的汇编代码。编译后可运行在64位的Windows上(CPUItaniumWindows例外)。

进入contrib\masmx64目录,运行bld_ml64.bat即可编译gvmat64.asminffasx64.asm。文件bld_ml64.bat的内容如下:

ml64.exe /Flinffasx64 /c /Zi inffasx64.asm

ml64.exe /Flgvmat64 /c /Zi gvmat64.asm

它的含义是调用ml64.exe,将gvmat64.asminffasx64.asm编译生成gvmat64.objinffasx64.obj

为了能够正常编译,有两种方法:

方法一:将ml64.exeC:\Program Files\Microsoft Visual Studio 8\VC\bin\x86_amd64复制到bld_ml64.bat所在目录,运行bld_ml64.bat即可;

方法二:在bld_ml64.bat所在目录创建vc2005.bat,其内容如下:

"%VS80COMNTOOLS%..\..\VC\bin\x86_amd64\ml64.exe" /Flinffasx64 /c /Zi inffasx64.asm

"%VS80COMNTOOLS%..\..\VC\bin\x86_amd64\ml64.exe" /Flgvmat64 /c /Zi gvmat64.asm

现在运行vc2005.bat即可进行编译。

2.2 Visual C++ 6.0

使用Visual C++6.0打开projects\visualc6\zlib.dsw,如下图所示。可以看到一共有三个项目:exampleminigzipzlib。前两个项目不用理会,鼠标右键单击zlib,然后单击【Set as Active Project】,设置zlib为活动项目。

2.2.1 编译配置项

项目zlib一共有8个编译配置项,如下图所示:

"Win32 DLL*"表示编译生成动态库;

"Win32 LIB*"表示编译生成静态库;

"*Release"表示编译生成Release版,发布程序时使用此版本;

"*Debug"表示编译生成Debug版,调试程序时使用此版本;

含有ASM的表示编译时使用汇编代码;

未含ASM的表示编译时不使用汇编代码。

2.2.2 ASMVASMINF

查看zlib的配置,当选中含有ASM的编译配置项时,可以看到宏ASMVASMINF被定义了。如下图所示:

ASMINF被定义,则inflate_fastC代码被禁用,以下代码节选自inffast.c

#ifndef ASMINF

void inflate_fast(strm, start)

z_streamp strm;

unsigned start;

{

... ... ...

}

#endif /* !ASMINF */

ASMV被定义,则longest_matchC代码被禁用,以下代码节选自deflate.c

#ifndef ASMV

local uInt longest_match(s, cur_match)

deflate_state *s;

IPos cur_match;

{

... ... ...

}

#endif /* ASMV */

也就是说:定义宏ASMINF的含义是启用inflate_fast的汇编代码;定义宏ASMV的含义是启用longest_match的汇编代码。

2.2.3 排除编译

Visual C++6.0无法编译64位的汇编代码,因此不用管inffas8664.cinffasx64.asmgvmat64.asm这三个文件。

zlib项目里含有与32位汇编相关的三个文件:gvmat32.asmgvmat32c.cinffas32.asm

当选中不含ASM的编译配置项时,可以看到这三个文件在编译时是被排除在外的,如下图所示:

当选中含有ASM的编译配置项时,可以看到这三个文件将参与编译,如下图所示:

2.2.4 自定义编译

当选中含有ASM的编译配置项时,可以看到gvmat32.asminffas32.asm是如何被编译的。如下图所示:

命令如下所示:

ml.exe /nologo /c /coff /Cx /Zi /Fo"$(IntDir)\$(InputName).obj" "$(InputPath)"

因为VC++6.0并不自带ml.exe,因此上述命令执行时将会失败。解决方法:将C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\bin\ml.exe复制到C:\Program Files\Microsoft Visual Studio\VC98\Bin。即将vc2002ml.exe复制到vc6的安装目录。

2.3 Visual C++ 2002

使用vc2002打开contrib\vstudio\vc7\zlibvc.sln,如下图所示。可以看到一共有五个项目:miniunzminiziptestZlibDllzlibstatzlibvc。前三个项目不用理会,zlibstatzlibvc分别是zlib的静态库、动态库项目。这里只关心zlibvc项目,鼠标右键单击zlibvc,然后单击【Set as StartUp Project】,设置zlibvc为活动项目。

2.3.1 编译配置项

项目zlib一共有5个编译配置项,如下图所示:

"Debug"编译生成Debug版,调试程序时使用此版本;

"Release"编译生成Release版,使用汇编代码;

"ReleaseAxp"编译生成Release版,不使用汇编代码;

"ReleaseWithoutAsm"编译生成Release版,不使用汇编代码。它似乎与ReleaseAxp没什么区别;

"ReleaseWithoutCrtdll"编译生成Release版,使用汇编代码。

ReleaseReleaseAxpReleaseWithoutAsm使用的C运行时库文件不是msvcr70.dll,而是crtdll.dll。笔者对此的理解是:crtdll.dllmsvcr70.dll更加普及。

ReleaseWithoutCrtdll使用的C运行时库文件是msvcr70.dll不再是crtdll.dll

虽然crtdll.dllmsvcr70.dll更加普及,但是crtdll.lib很难找到了。没有crtdll.lib的情况下,ReleaseReleaseAxpReleaseWithoutAsm无法完成链接。

2.3.2 ASMVASMINF

使用汇编代码的配置项,均定义了宏ASMVASMINF,如下图所示

2.3.3 排除编译

使用汇编代码的配置项,gvmat32c.c被排除编译。如下图所示:

未使用汇编代码的配置项,gvmat32c.c未被排除编译。如下图所示:

2.3.4 嵌入汇编obj文件

VC++6.0不同,VC2002里不再自定义编译asm文件,而是直接使用asm文件编译后的obj文件。

下图是配置项"ReleaseWithoutCrtdll"的LinkerInput选项。可以看到它将使用asm文件的编译结果文件:gvmat32.objinffas32.obj

2.4 Visual C++ 2005

使用vc2005打开contrib\vstudio\vc8\zlibvc.sln,如下图所示。可以看到一共有六个项目。前四个项目不用理会,zlibstatzlibvc分别是zlib的静态库、动态库项目。这里只关心zlibvc项目,鼠标右键单击zlibvc,然后单击【Set as StartUp Project】,设置zlibvc为活动项目。

2.4.1 编译配置项

项目zlib一共有3个编译平台,如下图所示:

Win32 编译出32位程序,可运行在32位和64Windows上;

x64 编译出64位程序,可运行在64Windows上;

Itanium编译出64位程序,只能运行在CPUItaniumWindows上。

每个编译平台又有3个配置项,如下图所示:

"Debug"编译生成Debug版,调试程序时使用此版本;

"Release"编译生成Release版,使用汇编代码;

"ReleaseWithoutAsm"编译生成Release版,不使用汇编代码。

2.4.2 ASMVASMINF

使用汇编代码的配置项,均定义了宏ASMVASMINF,如下图所示

2.4.3 排除编译

gvmat32c.cinffas8664.c可能被排除编译。如下图所示:

编译平台为Win32时,inffas8664.c始终被排除编译,gvmat32c.c视是否使用汇编代码而定;

编译平台为x64时,gvmat32c.c始终被排除编译,inffas8664.c视是否使用汇编代码而定;

编译平台为Itanium时,gvmat32c.cinffas8664.c始终被排除编译。因为这个编译平台的汇编代码压根没有实现。

2.4.4 嵌入汇编obj文件

VC2002相同,VC2005也不再自定义编译asm文件,而是直接使用asm文件编译后的obj文件。

需要注意的是:Win32x64平台使用的obj文件是不同的。下图是x64平台下,使用masmx64目录下的两个obj文件。

2.5 ZLIB_WINAPI

定义了ZLIB_WINAPI,则zlib导出的函数其调用约定全部被设置为WINAPI,即__stdcall。这么做的好处是:不是C语言的客户程序,如VB6.0也可以使用zlib动态库了。

VC++6.0的宏定义里没有发现ZLIB_WINAPI,也就是说VC++6.0编译生成的zlib动态库,其导出函数的调用约定不是__stdcall,而是__cdecl

VC2002VC2005的宏定义里均有ZLIB_WINAPI,编译生成zlib动态库后,其导出函数的调用约定是__stdcall

客户端使用zlib动态库时,应该也要定义ZLIB_WINAPI,具体代码如下:

#define ZLIB_WINAPI

#include "zlib.h"

遗憾的是,至少在libpng1.4.12项目里笔者并没有发现#include "zlib.h"之前有#define ZLIB_WINAPI

笔者认为比较好的方法应该是把#define ZLIB_WINAPI直接添加到zlib.h里,保证编译生成zlib动态库、客户端调用zlibzlib的导出函数均是__stdcall的。

 

3版本1.2.8

3.1 编译汇编代码

与版本1.2.3相比,版本1.2.8longest_match函数的32位汇编代码由gvmat32c.cgvmat32.asm两个文件合并为match686.asm这一个文件。具体如下表所示:

C

ASM x86

ASM x64

inflate_fast

inffast.c

inffas32.asm

inffas8664.c

inffasx64.asm

longest_match

deflate.c

match686.asm

gvmat64.asm

现在的情况变为:

不用汇编代码,需要编译inffast.cdeflate.c

使用32位汇编代码,需要编译inffas32.asmmatch686.asm

使用64位汇编代码,需要编译inffas8664.cinffasx64.asmgvmat64.asm

3.1.1 编译

编译32位汇编代码:将ml.exeC:\Program Files\Microsoft Visual Studio 8\VC\bin\复制到contrib\masmx86,运行bld_ml32.bat即可。

编译64位汇编代码:将ml64.exeC:\Program Files\Microsoft Visual Studio 8\VC\bin\x86_amd64复制到contrib\masmx64,运行bld_ml64.bat即可。

当然,使用vc2008vc2010vc2012vc2013vc2015ml.exeml64.exe应该也是可以的。

3.2 使用Visual C++编译

进入contrib\vstudio目录,可以发现vc9vc10vc11三个子目录,分别用vc2008vc2010vc2012编译。设置与上一章大致相同,这里就不赘述了。需要特别说明的是vc2010里的"编译前事件"。

3.2.1 编译前事件

vc2010里有"编译前事件",如下图所示:

其含义为:编译代码之前将执行"编译前事件",上图的命令如下所示:

cd ..\..\masmx64

bld_ml64.bat

其含义就是设置contrib\masmx64为当前目录,然后执行bld_ml64.bat

..\..\masmx64是相对路径,相对于项目文件(contrib\vstudio\vc10\zlibvc.vcxproj)的相对路径,即contrib\vstudio\vc10\..\..\masmx64,也就是contrib\masmx64

使用vc2010编译zlib代码,不用费劲找ml.exeml64.exe了,也不用考虑应该使用哪个版本的ml.exeml64.exe了。一切方便了很多。

posted @ 2016-12-13 10:25  hanford  阅读(3043)  评论(0编辑  收藏  举报