【问题标题】:How can I pass parameters in assembler x86 function call?如何在汇编程序 x86 函数调用中传递参数?
【发布时间】: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 中使用您的代码找到的地址。

标签: assembly x86 nasm calling-convention


【解决方案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 不会调用该宏)确实实现了将参数推送到调用目的地之后指定的堆栈,并从其地址被推送的字符串文字创建常量。
    猜你喜欢
    • 1970-01-01
    • 2012-11-06
    • 2016-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-03
    • 1970-01-01
    相关资源
    最近更新 更多