如何在汇编程序 x86 函数调用中传递参数?

Posted

技术标签:

【中文标题】如何在汇编程序 x86 函数调用中传递参数?【英文标题】:How can I pass parameters in assembler x86 function call? 【发布时间】:2016-10-24 19:36:51 【问题描述】:

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

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


   main:
       push 0x08
       call my_function

我很久以前就知道,我们可以使用堆栈在主程序和函数之间传递参数。

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

我应该如何获取我的函数参数?

【问题讨论】:

看看他的另一个answer。问题是 CALL 将返回地址放在堆栈上,这就是您将在 EAX 中使用您的代码找到的地址。 【参考方案1】:

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

在您的情况下,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

在您的 cmets 中,您询问此答案是否表示内存泄漏。答案是不。”原因是 调用者 负责清理它添加到堆栈中的任何内容。基于已编写的其他 cmets 的更完整示例可能如下所示:

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 恢复为其调用函数时的原始值,从而清除已发生的任何本地分配和对齐操作。最后,我们将pop ebp 移出堆栈,将返回地址指针作为函数内堆栈上的最终值。我们现在回来了。

回来后,我们用砰的一声清理干净。

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

【讨论】:

谢谢大卫,但在你的情况下,你永远不会打电话给流行音乐。你不觉得是内存泄漏吗? @Bob5421:通常由调用者(在call my_function 之后)执行pop。这只是如何在函数内部访问堆栈参数的最短示例,它不是一个完整的示例。所以是的,它会“泄漏”。 我已经反汇编了一些 elf 可执行文件。有时我看到他们直接改变了 esp 值。我想它与 pop/push 相同(除了它们不获取或添加值)。我也看到了ebp。我想堆栈元素的真实内存地址在 ebp+esp ? ESP 直接改成在栈上分配空间。 EBP 通常用于在调用函数时跟踪原始 EBP 值,以便轻松清理堆栈。 EBP 的值通常作为函数前导码的一部分存储到堆栈中,因此在正常使用中,您应该期望在 +8 处看到堆栈上的内容。至此,您已经描述了一个典型的“堆栈框架”。不过,您要知道的最重要的事情是,在堆栈上传递东西是典型的。如今,大多数接口都喜欢使用寄存器传递,因为没有推送和弹出,所以速度更快...... ...必需。这就是为什么我建议您查找您将要工作的平台的调用标准!【参考方案2】:

除了大卫的回答,这是另一个例子

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);

【讨论】:

【参考方案3】:

解释见下文:-

[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

【讨论】:

存储到[esp-4] 是不安全的:它可以(至少在理论上)被异步破坏,除非您使用自定义系统/调用约定和 32 位的红色区域代码。此外,这是什么汇编程序,call 的额外操作数会神奇地为您推送? (还有从字符串文字为您创建的字符串常量?)另外,最后的 printf 有 2 个参数,但只有 add esp,4 没有 8 个。 @Peter Cordes:这是 uses the macro collection nagoa+.inc 的 NASM 源。 call_ 宏(%define call call_ 意味着大写的CALL 不会调用该宏)确实实现了将参数推送到调用目的地之后指定的堆栈,并从其地址被推送的字符串文字创建常量。

以上是关于如何在汇编程序 x86 函数调用中传递参数?的主要内容,如果未能解决你的问题,请参考以下文章

python怎么传参

x86汇编语言

C/C++学习笔记 关于调用约定

如何在 x86 程序集中按值传递结构

Python的函数参数传递

python的参数传递是值传递还是引用传递??