如何在MASM中使用弹出和后退

时间:2019-07-18 08:10:48

标签: c memory-management x86 masm cpu-registers

我最近开始使用MASM语言学习x86 Assembly。

我正在使用Isreal Gbati的Udemy课程“从头开始的x86汇编语言”来学习。

以下代码摘自该课程中的一课(不是我想出的代码)。此函数在C程序中由main调用。在这里:

#include <stdio.h>
#include <stdlib.h>

extern int AdderASM(int a, int b, int c);

int main(void)
{
    int a = 17;
    int b = 11;
    int c = 14;
    int sum = AdderASM(a, b, c);

    printf("A = %d\n", a);
    printf("B = %d\n", b);
    printf("C = %d\n", c);

    printf("SUM FROM ASSEMBLY FUNCTION = %d\n", sum);

    return 0;
}

这是程序集:

.386
.model flat, c

.code

AdderASM    PROC

            PUSH EBP             
            MOV EBP, ESP        

            MOV EAX, [EBP+8]     
            MOV ECX, [EBP+12]   
            MOV EDX, [EBP+16]   

            ADD EAX, ECX        
            ADD EAX, EDX        

            POP EBP

            RET

AdderASM    ENDP
            END

我不理解以下内容:

当我们使用pop时,据我所知有点像在C语言中使用free()。如果我错了,请纠正我

那为什么我们只在EBP寄存器上使用pop?我们不应该同时弹出ECX和EDX寄存器吗?

我知道在C函数中,需要在函数结束之前释放由malloc()分配的内存的指针。所使用的寄存器都是通用的32位寄存器,但是EBP作为堆栈帧指针有特殊用途。这就是为什么需要释放它的原因?

此外,我知道在该过程的末尾使用了ret,但是我们怎么知道该函数完全返回一个值?

为了更好地解释我的问题,这是用C语言编写的相同函数:

int AdderClang(int a, int b, int c)
{
    return a + b + c;
}

如果我只放置return;而不是return a + b + c;,我不知道会发生什么,但这不是预期的结果。我们还可以说这个C函数返回一个int,因为它在声明中告诉了我们。

所有这些都可以在本课程的后面部分进行解释,我敢肯定,对我的问题的回答很简单。但是,我试图慢慢走,以确保我了解自己在做什么。是的,我知道Assembly不是C,因此比较两种语言可能不是正确的方法,但是我正在学习Assembly以更好地理解C中的内存管理知识。

谢谢大家的光临!

1 个答案:

答案 0 :(得分:6)

  

当我们使用export class GitHub{ constructor(){ this.clientID='6ea9567c0f22d48fb20e'; this.clientSecret='a4ec6e6b2040ddd5d197079014f8a4e0fb7fe839'; this.repos_count=5; this.repos_sort='created: asc'; } async getUser(user){ let response = await fetch(`https://api.github.com/users/${user}?clientID=${this.clientID}&clientSecret=${this.clientSecret}`); let repoResponse = await fetch(`https://api.github.com/users/${user}/repos?per_page=${this.repos_count}&sort=${this.repos_sort}?clientID=${this.clientID}&clientSecret=${this.clientSecret}`); let parsedJson = await response.json(); let reposJson = await repoResponse.json(); return { data:parsedJson, reposJson } } } 时,据我所知有点像在C语言中使用pop

事实并非如此。 free()push x复制到堆栈的顶部,并移动堆栈指针,使新的顶部位于所推入的值以下(请记住,在x86上,堆栈在内存中向下增长)。 x的作用相反:将堆栈的顶部复制到pop x中,然后移动堆栈指针,使新的顶部位于弹出的值之上(即,从堆栈中删除该值)。 / p>

有效地,伪C等效项是这样:

x

因此,void push(int x) { --esp; *esp = x; } void pop(int *x) { *x = *esp; ++esp; } 并不意味着“清理pop ebp寄存器”,而是意味着“从堆栈中弹出一个值并将其存储在ebp寄存器中”。由于我们先前按下过ebp,因此这只是在结束功能时将其还原。


  

此外,我知道在该过程的末尾使用了ebp,但是我们怎么知道该函数完全返回一个值?

您可以说在汇编世界中,每个函数都返回一个值。调用约定指定如何返回值。在x86上,返回值存储在ret寄存器中。因此,eax跳转到调用函数的位置,此时ret中的内容就是调用者作为返回值得到的。这就是为什么函数在eax中计算总和的原因,因此恰好在调用者期望的位置。

同样,在伪C中,您可以想象正常的C eax语句是这样实现的:

return