Assembly 如何在汇编器x86函数调用中传递参数?

Assembly 如何在汇编器x86函数调用中传递参数?,assembly,x86,nasm,calling-convention,Assembly,X86,Nasm,Calling Convention,看看这个汇编代码。它是为32位x86设计的,将由nasm编译 ... my_function: pop %eax ... ret main: push 0x08 call my_function 我很久以前就知道,我们可以使用堆栈在主程序和函数之间传递参数 我希望eax包含0x08,但这是错误的,我无法解释原因 如何获取函数参数?首先,如果您希望与平台上的其他语言或库进行接口,请务必阅读该平台定义

看看这个汇编代码。它是为32位x86设计的,将由nasm编译

   ...
   my_function:
        pop %eax
        ...
        ret


   main:
       push 0x08
       call my_function
我很久以前就知道,我们可以使用堆栈在主程序和函数之间传递参数

我希望eax包含0x08,但这是错误的,我无法解释原因


如何获取函数参数?

首先,如果您希望与平台上的其他语言或库进行接口,请务必阅读该平台定义的接口。可以使用多种调用机制

在您的例子中,
call
指令将返回地址推送到堆栈上。您可以通过使用一些算术和
esp
来访问参数。由于您使用的是
eax
,因此我将假定为32位代码(以及32位堆栈宽度)。我使用的是intel语法,因为我可以在不查找任何内容的情况下编写:

my_function:
    mov eax, [esp+4]    ; Move the contents of ESP+4 into EAX
                        ; ESP should be pointing at the 32 bit RIP.
                        ; ESP+4 should be the pushed parameter.
    ...
    ret


main:
   push 0x08
   call my_function
在您的评论中,您会问,关于这个答案,这是否表示内存泄漏。答案是“否”。原因是调用方负责清理添加到堆栈中的任何内容。基于已编写的其他注释的更完整示例可能如下所示:

my_function:
    push ebp            ; Store the current stack frame
    mov  ebp, esp       ; Preserve ESP into EBP for argument references
    and  esp, 0xfffffff0; Align the stack to allow library calls
    mov  eax, [ebp+8]   ; Move the contents of EBP+8 into EAX
                        ; [EBP] should be the saved 32 bit EBP.
                        ; [EBP+4] should be the 32 bit EIP (return address).
                        ; [EBP+8] should be the pushed parameter.
    ...                 ; Do lots of cool stuff
    mov  esp, ebp       ; Restore the stack and ebp
    pop  ebp
    ret


main:
   push 0x08
   call my_function
   pop ebx              ; Clean up the stack
请注意,当我们将堆栈(如果您不确定为什么会发生这种情况,那么在研究平台的调用标准时,您会很快找到它)与16字节边界对齐时,我们甚至不会试图计算出esp的变化有多大。由于
ebp
将充当我们的“书签”,因此我们可以让
esp
移动以进行对齐,或者无需考虑局部变量分配

在函数尾声中,我们将
ebp
移回
esp
,这将在调用函数时将
esp
恢复到其原始值,从而清除所有已发生的本地分配和对齐操作。最后,我们将ebp从堆栈中弹出,将返回地址指针作为函数中堆栈上的最终值。我们现在回来

回来后,我们用砰的一声打扫干净


或者,可以使用指定堆栈上要释放的字节数的返回来清理堆栈(例如
ret4
)。这完全取决于您的呼叫标准是指定调用者清理还是指定被调用者清理。

请参见下面的说明:-

[BITS 32]

%include "nagoa+.inc"

%include "cfunctions.txt"

[SEGMENT .DATA USE32]

    ret_value   db  "I am the Return Value",0
    localvar    db  "My Local Variable Value",0

[SEGMENT .BSS USE32]
    arg_param   resb    160


[SEGMENT .TEXT USE32]

my_function:
    ;save arguments/parameters as this esp+ space will be destroyed by system function calls
        mov eax,[esp+4]

    ;enjoy local variables for processing
    ;enter 4,0 
        mov dword [esp-4],localvar

        call printf,`Argument/Parmeter=%s  and Local Variable=%s`,eax,[esp-4]
        add esp,12
    ;leave

    ;fill-up return values
        mov eax,ret_value

    ret 
    ;ret 4

..start:
    call puts,`Enter Argument/Parmeter`
    add esp,4
    call gets,arg_param
    add esp,4

    push arg_param
    CALL my_function

    call printf,`Return Value From Called Function=%s`,eax
    add esp,4

call exit, 0

除了David answers,这是另一个例子

push 0       ; fourth parameter
push 4       ; third parameter
push 4       ; second parameter
push [eax]   ; first parameter
call printf
< C >或C++ >

somefunction(first,second,third,fourth);

看看他的另一个。问题是调用会将返回地址放在堆栈上,这就是您将在EAX中找到的代码。谢谢David,但在您的情况下,您永远不会调用pop。你不认为这是内存泄漏吗?@Bob5421:通常由调用者(在
调用my_函数
之后)来执行
弹出操作
。这只是如何访问内部函数中的stack参数的最短示例,它不是一个完整的示例。所以是的,它会“泄漏”。我已经分解了一些elf可执行文件。有时我看到它们直接改变了esp值。我认为pop/push与pop/push是一样的(只是它们不获取或添加值)。我也看到了ebp。我假设堆栈元素的实际内存地址是ebp+esp?esp直接更改为在堆栈上分配空间。EBP通常用于在调用函数时跟踪原始EBP值,以便轻松清理堆栈。EBP的值通常作为函数前导的一部分存储在堆栈上,因此在正常使用中,您应该期望在+8处看到堆栈上的内容。此时,您已经描述了一个典型的“堆栈框架”。不过,您要知道的最大一点是,在堆栈上传递内容并不典型。现在,大多数接口都喜欢使用寄存器进行传递,这会更快,因为不需要推送和POP。这就是为什么我建议您查找您将要工作的平台的调用标准!存储到
[esp-4]
是不安全的:它可以(至少在理论上)异步关闭,除非您对32位代码使用带有红色区域的自定义系统/调用约定。另外,这是什么汇编程序,在这里,
调用
的额外操作数神奇地被推送给您?(以及从字符串文本为您创建的字符串常量?)此外,最终的
printf
有2个参数,但只
添加esp,4
而不是8。@Peter Cordes:这是NASM源代码。
call
宏(带有
%define call
,这意味着大写的
call
不会调用宏)确实实现了将参数推送到调用目标后指定的堆栈,并从推送地址的字符串文本创建常量。