现在的位置: 首页 > 综合 > 正文

Win32汇编源程序的结构(2)

2013年09月05日 ⁄ 综合 ⁄ 共 2678字 ⁄ 字号 评论关闭

2、model语句

.model语句在低版本的宏汇编中已经存在,用来定义程序工作的模式,它的使用方法是:

.model 内存模式[,语言模式][,其他模式]

内存模式的定义影响最后生成的可执行文件,可执行文件的规模从小到大,可以有很多种类型,在DOS的可执行程序中,有只用到64 KB .com文件,也有大大小小的 .exe文件。到了Win32环境下,又有了可以用4 GB内存的PE格式可执行文件,编写不同类型的可执行文件要用 .model语句定义不同的参数,具体如表3.1所示。

3.1  内存模式

   

使

tiny

small

medium

compact

large

huge

flat

用来建立 .com文件,所有的代码、数据和堆栈都在同一个64 KB段内

建立代码和数据分别用一个64 KB段的 .exe文件

代码段可以有多个64 KB段,数据段只有一个64 KB

代码段只有一个64 KB段,数据段可以有多个64 KB

代码段和数据段都可以有多个64 KB

large,并且数据段中的一个数组也可以超过64 KB

Win32程序使用的模式,代码和数据段使用同一个4 GB

在前面章节中已经提到过:Windows程序运行在保护模式下,系统把每一个Win32应用程序都放到分开的虚拟地址空间中去运行,也就是说,每一个应用程序都拥有其相互独立的4 GB 地址空间,对Win32程序来说,只有一种内存模式,即flat(平坦)模式,意思是内存是很平坦地从0延伸到 4 GB,再没有64 KB段大小限制。对比一下DOSHello WorldWin32Hello World开始部分的不同,DOS程序中有这样两句:

mov     ax,data

mov     ds,ax

意思是把数据段寄存器DS指向data数据段,data数据段在前面已经用 data segment 语句定义,只要DS不重新设置,那么从此以后指令中涉及的数据默认将从data数据段中取得,所以下面的语句是从data数据段取出szHello字符串的地址后再显示:

mov     ah,9

mov     dx,offset szHello

int     21h

纵观Win32汇编的源程序,没有一处可以找到dses等段寄存器的使用,因为所有的4 GB空间用32位的寄存器全部都能访问到了,不必在头脑中随时记着当前使用的是哪个数据段,这就是“平坦”内存模式带来的好处。

如果定义了 .model flatMASM自动为各种段寄存器做了如下定义:

ASSUME  cs:FLAT, ds:FLAT, ss:FLAT, es:FLAT, fs:ERROR, gs:ERROR

也就是说,CSDSESSS段全部使用平坦模式,FSGS寄存器默认不使用,这时若在源程序中使用FSGS,在编译时会报错。如果有必要使用它们,只需在使用前用下面的语句声明一下就可以了:

assume  fs:nothing, gs:nothing 或者 assume fs:flat, gs:flat

Win32汇编中,.model语句中还应该指定语言模式,即子程序的调用方式,例子中用的是stdcall,它指出了调用子程序或Win32 API时参数传递的次序和堆栈平衡的方法,相对于stdcall,不同的语言类型还有CSysCallBASICFORTRANPASCAL,虽然各种高级语言在调用子程序时都是使用堆栈来传递参数,但它们的处理方法各有不同。要和别的语言配合,就必须指定相应的语言种类。WindowsAPI调用使用的是stdcall格式,所以在Win32汇编中没有选择,必须在 .model中加上stdcall参数。关于参数传递的具体细节,在3.4.2节中有详细的描述。

3. option语句

option语句定义的选项有很多,如option language定义和option segment定义等,在Win32汇编程序中,需要的只是定义option casemap:none,这个语句定义了程序中的变量和子程序名是否对大小写敏感,由于Win32 API中的API名称是区分大小写的,所以必须指定这个选项,否则在调用API的时候会有问题。

3.1.2  段的定义

1. 段的概念

把上面的Win32Hello World源程序中的语句归纳精简一下,再列在下面:

.386

.model flat,stdcall

option casemap:none

  <一些include语句>

.data

  <一些字符串、变量定义>

.code

  <代码>

  <开始标号>

     <其他语句>

end 开始标号

上一节讲到的选项、模式等定义并不会在编译好的可执行程序中产生什么东西,它们只是说明,而真正的数据和代码是定义在各个段中的,如上面的 .data段和 .code段,考虑到不同的数据类型,还可以有其他种类的数据段,下面是包含全部段的源程序结构:

.386

.model flat,stdcall

option casemap:none

  <一些include语句>

.stack [堆栈段的大小]

.data

  <一些初始化过的变量定义>

.data?

  <一些没有初始化过的变量定义>

.const

  <一些常量定义>

.code

  <代码>

  <开始标号>

     <其他语句>

end 开始标号

.stack.data.data?.const .code是分段伪指令,Win32中实际上只有代码和数据之分,.data.data? .const是数据段,.code是代码段,和DOS汇编不同,Win32汇编不必考虑堆栈,系统会为程序分配一个向下扩展的、足够大的段作为堆栈段,所以 .stack段定义常常被忽略。

   前面不是说过Win32环境下不用段了吗?是的,这些“段”实际上并不是DOS汇编中那种意义的段,而是内存的“分段”。上一个段的结束就是下一个段的开始,所有的“分段”合起来,包括系统使用的地址空间,就组成了整个可以寻址的4 GB空间。Win32环境的内存管理使用了80386处理器的分页机制,每个页(4 KB大小)可以自由指定属性,所以上一个4 KB可能是代码,属性是可执行但不可写,下一个4 KB就有可能是既可读也可写但不可执行的数据,再下面呢?有可能是可读不可写也不可执行的数据。Win32汇编源程序中“分段”的概念实际上是把不同类型的数据或代码归类,再放到不同属性的内存页(也就是不同的“分段”)中,这中间不涉及使用不同的段选择器。虽然使用和DOS汇编同样的 .code .data语句来定义,意思可是完全不同了!为了简单起见,在本书中还是简称“段”,读者应该注意到其中不同的含义。

抱歉!评论已关闭.