64位BASM学习随笔(一)

??

Delphi的BASM一直是我最喜爱的内嵌汇编语言,同C/C++的内联汇编相比,它更方便,更具灵活性,因为C/C++的内联汇编只能是或插入式的汇编代码,函数花括号背后隐藏的函数框架,限制了汇编代码的发挥,如无论有无参数和局部变量,总是有个栈框架,更烦人的是只要你在函数中使用了esi,edi,ebx寄存器,就自动给你保存和恢复,使得这些寄存器没法在函数之间传递信息等;而Delphi的BASM可以是插入式的汇编代码,也可是完全的汇编方法,在完全的汇编方法下,怎么发挥就是自己的事了。
    Delphi XE2后就可以编写64位应用程序了,但我一直没时间试一下64位的BASM代码有无变化,春节前后抽时间研究了一下,发现64位的BASM变化还是很大的,不仅仅是简单的寄存器位数更长的问题,而是整个BASM方法的框架(不只是BASM,而是整个64位计算机应用程序框架)都发生了根本性的变化,从编写程序代码的角度而言,这种变化似乎比以前16位程序向32位程序进阶时更大。
    一、64位BASM不支持插入汇编代码,只能写纯BASM方法。如下面的代码是错误的:

function Test(v: Integer): Integer;
var
  i: Integer;
begin
  i := v * v;
  asm
    mov   eax, i
  end;
end;

只能这样写:

function Test(v: Integer): Integer;
asm
  mov   eax, v
  imul  eax, eax
end;

二、64位BASM只支持一种调用方式,不论你标明stdcall,pascal,cdecl与否,调用方式都是寄存器参数传递,清栈由调用方法负责(与cdecl类似)。这种变化不只是BASM,好像整个64位程序都是这样,只有一种调用方式。stdcall,pascal,cdecl调用说明只是对32位程序代码的兼容,不会报错。
    三、64位BASM数据类型除指针类型由32位进阶64位外,其它无变化,最常用的Integer、LongWord长度仍然是四字节(前几天据网上传,Delphi XE8的LongWord会改为64位)。
    四、64位寄存器的变化。
    1、寄存器长度增了一倍,由32位进阶为64位,eax,ebx,ecx,edx,esi,edi,ebp,esp等变为rax,rbx,rcx,rdx,rsi,rdi,rbp,rsp,当然e字头的寄存器照样能作32位寄存器使用,同样16位,8位寄存器也可使用。

这里有一点是要特别注意的,而64位程序中,默认的操作数长度仍然是32位,只有默认地址长度才是64位,这点同16位进阶32位有些不同,操作寄存器的低16位不会不会影响其高16位,而64位下,改变寄存器的低32位,会导致寄存器的高32位清零。如下面代码:

mov   eax, edx
    shl   rax, 32
    mov   eax, edx

其本意是在rax中形成2个并行的32位数字,结果第三句代码导致rax高32位清零,使用or  eax, edx也是一样会导致高32位清零,只有or  rax, rdx才是正确的(当然要保证rdx的高32位是零)。

另外,操作64位地址也要注意,虽然64位程序的地址默认是64位,但使用类似[esi+edx]的32位地址操作也不会报错,同样操作结果好像也是正确的,但我认为,应尽量避免这类代码,因为目前看来似乎结果是正确的,主要是因为目前应用程序能操作的数据长度没超过32位,如果以后随着硬件的变化,系统也会发生变化,一旦应用能使用的数据量大于32位,你的代码就有问题了。还有地址的增减也是这样,无论32位还是64位代码,整数长度还是32位,如果增减地址的操作数是32位的,最好转换为64位,除非你能保证其是正数,如下面过程:

function Test(v: Integer): Integer;
asm

push  rbx
    mov   eax, v
    add   rbx, rax

.......

pop   rbx

end;

参数v是32整数,直接用地址rbx去加就很容易出问题,除非你能保证v不为负数。这里可以用cdqe或者使用movsxd  rax, v进行扩展。

压栈push和出栈pop语句的操作数只能是64位,如push  eax是错误的。
    2、通用寄存器多了r8 - r15等8个寄存器,在BASM方法内,r8 - r11可直接使用,而R12 - R15在使用时同rsi,rdi,rbx一样,应注意保存和恢复。r8 - r15是64位形式,也可表示为32位,16位和8位,如r8,r8d,r8w,r8b分别为64位,32位,16位和8位,而且64位坏境下,rsi, rdi,rbp,rsp也可以用sil,dil,bpl和spl操作低8位。r8 - r15不像rax,rbx,rcx,rdx几个通用寄存器有高低8位寄存器,而且以前通用寄存器的高8位不能和r8 - r15寄存器使用在同一语句中,如mov  ah, r8b; mov  bh, [r8]等都是错误的。
    3、XMM寄存器也多了8个,依次为xmm8 - xmm15,不过,xmm6 - xmm15在使用时应注意保存和恢复(xmm6,xmm7在32位代码中是不需要保护的)。保存SSE寄存器很麻烦,它不能像常规寄存器使用压栈和出栈语句,但BASM中有一个savenv伪指令很方便(我不知道这是BASM独有的,还是其它汇编共有的),如.savenv  xmm7(注意savenv前有个点),Delphi编译器就会在BASM方法中加上保护和恢复xmm7寄存器的语句。
    四、64位BASM方法默认参数传递的变化。无论是32位还是64位BASM方法,默认都使用寄存器传递参数,不同的是32位BASM是前3个参数非浮点数参数使用寄存器传递,从形参左边开始,依次是ecx,edx,ecx,浮点数参数和三个以上非浮点数参数使用栈传递;而64位BASM是前4个参数使用寄存器传递,如果是非浮点数参数,从形参左边开始,依次为rcx,rdx,r8,r9,浮点数则使用xmm0 - xmm3传递。在32位方法中,前3个参数中间夹着浮点数时,寄存器参数会顺延,如方法:

function Test(v1, v2: Integer; v3: double; v4: Integer): double;

asm

fld     v3

end;

寄存器使用:eax=v1,edx=v2,[ebp+8]=v3,ecx=v4,这里ecx是顺延的。而64位方法中不顺延,不论是否浮点数,寄存器的位置是不改变的,如下面的方法:

procedure Test(v1, v2: Integer; v3: double; v4, v5: Int64);

asm

end;

寄存器使用:ecx=v1,edx=v2,xmm2=v3,r9=v4,[rsp+28h](无框架)或者[rsp+30h](有框架)=v5,如果v3是非浮点数,寄存器应该是r8,这里用xmm2表示浮点数参数,r8寄存器没有顺延,同样v3没有使用xmm0,而是严格按位置参数位置安排xmm2。至于参数v5则是使用栈传递的,至于其栈中偏移位置是28h或30h,而不是8和16的原因后面在专门谈及。

五、64位BASM返回值的变化。64位的BASM方法的返回值也有些变换,常规的返回值还是eax或rax,最明显的是可以用rax返回64位整数类型,而不必使用edx:eax返回了。浮点数的返回值是变化最大的,如前面32位Test函数代码是使用fld  v3通过80x87寄存器来传递的,这句代码用在64位BASM函数中就是错误的,因为64位函数返回浮点数不再使用80x87寄存器,而是使用SSE寄存器xmm0,所以64位代码只能是类似movaps  xmm0, v3或者直接movaps  xmm0, xmm2。

还有一种特殊的返回值,如下面的方法,返回一个TRect类型:

function GetRect(Left, Top, Right, Bottom: Integer): TRect;
asm

// 32位代码:

push  ebx

mov   ebx, Result // 或者mov  ebx, [ebp+8]

mov   [ebx].TRect.Left, eax

mov   [ebx].TRect.Top, edx

mov   [ebx].TRect.Right, ecx

mov   eax, Bottom // 或者mov  eax, [ebp+12]

mov   [ebx].TRect.Bottom, eax

pop    ebx

// 64位代码:
    mov   [rcx].TRect.Left, edx
    mov   [rcx].TRect.Top, r8d
    mov   [rcx].TRect.Right, r9d
    mov   eax, Bottom
    mov   [rcx].TRect.Bottom, eax

end;

通过对比可以看出,对于这种结构形式的返回值,如果是小于或等于通用寄存器的偶数字节结构使用eax或rax返回,这一点32位和64位代码都是相同的,而其它结构返回值就不同了,32位代码是作为最后一个栈参数传递结构地址的,而64位代码则是使用第一个参数来传递结构地址的,具体说是用rcx来传递的,下面是一个调用BASM过程例子:

procedure Test;
var
  r: TRect;
asm
    .params 5

// r := GetRect(1, 2, 3, 4)
    lea   rcx, r
    mov   edx, 1
    mov   r8d, 2
    mov   r9d, 3
    mov   [ebp+20h], 4
    call  GetRect
end;

用来传递第一个参数的寄存器rec被返回值占用了,或者说,这种结构返回值是作为第一个参数传递的,前面的GetRect函数实际上是下面的形式的变形:

procedure GetRect(var r: TRect; Left, Top, Right, Bottom: Integer);

在调用例子过程中,有一个伪指令.params用来自动分配参数内存空间,而参数Bottom也是使用栈传递的,但并没有使用压栈指令,具体原因涉及64位函数架构,比较复杂,由于今天时间不早了,明天继续。。。。。。

时间: 2024-12-18 16:52:43

64位BASM学习随笔(一)的相关文章

win7+64位+Java学习基本软件安装+环境配置+eclipse(IDE)

一.下载安装JDK 1.安装包下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848520.html 根据需要进行下载,我的电脑是win7+64位,所以选择版本:jdk-9.0.1_windows-x64_bin.exe 2.双击安装包 ,点击下一步,建议不要装在C盘,我一般习惯性将所有软件装在E盘下面,首先是安装JDK: 3.继续点击下一步,安装完jdk-9.0.1,会自动安装jre-9.

【OSG学习笔记之一:】OSG+VS2010+win7 64位环境搭建

虽然出生的时候,没有说过“Hello World!”,但是自从走上了编程之路,每一次输出“Hello World!”的时候,都觉得好比中了彩票大奖似的: 仔细算算,从2012年暑假到现在,经历了3年半的光阴,这段时间,不仅是知识.阅历.成长获得最大的一段,也是人生的一大转折点.在这期间,得以继续深造学习自己的专业,虽然踏入这行的时候,也是调剂来的,但是我用这3年的时间,恶补自己7年的不足:2015年,发生了三件大事,我毕业了,我工作了,我有女票了(三件事的重要程度递增):吃得苦中苦,方位人上人,

关于64位W7下怎么学习汇编语言的一些心得!

出处:http://tieba.baidu.com/p/2277546332 1.首先下载DOSBOX,它的作用就是让你在64位下使用32.16位的软件.如果不使用DOSBOX就会出现程序不兼容的对话框. 开始安装: 双击打开DOSBox软件,按步骤进行安装,安装目录任选,不过由于C盘是系统盘,所以最好安装在其他盘里:安装成功后打开DOSBox, 我们就可以看到一个非常熟悉的界面了,和电脑里的命令提示符DOS窗口差不多: (注意:由于它的两个窗口是在一起作用的,所以使用的时候不要关掉其中的任一个

VSTO 学习笔记(十一)开发Excel 2010 64位自定义公式

原文:VSTO 学习笔记(十一)开发Excel 2010 64位自定义公式 Excel包含很多公式,如数学.日期.文本.逻辑等公式,非常方便,可以灵活快捷的对数据进行处理,达到我们想要的效果.Excel的内置公式虽然强大,但是在ERP等系统中,往往需要进行很多业务上的计算,如收发存台账.指定年.月.部门的消耗查询等,都是需要从数据库中查询的,且和业务紧密相连.这时仅仅依靠Excel的内置公式就不够了,需要添加自定义的公式来进行这些业务计算. 测试代码下载 本系列所有测试代码均在Visual St

VSTO学习笔记(三) 开发Office 2010 64位COM加载项

原文:VSTO学习笔记(三) 开发Office 2010 64位COM加载项 一.加载项简介 Office提供了多种用于扩展Office应用程序功能的模式,常见的有: 1.Office 自动化程序(Automation Executables) 2.Office加载项(COM or Excel Add-In) 3.Office文档代码或模板(Code Behind an Office Document or Template) 4.Office 智能标签(Smart Tags) 本次我们将学习使

关于64位Win7/Win 8 下怎么学习汇编语言

我看有许多同学用Win 7/Win 8 学习汇编,现在好多人的内存升级了都用64位系统了,但是64位W7没有自带的DEBUG和MASM. 1.首先下载DOSBOX,(下面附带地址)它的作用就是让你在64位下使用32.16位的软件.如果不使用DOSBOX就会出现程序不兼容的对话框. 安装完DOSBox后,我们就能运行汇编后的程序了 打开DOSBox后,我们可以看到Z : \ >,这是DOSBox里的虚拟盘,我们可以采用mount命令将其转变到自己的D盘: mount c e:\masm (PS:这

OpenCV学习笔记(二十):Win8.1 64位+OpenCV 2.4.9+Python2.7.9配置

OpenCV提供了Python接口,主要特性包括: 提供与OpenCV 2.x中最新的C++接口极为相似的Python接口,并且包括C++中不包括的C接口 提供对OpenCV 2.x中所有主要部件的绑定:CxCORE (almost complete), CxFLANN (complete), Cv (complete), CvAux (C++ part almost complete, C part in progress), CvVidSurv (complete), HighGui (co

linux学习(六) Ubuntu14.04(64位)配置android环境报错(...adb": error=2, 没有那个文件或目录)的解决方案

1.问题描述: 最近测试android环境的时候,运行程序,提示以下错误信息: Cannot run program error=2, 没有那个文件或目录' while attempting to get adb. 运行环境是 unbuntu 14.04 64位系统; 2.问题原因: 在网上搜索答案的时候,找到了问题的原因:因为系统是64位的,而Android sdk只有32bit的程序.所以就存在这不兼容的问题,缺少一些sdk运行的库. 3.解决方案: 很多软件只有32位的,还有很多比较依赖3

学习笔记-虚拟机-Vmware-环境搭建,解决64位不支持的问题

首先下载好官方安装包后 点击安装,选择安装方式:典型或者自定义 选择典型,设置好安装路径. 安装完成后新建虚拟机. 选择CentOS镜像文件,创建虚拟机. 这时候报出一个错误: 您已经配置此虚拟机使用64位客户操作系统.然而,64位操作系统不可用.此主机具有虚拟化支持能力的,但虚拟化技术被禁用.这通常是因为虚拟化技术已经在BIOS /固件设置或禁用或主机没有权限改变此设置. 请:(1) 验证 BIOS/固件设置 VT 启用和禁用 'trusted execution.'(2) 如果更改了这些 B