首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >指令在解码成机器语言时重复两次,

指令在解码成机器语言时重复两次,
EN

Stack Overflow用户
提问于 2020-03-24 07:25:40
回答 1查看 115关注 0票数 0

我基本上是在学习如何在X86架构中编写自己的指令,但为了做到这一点,我理解了它们是如何被解码和解释成一种低级语言的。

通过举一个简单的mov指令的例子,并使用.byte符号,我想详细了解指令是如何解码的,

我的简单代码如下:

代码语言:javascript
复制
#include <stdio.h>
#include <iostream>



int main(int argc, char const *argv[])
{
    int x{5};
    int y{0};

    // mov %%eax, %0

asm (".byte 0x8b,0x45,0xf8\n\t" //mov %1, eax
    ".byte 0x89, 0xC0\n\t"
    : "=r" (y)
    : "r" (x)

   );



   printf ("dst value : %d\n", y);

    return 0;
} 

当我使用objdump分析它是如何被分解成机器语言时,我得到了以下输出:

代码语言:javascript
复制
000000000000078a <main>:
 78a:    55                       push   %ebp
 78b:    48                       dec    %eax
 78c:    89 e5                    mov    %esp,%ebp
 78e:    48                       dec    %eax
 78f:    83 ec 20                 sub    $0x20,%esp
 792:    89 7d ec                 mov    %edi,-0x14(%ebp)
 795:    48                       dec    %eax
 796:    89 75 e0                 mov    %esi,-0x20(%ebp)
 799:    c7 45 f8 05 00 00 00     movl   $0x5,-0x8(%ebp)
 7a0:    c7 45 fc 00 00 00 00     movl   $0x0,-0x4(%ebp)
 7a7:    8b 45 f8                 mov    -0x8(%ebp),%eax
 7aa:    8b 45 f8                 mov    -0x8(%ebp),%eax
 7ad:    89 c0                    mov    %eax,%eax
 7af:    89 45 fc                 mov    %eax,-0x4(%ebp)
 7b2:    8b 45 fc                 mov    -0x4(%ebp),%eax
 7b5:    89 c6                    mov    %eax,%esi
 7b7:    48                       dec    %eax
 7b8:    8d 3d f7 00 00 00        lea    0xf7,%edi
 7be:    b8 00 00 00 00           mov    $0x0,%eax
 7c3:    e8 78 fe ff ff           call   640 <printf@plt>
 7c8:    b8 00 00 00 00           mov    $0x0,%eax
 7cd:    c9                       leave  
 7ce:    c3                       ret    

关于objdump的这个输出,为什么指令7aa: 8b 45 f8 mov -0x8(%ebp),%eax重复了两次,这背后有什么原因吗,或者我在使用.byte表示法时做错了什么?

EN

回答 1

Stack Overflow用户

发布于 2020-03-24 09:22:54

其中之一是编译器生成的,因为您要求GCC在选择寄存器时为您提供输入。这就是"r"(x)的意思。并且您在编译时禁用了优化(默认-O0),因此它实际上将x存储到内存中,然后在您的asm语句之前重新加载它。

你的代码不需要对内存的内容或者指向的地方做任何假设。

因为你使用的是89 c0 mov %eax,%eax,所以你的asm语句唯一安全的约束是输入和输出的显式寄存器约束, 强制编译器选择它。如果你在启用优化的情况下编译,你的代码完全崩溃,因为你欺骗了编译器关于你的代码实际做了什么。

代码语言:javascript
复制
// constraints that match your manually-encoded instruction
asm (".byte 0x89, 0xC0\n\t"
    : "=a" (y)
    : "a" (x)
   );

没有强制要求GCC为"m"源或"=m"目标操作数选择特定寻址模式的约束,因此您需要要求特定寄存器中的输入/输出。

如果你想对自己的mov指令进行不同于标准mov的编码,请参见which MOV instructions in the x86 are not used or the least used, and can be used for a custom MOV extension -你可能想在常规的mov操作码之前使用前缀,这样你就可以让汇编程序为你编码寄存器和寻址模式,比如.byte something; mov %1, %0

查看编译器-生成asm输出(gcc -S,而不是.o或可执行文件的反汇编)。然后您可以看到哪些指令来自asm语句,哪些指令是由GCC发出的。

如果您没有在asm模板中显式引用某些操作数,但仍希望查看编译器选取的内容,则可以在asm注释中使用它们,如下所示:

代码语言:javascript
复制
asm (".byte 0x8b,0x45,0xf8    # 0 = %0   1 = %1  \n\t"
    ".byte 0x89, 0xC0\n\t"
    : "=r" (y)
    : "r" (x)
   );

而gcc会帮你填上它,这样你就可以看到它希望你读写的是什么操作数。(带有g++ -m32 -O3Godbolt)。我把你的代码放在void foo(){}中,而不是main中,因为GCC -m32认为它需要重新对齐main顶部的堆栈。这使得代码更难理解。

代码语言:javascript
复制
# gcc-9.2 -O3 -m32 -fverbose-asm
.LC0:
        .string "dst value : %d\n"
foo():
        subl    $20, %esp       #,
        movl    $5, %eax        #, tmp84
 ## Notice that GCC hasn't set up EBP at all before it runs your asm,
 ## and hasn't stored x in memory.
 ## It only put it in a register like you asked it to.
        .byte 0x8b,0x45,0xf8   # 0 = %eax   1 = %eax    # y, tmp84
        .byte 0x89, 0xC0

        pushl   %eax  # y
        pushl   $.LC0 #
        call    printf  #
        addl    $28, %esp       #,
        ret

还要注意的是,如果你编译为64位,它可能会选择%esi作为寄存器,因为printf希望它的第二个参数在那里。因此,"a"而不是"r"约束实际上很重要。

如果你给一个必须在函数调用中存活下来的变量赋值,你可以让32位的GCC使用一个不同的寄存器;然后,GCC会选择一个调用保留的寄存器,比如EBX,而不是EAX。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60822941

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档