windows下32位汇编语言学习笔记 第二章  准备编程环境

Win32可执行文件的生成过程:

Win32下的可执行文件就是常说的PE文件。对于汇编来说,生成的步骤如下:
1.编辑.asm格式的汇编源代码.
2.用ml.exe 将.asm源码生成.obj文件.
3.用link.exe 将.obj .rc(资源文件,用于windows窗口界面定义的一种格式)连接成.exe文件.

C API编程生成步骤和上面几乎一样。
1.编辑.C格式的源代码。
2.用cl.exe(vs2008编译器)或者gcc.exe(GCC编译器),生成.ojb .o文件.
3.用link.exe 连接成.exe 文件。

其中的.rc是可选的,如果只是简单的创建一个窗口,使用默认的界面样式,不需要编辑RC文件。

因为我看这本书是结合"windows程序设计"和"windows核心编程"一起阅读,所以在相关章节,我会结合后两本书并把要点写明。


开发环境的建立:


若要做其事,先要利其器,选好了工具学习使用起来就会更方便。

asm文件编辑器的选择
我使用的是Scite,一个开源的免费的,支持windows,linux的编辑器,支持N种种语言的语法高亮。
可自定义菜单功能。可设置不同语言的F1帮助,支持.chm .hlp .col(msdn帮助格式) F1键打开。
占用内存少,打开文件速度快,优点数不胜数。以前一直是用NotePad++的,后来发现其作者居然在主页上公开抵制奥运发表政治看法,就再不使用。

简单说下配置方法,详细的百度,google下自己搜索。
1.配置编译
Scite 里默认的编译快捷键是F7,打开asm.properties配置文件照下面修改
我这里下载了个MASMPlus,是国人开发的一个汇编IDE,里面就包含了所需要的连接编译命令和windows .inc .lib 库,其实这个编辑器也不错。

#MASM路径设置,
asmpath=D:\Program Files\MASMPlus
下面这个语句就是绑定快捷键盘F7的编译命令
/I 参数是必须的,指定include 的路径。其他的具体看书上的说明
command.compile.$(file.patterns.asm)=$(asmpath)\bin\ml.exe /I "$(asmpath)\include" /I "$(FileDir)" /c /coff /nologo /Fo $(FileName).obj $(FileNameExt)

#ctrl+ F7 连接命令
/LIBPATH 这个必须指定到Lib目录
make.command=link
command.build.*.asm=$(asmpath)\bin\link.exe /LIBPATH:"$(asmpath)\Lib" /LIBPATH:"$(asmpath)\Exlib" /LIBPATH:"$(FileDir)" /SUBSYSTEM:WINDOWS /nologo /OUT:$(FileName).exe $(FileName).obj *.res

里面用到了很多Scite自带的变量必须 $(FileDir)文件绝对路径 $(FileName)文件名 $(FileNameExt)文件名带扩展名,等等,也可以自定义,比如上面的asmpath就是我自定的路径。

配置好以后,就可F7编译,ctrl+F7连接了。绝对不建议使用radasm这样的复杂IDE环境,功能是很强,但是大部分的功能初学者根本用不到。不要迷失在IDE环境中....


关于link
link有个参数 :BASE ,意思是把程序装入指定的地址。上一章的时候说过0x00001000-0x00400000 是DOS兼容的内存区域,不写dos程序这块地址浪费了可惜,看看能不能把装入地址设置到这里。

试下能不能装到BASE:0x00010000地址,因为分配粒度是64K装入地址必须是分配粒度的倍数,这里刚好就是64K,也是整个内存空间的第一个分配粒度地址,这里编译的就是第二章的Test.asm

使用MASMPlus自带的 link 5.12.8078 连接后提示:错误的windows 95地址...
LINK : warning LNK4096: /BASE value "10000" is invalid for Windows 95; image may not run 

使用vs2008自带的 link 9.00.30729.01 
这次成功了,运行test.exe后查看装入地址,用Process Explorer导出看看,果然载入基址就是0x10000这个地方,后面的0x4000是大小.
Test.exe D:\My document\book\汇编\罗云彬WINDOWS环境下32位汇编语言程序设计第2版(清晰+源码)\附书光盘里面的内容打包\Chapter02\Test\Test.exe  0x10000  0x4000

看来微软也意识到,现在不会再有人开发dos兼容程序了,0x400000以前这块内存地址也不用留给16位程序了,编译器也不再提示。
但如果不指定,默认还是装入到0x00400000这个地址,也许大家都习惯了,微软也不想随便就把首选装入地址改掉。

但是有个问题,是不是首选地址往前移了0x3F0000就说明应用程序又多了3M多的内存寻址空间可以使用呢?

在试下往后最大能加载到什么地址
我们知道,0x7FFF0000后面就是64K禁区,这里就是用户空间和内核空间的分界线。

先试下0x7FFE0000 ,可以连接,但是运行就提示
---------------------------
Test.exe - 应用程序错误
---------------------------
应用程序正常初始化(0xc0000018)失败。请单击“确定”,终止应用程序。 
---------------------------
确定   
---------------------------

这个初始化代码我搜索vs2008的所有头文件,在ntstatus.h里查到下面的描述:
// {Conflicting Address Range}
// The specified address range conflicts with the address space.   指定的地址范围与地址空间冲突?(我英文很差)
//
#define STATUS_CONFLICTING_ADDRESSES     ((NTSTATUS)0xC0000018L)

实际上到了这个地址,就已经不让使用了。虽然可以连接,但是运行就出错,用了不该用的地址。


上一章说过,虽然64K禁区开始地址是0x7FFF0000但实际上从0x7FFE1000开始最后这段内存空间已经被标记成Reserve状态的PAGENOACCESS,所以从0x7FFE开始后面的128K 内存寻址空间就已经不能用了
64K禁区增加到了128K,呵呵

最大的加载地址只能是0x7FFD0000,这是最后一个可利用的分配粒度边界。


综上所述,win32 程序可以使用的内存寻址空间是:0x00010000-0x7FFD0000 这段区域,也就2G多一点。 传说中4G的寻址空间实际能自己支配的只用一半,呵呵,郁闷不?


特别注意下,link.exe可以使用vs2008自带的最新的,ml.exe可不行,编译就出错,提示windows.inc里的一段数据初始化失败,可能是配套的inc不是新版的缘故。
写到这里我也发现MASMPlus自带的inc和lib都比较老,从这里开始就直接使用RadASM里的inc和lib.ml.exe 和link.exe 就直接使用vs2008里自带的。


/STACK参数  格式:/STACK:reserve[,commit]
第一个reserve参数的数值是指保留的栈大小,第二个commit参数值指定提交的大小。

比如,用/stack,0x100000 指定保留1MB的栈大小,这是在内存空间里保留出的堆栈大小,并不影响物理内存大小,因为还没提交。

/stack,0x100000,0x100000 则既保留的同时也提交这块内存,现在的物理内存已经被映射到这个内存空间,可以看见进程里test.exe的内存大小已经从700多K长到1.7MB了

提交和保留的
保留只是在内存空间地址上分配出指定的大小,准备使用,但还不能使用。可以通过 Process Explorer 查看test.exe进程的虚拟内存增大。
提交是把保留出来的内存空间地址和物理内存做映射,提交以后才能正真使用这块地址。这是后物理内存占用也增大了。

第一章曾今不确定默认栈大小,现在可以确定下来了,就是1MB,msdn里关于link的/stack帮助里明确写到"The default stack size is 1 MB"

那么栈保留在用户空间的什么位置?
msdn里没有说明,也可能我没查到。我设置了不同的stack大小,用Cheat Engine 查看内存空间情况(打开进程,点memory view 然后按 Ctrl + R)发现保留的地址总是在
0x00030000 这个内存空间地址上开始。这里有2个问题

1.为啥从这里开始 注:后面是纯粹我自己的理解(没找到相关参考),不对的地方请纠正。
内存空间是一块连续的区域,栈是由系统维护的,一旦指定了大小程序中无法更改。系统的原则是尽量往内存的高地址处放,如果放到中间的位置,有可能会导致出现内存空间出现碎片,
导致下一次提交保留内存比较大时还需要系统移动这些在内存空间中间的保留段,为给新分配的空间腾地方,这个过曾会造成频繁的和页面文件交换数据导致系统变慢。

2.0x00001000-0x00400000 DOS兼容内存区 真浪费了么
明显没有,从栈的空间地址的分配上就看出,0x00030000刚开就在这个区域里,这4M的空间其他的地方怎么用的暂且不说,起码默认1MB栈空间就用是这里的内存空间。
如果把载入首先地址地设置成0x00010000 情况如何呢?
这时候栈的内存区域起始地址就往下增加了在0x00040000 这个地址上。

所以担心系统会浪费进程仅可用的2G虚拟内存根本是没有必要的,反而系统会充分帮助你的应用程序使用这块空间。



RC编辑器

做复杂的界面就需要个RC资源编辑器了,放按钮,设置图片等等,如果纯写代码就比较麻烦了,可以下载个ResEdit-1.4.4.19 资源编辑器,本书例子里的RC资源可以直接用这个打开编辑。
可以再配置脚本里设置一个编译RC的快捷键

makepath=E:\compile\VC2008
command.name.1.*.rc=RC
command.1.*.rc=$(makepath)\bin\rc.exe /i "$(asmpath)\include" $(FileName).rc

这样用scite 打开按Ctrl +1 就可以编译成.ras资源文件了。


关于MAKE
在你的项目很复杂的情况下,每次ml后再 link就比较繁琐,就可以通过编辑makefile 使用make帮助自动编译。现在学习阶段代码都很小,没有复杂的文件关系,所以用不着这个东西。
主要是使用make必须写makefile文件,不能用参数解决,我现在还不知道如何用scite的配置自动生成模版文件,还是暂时不用了。

帮助文档

既然是windows32编程,用到的也都是win32API所以,msdn是必不可少的,这里推荐大家去http://www.skygz.com/ 下载msdn1.5绿色版,里面还有vs2008绿色版。
要养成看帮助的习惯,而不要把代码自动完成当做参考.

本章看起来比较轻松,重要的是自己手动搭配一个.asm 汇编的编程环境,还是那句话,不要用复杂的东西,高级的功能一个也用不到。大家可以用我推荐的Scite自己配置,
也可以使用MASMPlus。



星期六, 五月 02, 2009