5

我为 amd64 编写了这个小汇编文件。代码的作用对于这个问题并不重要。

        .globl fib

fib:    mov %edi,%ecx
        xor %eax,%eax
        jrcxz 1f
        lea 1(%rax),%ebx

0:      add %rbx,%rax
        xchg %rax,%rbx
        loop 0b

1:      ret

然后我开始在 Solaris 和 Linux 上组装然后反汇编它。

索拉里斯

$ as -o y.o -xarch=amd64 -V y.s                            
as: Sun Compiler Common 12.1 SunOS_i386 Patch 141858-04 2009/12/08
$ dis y.o                                                  
disassembly for y.o


section .text
    0x0:                    8b cf              movl   %edi,%ecx
    0x2:                    33 c0              xorl   %eax,%eax
    0x4:                    e3 0a              jcxz   +0xa      <0x10>
    0x6:                    8d 58 01           leal   0x1(%rax),%ebx
    0x9:                    48 03 c3           addq   %rbx,%rax
    0xc:                    48 93              xchgq  %rbx,%rax
    0xe:                    e2 f9              loop   -0x7      <0x9>
    0x10:                   c3                 ret    

Linux

$ as --64 -o y.o -V y.s
GNU assembler version 2.22.90 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.22.90.20120924
$ objdump -d y.o

y.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <fib>:
   0:   89 f9                   mov    %edi,%ecx
   2:   31 c0                   xor    %eax,%eax
   4:   e3 0a                   jrcxz  10 <fib+0x10>
   6:   8d 58 01                lea    0x1(%rax),%ebx
   9:   48 01 d8                add    %rbx,%rax
   c:   48 93                   xchg   %rax,%rbx
   e:   e2 f9                   loop   9 <fib+0x9>
  10:   c3                      retq   

生成的机器码怎么不一样?太阳为生成8b cfmov %edi,%ecx而气体89 f9为相同的指令生成。这是因为在 x86 下对同一指令进行编码的各种方式,还是这两种编码真的有特别的区别?

4

2 回答 2

6

一些 x86 指令有多种编码来做同样的事情。特别是,任何作用于两个寄存器的指令都可以交换寄存器,并反转指令中的方向位。

给定的汇编器/编译器选择哪一个完全取决于工具作者选择的工具。

于 2013-07-31T14:29:53.067 回答
1

尚未指定 , 和 操作的操作movxor大小add。这会产生一些歧义。GNU 汇编器手册i386 Mnemonics提到了这一点:

如果指令没有指定后缀,那么 as 会尝试根据目标寄存器操作数(约定的最后一个)填充缺失的后缀。[...]。请注意,这与 AT&T Unix 汇编器不兼容,后者假定缺少的助记符后缀意味着长操作数大小。

这意味着 GNU 汇编器选择不同 - 它会选择 R/M 字节指定目标操作数的操作码(因为目标大小是已知/隐含的),而 AT&T 选择 R/M 字节指定源的操作码操作数(因为隐含了操作数大小)。

我已经完成了这个实验,并在你的汇编源代码中给出了明确的操作数大小,它不会改变GNU 汇编器的输出。但是,上面文档的另一部分,

可以通过可选的助记符后缀指定不同的编码选项。当从一个寄存器移动到另一个寄存器时,`.s' 后缀在编码中交换 2 个寄存器操作数。

哪个可以用;以下源代码与 GNU 一起as为我创建了您从 Solaris 获得的操作码as

.globl fib

fib:    movl.s %edi,%ecx
        xorl.s %eax,%eax
        jrcxz 1f
        leal 1(%rax),%ebx

0:      addq.s %rbx,%rax
        xchgq %rax,%rbx
        loop 0b

1:      ret
于 2013-08-05T20:42:14.997 回答