Assembly 返回x86汇编语言中的值

Assembly 返回x86汇编语言中的值,assembly,x86,Assembly,X86,我目前正在学习x86汇编语言,这门课刚开始,我在理解堆栈在特定情况下的工作方式时遇到了一些问题 假设我有以下代码: double(entier n) { return n + n; } 我尝试将其转换为x86代码,结果是: push ebp #save old pointer stack mov ebp, esp #put new pointer stack mov ebx, dword[ebp + 8] #get argument n and put it in ebx add ebx, d

我目前正在学习x86汇编语言,这门课刚开始,我在理解堆栈在特定情况下的工作方式时遇到了一些问题

假设我有以下代码:

double(entier n) { return n + n; }
我尝试将其转换为x86代码,结果是:

push ebp #save old pointer stack
mov ebp, esp #put new pointer stack
mov ebx, dword[ebp + 8] #get argument n and put it in ebx
add ebx, dword[ebp + 8] #add n to ebx 
但后来我完全被阻塞了,无法找到如何返回ebx的值。我在internet上找到了以下解决方案:

mov [ebp + 12], ebx
pop ebp
ret
pop ebp
ret

我不明白它是怎么工作的。ebp+12不是第二个参数的值吗?就我而言,没有。pop用于移动esp指针,但在这种情况下为什么需要2个pop和2个返回?这只是为了删除函数声明期间使用的值吗?

由于您似乎对此感到非常困惑,让我向您演示如何执行此操作:

double: push ebp           ; establish...
        mov ebp, esp       ; ...stack frame

        mov eax, [ebp + 8] ; load argument from stack into eax
        add eax, eax       ; add it to itself

        leave              ; tear down the stack frame
        ret                ; return to the caller
注意,我为寄存器选择了eax而不是ebx。这有两个原因:


eax是调用者保存的寄存器,意味着调用者必须注意在需要时保留其值,而ebx是被调用者保存的寄存器,意味着被调用者(即double)必须保留其值。如果我们想使用ebx,我们必须保存并恢复它的旧值。如果我们使用调用方保存的寄存器,比如eax,我们可以避免这种努力。 eax是根据惯例找到返回值的寄存器。调用者将使用eax的值作为返回值

在几乎所有x86的调用约定中,返回值都是返回时在eax中找到的值。因此,通过将加法的结果放在eax中,我们不必做任何额外的工作来设置返回值

对于以后的问题,我建议您在打开优化的情况下参考C编译器的输出。C编译器非常擅长生成程序集,很少出错。在类似UNIX的系统(如Linux)上,可以使用-S选项生成汇编代码。对于gcc,我建议您键入

gcc -m32 -O3 -masm=intel -fno-omit-frame-pointer -S -o- source.c
以英特尔语法将source.c的汇编代码打印到终端,这是您似乎正在使用的汇编风格


有关更多详细信息,请参见。

因为您似乎对此感到非常困惑,所以让我向您演示如何操作:

double: push ebp           ; establish...
        mov ebp, esp       ; ...stack frame

        mov eax, [ebp + 8] ; load argument from stack into eax
        add eax, eax       ; add it to itself

        leave              ; tear down the stack frame
        ret                ; return to the caller
注意,我为寄存器选择了eax而不是ebx。这有两个原因:


eax是调用者保存的寄存器,意味着调用者必须注意在需要时保留其值,而ebx是被调用者保存的寄存器,意味着被调用者(即double)必须保留其值。如果我们想使用ebx,我们必须保存并恢复它的旧值。如果我们使用调用方保存的寄存器,比如eax,我们可以避免这种努力。 eax是根据惯例找到返回值的寄存器。调用者将使用eax的值作为返回值

在几乎所有x86的调用约定中,返回值都是返回时在eax中找到的值。因此,通过将加法的结果放在eax中,我们不必做任何额外的工作来设置返回值

对于以后的问题,我建议您在打开优化的情况下参考C编译器的输出。C编译器非常擅长生成程序集,很少出错。在类似UNIX的系统(如Linux)上,可以使用-S选项生成汇编代码。对于gcc,我建议您键入

gcc -m32 -O3 -masm=intel -fno-omit-frame-pointer -S -o- source.c
以英特尔语法将source.c的汇编代码打印到终端,这是您似乎正在使用的汇编风格


有关更多详细信息,请参见。

什么是实体?在这种情况下,调用约定是在eax中返回一个返回值。你应该在谷歌上搜索一下,为你运行的任何平台编写汇编程序,这样你就可以获得基本知识。你们有两个ret语句,它们按顺序被pop ebp分开,我认为这是一个印刷错误,它是一个整数。对不起,忘了正确编辑它。如果您使用的是C语言,那么它应该是int。您还没有说您使用的是哪种高级语言,或者您使用的是哪种平台。它可以带来不同。看起来像C,但我不想假设。特别是因为你使用C关键字double作为函数名,这通常不是一个好主意。返回值进入eax。当函数返回时,eax中的任何内容都是返回值。@LoloGiordano我无法告诉您在internet上找到的随机代码段的作用。这似乎是荒谬和错误的。我建议你至少从一本教程或书开始学习,而不是从互联网上随机找到的代码片段。什么是完整的?在这种情况下,调用约定是在eax中返回一个返回值。你应该在谷歌上搜索一下,为你运行的任何平台编写汇编程序,这样你就可以获得基本知识。你们有两个ret语句,它们按顺序被pop ebp分开,我认为这是一个印刷错误,它是一个整数。对不起,忘了正确编辑它。如果你是C,那么它应该是
你还没有说你在使用什么高级语言,或者你在什么平台上。它可以带来不同。看起来像C,但我不想假设。特别是因为你使用C关键字double作为函数名,这通常不是一个好主意。返回值进入eax。当函数返回时,eax中的任何内容都是返回值。@LoloGiordano我无法告诉您在internet上找到的随机代码段的作用。这似乎是荒谬和错误的。我建议你至少从一本教程或书开始学习,而不是从互联网上随机找到的代码片段;更容易思考,您可以从调用者的角度思考这两个术语:函数将对我的寄存器做什么,或者从被调用者的角度思考我如何使用寄存器。也避免暗示任何人需要做任何保存任何东西。如果我们想使用ebx,我们必须保存并恢复它的旧值。如果我们使用被叫方保存的寄存器,我们可以避免这种努力。我想你的意思是说来电者在这里保存了注册。我喜欢这个术语:通话中断vs.通话保留;更容易思考,您可以从调用者的角度思考这两个术语:函数将对我的寄存器做什么,或者从被调用者的角度思考我如何使用寄存器。也避免暗示任何人需要做任何保存任何东西。如果我们想使用ebx,我们必须保存并恢复它的旧值。如果我们使用被叫方保存的寄存器,我们可以避免这种努力。我想你的意思是说来电者把登记簿保存在这里了。