原文地址

While implementing the x64 built-in assembler for Delphi 64bit, I got to “know” the AMD64/EM64T architecture a lot more. The good thing about the x64 architecture is that it really builds on the existing instruction format and design. However, unlike the move from 16bit to 32bit where most existing instruction encodings were automatically promoted to using 32bit arguments, the x64 design takes a different approach.

One myth about the x64 instructions is that “everything’s wider.” That’s not the case. In fact many addressing modes which were taken as absolute addresses (actually offsets within a segment, but the segments are 4G in 32bit), are actually now 32bit relative offsets now. There are very few addressing modes which use a full 64bit absolute address. Most addressing modes are 32bit offsets relative to one of the 64bit registers. One interesting addressing mode that is “implied” in many instruction encodings is the notion of RIP-relative addressing. RIP, is the 64bit equivalent of the 32bit EIP, or 16bit IP, or Instruction Pointer. This represents from which address the CPU will fetch the next instruction for execution. Most hard-coded addresses within many instructions are now relative offsets from the current RIP register. This is probably the biggest thing you have to wrap your head around when moving from 32bit assembler.

Even though many instructions will implicitly use the RIP-relative addressing mode, there are some instruction addressing modes that continue to use a 32bit offset, and are not RIP-relative. This can really bite you when doing simple mechanical translations from 32bit to 64bit. These are the SIB form with a 32bit (or even 8bit) offset. What can happen is that you end up forming an address that can only address 32bits, and is thus limited to addressing items below the 4G boundary! And this is a perfectly legal instruction! To demonstration this, consider the following 32bit assembler that we’ll translate to 64bits.

  var
TestArray: array[0..255] of Word; function GetValue(Index: Integer): Word;
asm
MOV AX,[EAX * 2 + TestArray]
end;

Let’s now translate this for use in 64bit using a simple mechanical translation.

  var
TestArray: array[0..255] of Word; function GetValue(Index: Integer): Word;
asm
MOVSX RAX,ECX
MOV AX,[RAX * 2 + TestArray]
end;

Pretty straight forward, right? Not so fast there partner. Let’s see;
I know that I need to use a full 64bit register for the offset but
since Integer is still 32bits, I need to “sign-extend” it to 64bits. The
venerable MOVSX (Move with sign extension) instruction “promotes” the
signed 32bit offset to 64bits while preserving the sign. Nope, that’s
not a problem. The only thing I changed in the next instruction was EAX
to RAX, so how could that be a problem? Well, when you compile this code
you’ll get a rather strange error message:

[DCC Error] Project7.dpr(18): E2577 Assembler instruction requires a 32bit absolute address fixup which is invalid for 64bit

Huh? Remember the little note above about the SIB instruction form?
Because the RAX (or EAX in 32bit) register is being scaled (the * 2),
this instruction must use the SIB (Scale-Index-Base) instruction form.
When using the SIB form RIP isn’t considered when calculating the actual
address. Additionally, the offset encoded in the instruction can still
only be 8 or 32bits. No 64bit offsets.

In 32bit, the compiler would generate a “fixup” to ensure that the
encoding of the instruction offset field to the global “TestArray”
variable was properly “fixed up” at runtime should the image happened to
be relocated to another address. This is a 32bit absolute address. The
64bit version of this instruction, while actually a truly valid
instruction, would only have 32bits in which to place the address of
“TestArray.” The “fixup” generated would have to remain 32bit. This
could lead to creating an image that were it ever relocated above the 4G
boundary, would likely crash at best or read the wrong memory address
at worst!

Ok, so now what? There is a SIB form that we can use to work
around this problem, but it requires burning another register. The good
news is that we now have another 8 registers with which to work. So if
you have a rather complicated chunk of 32bit assembler code that burns
up all the existing usable 32bit registers, you now have another group
of registers that can help solve this problem without having to rework
the code even more. So here’s how to fix this for 64 bit:

  var
TestArray: array[0..255] of Word; function GetValue(Index: Integer): Word;
asm
MOVSX RAX,ECX
LEA R10,[TestArray]
MOV AX,[RAX * 2 + R10]
end;

Here, I used the volatile R10 register (R8 an R9 are used for
parameter passing) to get the absolute address of TestArray using the
LEA instruction. While the “address” portion of this instruction is
still 32bits, it is taken as RIP-relative. In other words, this value is
the “distance” from the next instruction to the variable TestArray in
memory. After this instruction, R10 now contains a true 64bit address of
the TestArray variable. I must still use the SIB form in the next
instruction, but instead of a hard-coded “offset” I use the value in
R10. Yes, there is still an implicit offset of 0, which uses the 8bit
offset form.

You can see that mindless, mechanical translations of assembler code
is likely to cause you some grief due to some of the subtle changes in
instruction behaviors. For this very reason, we strongly recommend you
use all Object Pascal code instead of resorting to assembler when
possible. This will not only better ensure that your code will more
likely move unchanged to other processor architectures (think ARM here
folks), but you’ll not have to worry about such assembler gotchas in the
future. If you’re using assembler code because “it’s faster,” I would
encourage you to look closely at the algorithm used. There are many
cases where the proper algorithm written in Object Pascal will yield
greater gains than a simple translation to assembler using the same
algorithm. Yes there are some things which you simply must do in
assembler (strange, off-beat calling conventions, “LOCK” instructions
for concurrency, etc…), but I would contend that many assembler
functions can be moved back to Object Pascal with little impact on
performance.

x64 assembler fun-facts(转载)的更多相关文章

  1. More x64 assembler fun-facts–new assembler directives(转载)

    原文地址 The Windows x64 ABI (Application Binary Interface) presents some new challenges for assembly pr ...

  2. puppet之自定义fact(转载)

    1.使用环境变量'FACTERLIB'创建fact 1.1.在自定义目录里面定义一个fact,列出当前系统登录的用户数 [root@agent1 ~]# vim /var/lib/puppet/kis ...

  3. X64 Deep Dive

    zhuan http://www.codemachine.com/article_x64deepdive.html X64 Deep Dive This tutorial discusses some ...

  4. Cygwin安装时,选择163的源后出错:Unable to get setup.ini from <http://mirrors.163.com/cygwin/>

    [问题] 折腾: [记录]Cygwin下把make从v3.82换成v3.81 期间,选择了163的源,结果出错: Cygwin Setup Unable to get setup.ini from & ...

  5. VSTO 学习笔记(十)Office 2010 Ribbon开发

    原文:VSTO 学习笔记(十)Office 2010 Ribbon开发 微软的Office系列办公套件从Office 2007开始首次引入了Ribbon导航菜单模式,其将一系列相关的功能集成在一个个R ...

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

    原文:VSTO 学习笔记(十一)开发Excel 2010 64位自定义公式 Excel包含很多公式,如数学.日期.文本.逻辑等公式,非常方便,可以灵活快捷的对数据进行处理,达到我们想要的效果.Exce ...

  7. 浅谈OCR之Onenote 2010

    原文:浅谈OCR之Onenote 2010 上一次我们讨论了Tesseract OCR引擎的用法,作为一款老牌的OCR引擎,目前已经开源,最新版本3.0中更是加入了中文OCR功能,再加上Google的 ...

  8. Linux gdb调试器用法全面解析

    GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主要可帮助工程师完成下面4个方面的功能: 启动程序,可以按照工程师自定义的要求随心所欲的运行程序. 让被调试的程序在工程师指定的断 ...

  9. 【VS开发】VSTO 学习笔记(十)Office 2010 Ribbon开发

    微软的Office系列办公套件从Office 2007开始首次引入了Ribbon导航菜单模式,其将一系列相关的功能集成在一个个Ribbon中,便于集中管理.操作.这种Ribbon是高度可定制的,用户可 ...

随机推荐

  1. TabLayout+ViewPager 标题不显示问题

    第一次用TabLayout+ViewPager 组合在布局中写好了三个标题预览没问题而且也设置了 app:tabIndicatorColor="@color/colorAccent" ...

  2. 会话保持及Form表单

    1,cookie技术视图views里面:def index(request): #获取请求中的cookie num = request.COOKIES.get('num') if num: num = ...

  3. Unity Shader Graph(二)Dissolve Effect

    此篇文章记录Dissolve Effect(溶解特效)的制作过程 软件环境 Unity 2018.1.2f1 Packages: Lightweight Render Pipeline 1.1.11 ...

  4. wepy打开页面首次不显示,但是数据已经有了

    page页面首次打开异步数据无法通过props传递到子组件 解决:在开发者工具关闭上传代码时自动压缩就解决了,在wepy文档里也有强调

  5. 性能测试Jmeter扩展学习-添加自定义函数

    我们在使用jmeter的时候有时候会碰到jmeter现有插件或功能也无法支持的场景,比如前端加密,此时我们就需要自己手动编写函数并导入了,下面就是手动修改并导入的过程. 首先我们需要下载jmeter源 ...

  6. [持续交付实践] pipeline使用:快速入门

    什么是pipeline 先介绍下什么是Jenkins 2.0,Jenkins 2.0的精髓是Pipeline as Code,是帮助Jenkins实现CI到CD转变的重要角色.什么是Pipeline, ...

  7. IntelliJ IDEA 导入多个maven项目

    IntelliJ IDEA的功能十分强大  我们日常开发项目会分多个maven项目 idea单个打开切换很是麻烦 下边是idea可以同时导入多个项目的方法 1.选择 FIle -> NEW -& ...

  8. Thrift 的五种工作模式

    一.thrift 共有5中工作模式,分成阻塞和非阻塞: 阻塞:TSimpleServer.TThreadPoolServer 非阻塞:TNonblockingServer.THsHaServer.TT ...

  9. Centos创建用户

    1.创建用户: adduser fish 2.用户设置密码: passwd linuxidc 3.创建文件夹: mkdir fish 4.删除文件夹 rm -rf fish 5.文件夹重命名: mv ...

  10. 安装zabbix3.4 centos7

    服务端 192.168.70.133 客户端 192.168.70.134 sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc ...