I have recently started learning x86 Assembly using the MASM language.
I am using the Udemy course "x86 Assembly Language From Ground Up" by Isreal Gbati to learn.
The code below is from a lesson in the course, (It's not code that I came up with). This function is called by main in a C program. Here it is:
#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;
}
And here is the assembly:
.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
I don't understand the following:
When we use pop
, from what I understand is kind of like using free()
in C. Correct me if I am wrong
So why are we only using pop on the EBP register? Shouldn't we pop the ECX and EDX registers as well?
I know that in a C function, pointers that are allocated memory by malloc()
need to be freed before the end of the function. The registers used are all general purpose 32 bit registers, but EBP has a special use as a stack-frame pointer. Is this why it needs to be freed?
Also, I understand that ret
is used at the end of the procedure, but how do we know that this function is returning a value at all?
To better explain my question, here is the same function written in C:
int AdderClang(int a, int b, int c)
{
return a + b + c;
}
If I only put return;
Instead of return a + b + c;
I don't know exactly what would happen but it wouldn't be the intended result. We can also tell that this C function returns an int because it tells us that in the declaration.
All of this may be explained later in the course, and I'm sure the answers to my questions are simple. However, I am trying to go slowly to make sure I understand what I am doing. And yes, I know that Assembly is not C, so comparing the 2 languages like I have may not be the right approach, but I am learning Assembly to better understand the memory management stuff in C.
Thank you all for your time!
When we use
pop
, from what I understand is kind of like usingfree()
in C.
That's not really the case. push x
copies x
on the top of the stack, and moves the stack pointer so that the new top is below the pushed value (remember that on x86, the stack grows downwards in memory). pop x
does the opposite: copies the top of the stack into x
, and then moves the stack pointer so that the new top is above the popped value (i.e. the value is removed from the stack).
Effectively, the pseudo-C equivalent would be this:
void push(int x) {
--esp;
*esp = x;
}
void pop(int *x) {
*x = *esp;
++esp;
}
Therefore, pop ebp
does not mean "clean the ebp
register", but it means "pop a value from the stack and store it in the ebp
register." Since we previously pushed ebp
, this is just reverting it as part of ending our function.
Also, I understand that
ret
is used at the end of the procedure, but how do we know that this function is returning a value at all?
You could say that in assembler world, every function is returning a value. The calling convention specifies how values are returned. On x86, the return value is stored in the eax
register. So ret
jumps to where the function was called from, and whatever is in eax
at that time is what the caller gets as the return value. That's why the function computes the sum in eax
, so that it's right where the caller will expect it.
Again, in pseudo-C, you could imagine a normal C return
statement is implemented kind of like this:
void return(int x) {
eax = x;
ret;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With