64位BASM学习随笔(一)
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去加就非常easy出问题,除非你能保证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 [rbp+20h], 4
call GetRect
end;
用来传递第一个參数的寄存器rec被返回值占用了,或者说,这样的结构返回值是作为第一个參数传递的。前面的GetRect函数实际上是以下的形式的变形:
procedure GetRect(var r: TRect; Left, Top, Right, Bottom: Integer);
在调用样例过程中,有一个伪指令.params用来自己主动分配參数内存空间。而參数Bottom也是使用栈传递的。但并没有使用压栈指令,详细原因涉及64位函数架构。比較复杂,由于今天时间不早了,明天继续。。。。。
。
64位BASM学习随笔(一)的更多相关文章
- win7+64位+Java学习基本软件安装+环境配置+eclipse(IDE)
一.下载安装JDK 1.安装包下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848520.h ...
- PHP在 win7 64位 旗舰版 报错 Call to undefined function curl_init()
代码在ubuntu下无缝运行OK 转到我的win7 64位 期间 学习机上 报错: Call to undefined function curl_init() 因为用到curl 远程抓取数据. 所以 ...
- 【OSG学习笔记之一:】OSG+VS2010+win7 64位环境搭建
虽然出生的时候,没有说过“Hello World!”,但是自从走上了编程之路,每一次输出“Hello World!”的时候,都觉得好比中了彩票大奖似的: 仔细算算,从2012年暑假到现在,经历了3年半 ...
- 关于64位Win7/Win 8 下怎么学习汇编语言
我看有许多同学用Win 7/Win 8 学习汇编,现在好多人的内存升级了都用64位系统了,但是64位W7没有自带的DEBUG和MASM. 1.首先下载DOSBOX,(下面附带地址)它的作用就是让你在6 ...
- VSTO 学习笔记(十一)开发Excel 2010 64位自定义公式
原文:VSTO 学习笔记(十一)开发Excel 2010 64位自定义公式 Excel包含很多公式,如数学.日期.文本.逻辑等公式,非常方便,可以灵活快捷的对数据进行处理,达到我们想要的效果.Exce ...
- VSTO学习笔记(三) 开发Office 2010 64位COM加载项
原文:VSTO学习笔记(三) 开发Office 2010 64位COM加载项 一.加载项简介 Office提供了多种用于扩展Office应用程序功能的模式,常见的有: 1.Office 自动化程序(A ...
- linux内核学习之三:linux中的"32位"与"64位"
在通用PC领域,不论是windows还是linux界,我们都会经常听到"32位"与"64位"的说法,类似的还有"x86"与"x86 ...
- 学习笔记23—window10 64位 python2.7 安装liblinear
最近在使用pythin,因为要使用libsvm,所以到官网去下载libsvm.官网地址为libsvm(https://www.csie.ntu.edu.tw/~cjlin/libsvm/)结果下载下来 ...
- Java串口编程学习1-环境配置(64位Win7)
最近在做zigbee的课程设计,需要Java实现对串口数据的读写操作. 网上找了很多代码,好像都比较过时了,直接拿来用没法跑通……QAQ……然后自己写个教程留底,如有不当之处还请各位路过的大神赐教. ...
随机推荐
- Maven+Docker,发布到Registry
1.配置Pom.xml <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEnc ...
- Spring Cloud (11) Hystrix-监控聚合监控
上一篇利用Hystrix Dashboard去监控断路器的Hystrix command,当我们有很多服务的时候,就需要聚合所有服务的Hystrix Dashboard数据了,这就需要Hystrix ...
- [ Nowcoder Contest 165 #D ] 合法括号序列
\(\\\) \(Description\) 键盘上有三个键,敲击效果分别是: 在输出序列尾部添加一个左括号 在输出序列尾部添加一个右括号 删除输出序列尾部的第一个元素,若输出序列为空,则什么都不发生 ...
- css文本背景样式
文本样式 文本类 text-transform:uppercase: 全部变为大写 text-transform:lowercase: 全部变为小写 text-transform:capitalize ...
- unity3d 各键值对应代码
KeyCode :KeyCode是由Event.keyCode返回的.这些直接映射到键盘上的物理键. 值 对应键 Backspace 退格键 Delete Delet ...
- jQuery——stop
为什么要停止动画? 对同一个元素,如果拥有一个以上的动画对其加以作用,那么后面的动画会被放入一个动画队列中.动画队列的动画是在其上一个动画完成以后才会执行. 控制两个参数四种情况 1.第一个参数表示后 ...
- python网络编程调用recv函数完整接收数据的三种方法
最近在使用python进行网络编程开发一个通用的tcpclient测试小工具.在使用socket进行网络编程中,如何判定对端发送一条报文是否接收完成,是进行socket网络开发必须要考虑的一个问题.这 ...
- GridView中的日期处理
数字 {0:N2} 12.36 数字 {0:N0} 13 货币 {0:c2} $12.36 货币 {0:c4} $12.3656 货币 "¥{0:N2}" ¥12.36 ...
- python--(协程 和 I/O多路复用)
python--(协程 和 I/O多路复用) 一.协程 1. >>>单线程下实现并发, 最大化线程的效率, 检测 IO 并自动切换,程序级别的任务切换, 之前多进程多线程都是系统级别 ...
- Django——2 路由分配设置 re_path正则匹配 include总路由 url传参 name使用 模板渲染render方法 模板渲染方法
Django 路由分配设置 re_path正则匹配 include总路由设置 url额外参数的传递 name的使用 模板的渲染:render方法 路由的分配中, 可以设定相应的转换器加以约束,比如 ...