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位函数架构。比較复杂,由于今天时间不早了,明天继续。。。。。

转载于:https://www.cnblogs.com/jhcelue/p/6909475.html

64位BASM学习随笔(一)相关推荐

  1. vba monthview控件64位_VBA学习

    下面是我推出的各套教程介绍(推荐应用:32位office,13版本).第一和第四是初级,第三是中级偏下,第二是中级偏上,第五套六套是高级,第一二三五六是PDF+程序文件,第四套是视频+PDF+程序文件 ...

  2. sublime text 64位_Python学习第一步 - 用Sublime搭建Python运行环境

    Python现在老火了,学起来也比较容易上手,但是对于一个新人来讲,最难的莫过于搭建环境了.我在开始学习的时候,把sublime作为代码编写和运行的工具,主要是喜欢他的简洁,所以在这里推荐给大家. O ...

  3. Redhat 6.2(64位) 及 Oracle 11.2.0.4(64位)安装随笔

    利用安装的空隙,先凭着记忆写下一点内容 一.安装Redhat6.2 因为有此前的经验,还算顺利,但也磕磕绊绊 另外,明白了为什么要关闭selinux,为的是消除很多限制.. 但是至此,还有一个问题没有 ...

  4. 无法启动python 因为计算机中丢失,win7 64位 python启动报错:无法启动此程序,因为计算机中丢失api-ms-win-crt-process-l1-1-0.dll...

    安装python3.7,安装成功后,在cmd窗口输入python检查是否安装成功,报错:无法启动此程序,因为计算机中丢失api-ms-win-crt-process-l1-1-0.dll 在网上查询了 ...

  5. linux内核学习之三:linux中的32位与64位

    linux内核学习之三:linux中的"32位"与"64位" 在通用PC领域,不论是windows还是linux界,我们都会经常听到"32位" ...

  6. android 键编译,Android 音视频学习系列 (四) 一键编译 32/64 位 FFmpeg 4.2.2

    前言 2020/5/20 增加了硬件解码编译脚本 编译环境 Centos + NDK20b + FFmpeg4.2.2 + Android-21/16 2020/4/26 更新了编译 64 位脚本 编 ...

  7. 深入学习理解java虚拟机--1.win10 下构建64位 openJDK8

    基于之前面试很多次被问到jvm运行原理及调优问题,以及jvm本身是技能提升不可逾越的一道坎,于是决定深入学习jvm,不久买了周志明的<深入理解java虚拟机--jvm高级特性与最佳实践>一 ...

  8. AutoCAD2007中文版【64位】下载地址 仅供学习交流

    AutoCAD2007中文版[64位]下载地址 仅供学习交流 链接:https://pan.baidu.com/s/1OXouQbYzOCLb9ErG6mwWPg 提取码:87wk 复制这段内容后打开 ...

  9. 树莓派学习::qt5.10.1交叉编译【带opengl ES2】到非官方64位系统

    qt5.10.1交叉编译[带opengl ES2](armv8 64位) 本次交叉编译是树莓派学习::qt5交叉编译(armv8 64位).的升级版,这次编译带opengl Es2的qt,原因是为了可 ...

最新文章

  1. JVM 内存模型:运行时常量池
  2. python如何导入matlab数据,python学习-python到matlab数据的传输
  3. ASP.NET Core 反向代理部署知多少
  4. LeetCode——866.回文素数
  5. raspberry pi_Raspberry Pi在单板计算机,新的符合FCC规则的路由器芯片等众多清单上排名第一
  6. Spring MVC小DEMO
  7. python_列表_常用操作
  8. (转)5分钟APIG实战: 使用Rust语言快速构建API能力开放
  9. 对比学习系列论文CPC(一)——CPC概览
  10. 全局空间自相关算法:Join Count
  11. Tomcat日志分割
  12. Idea中使用maven命令
  13. wps中将二维表转换为一维表
  14. Mac常见问题:无线键盘失灵!
  15. 电脑引导,电脑常见开机引导错误的解决方法
  16. 推荐《天才在左,疯子在右》
  17. 目标检测 YOLOv5 - ncnn模型的加密 C++实现封装库和Android调用库示例
  18. PAT-Head of Hangs
  19. PHP处理iso8583报文
  20. 怎样学习jQuery,jQuery学习教程

热门文章

  1. 关于Navicat 连接mysql报11001错误
  2. logback指定不同包下的日志输出到不同的文件
  3. Java类型转换工具类(十六进制—bytes互转、十进制—十六进制互转,String—Double互转)
  4. intellij idea 代码错误设置 Error 提示颜色修改
  5. Double值保留两位小数的四种方法
  6. 运行MYSQL数据库命令时connetion Timeout expired异常问题
  7. rabbitmq channel对象的方法
  8. Vue中watch的使用
  9. nginx 日志格式设置 和 负载均衡下 获取真实ip
  10. 【数学】稀疏图的随机游走问题