Rob*_*Man 5 c x86 memory-management 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;
}
Run Code Online (Sandbox Code Playgroud)
这是程序集:
.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
Run Code Online (Sandbox Code Playgroud)
我不理解以下内容:
当我们使用时pop,据我了解有点像free()在C语言中使用。如果我错了,请纠正我
那么,为什么只在EBP寄存器上使用pop?我们不应该同时弹出ECX和EDX寄存器吗?
我知道在C函数中,malloc()需要在函数结束之前释放通过分配内存的指针。所使用的寄存器都是通用的32位寄存器,但是EBP作为堆栈帧指针有特殊用途。这就是为什么需要释放它的原因?
另外,我知道ret在过程的末尾使用了它,但是我们如何知道该函数完全返回一个值?
为了更好地解释我的问题,这是用C编写的相同函数:
int AdderClang(int a, int b, int c)
{
return a + b + c;
}
Run Code Online (Sandbox Code Playgroud)
如果只放return;而不是return a + b + c;我不知道会发生什么,但这不是预期的结果。我们还可以说这个C函数返回一个int,因为它在声明中告诉了我们。
所有这些都可以在本课程的后面部分进行解释,我敢肯定,对我的问题的回答很简单。但是,我试图慢慢走,以确保我了解自己在做什么。是的,我知道Assembly不是C,因此比较两种语言可能不是正确的方法,但是我正在学习Assembly以更好地理解C中的内存管理知识。
谢谢大家的时间!
当我们使用时
pop,据我了解,有点像free()在C中使用。
事实并非如此。push x复制x在堆栈顶部,然后移动堆栈指针,使新的顶部低于所压入的值(请记住,在x86上,堆栈在内存中向下增长)。pop x进行相反的操作:将堆栈的顶部复制到中x,然后移动堆栈指针,使新的顶部位于弹出的值之上(即,该值已从堆栈中删除)。
实际上,伪C等效项是这样的:
void push(int x) {
--esp;
*esp = x;
}
void pop(int *x) {
*x = *esp;
++esp;
}
Run Code Online (Sandbox Code Playgroud)
因此,pop ebp这并不意味着“清除ebp寄存器”,而是意味着“从堆栈中弹出一个值并将其存储在ebp寄存器中”。由于我们先前已推送ebp,因此这只是将其还原为结束功能的一部分。
另外,我知道
ret在过程的末尾使用了它,但是我们如何知道该函数完全返回一个值?
您可以说在汇编世界中,每个函数都返回一个值。调用约定指定如何返回值。在x86上,返回值存储在eax寄存器中。因此ret跳转到调用该函数的eax位置,那时调用者将获得什么作为返回值。这就是函数在中计算总和的原因eax,因此恰好在调用者期望的位置。
再次,在伪C中,您可以想象正常的C return语句是这样实现的:
void return(int x) {
eax = x;
ret;
}
Run Code Online (Sandbox Code Playgroud)