0
votes

The Code

The following simple x86 assembly code is listed on Wikibooks for the CDECL calling convention:

Function Definition:

_MyFunction1:
  push ebp
  mov ebp, esp
  mov eax, [ebp + 8]
  mov edx, [ebp + 12]
  add eax, edx
  pop ebp
  ret

Calling Function:

push 3
push 2
call _MyFunction1
add esp, 8

It is what would be generated from the following C code:

_cdecl int MyFunction1(int a, int b)
{
  return a + b;
}

And this function call:

 x = MyFunction1(2, 3);

The issue

Unfortunately, I can't fully wrap my head around what is happening here. Here's a list of the events as I understand them, starting from the calling function:

  • push integer 3 onto the stack
  • push integer 2 onto the stack
  • call _MyFunction1, pushing Instruction Pointer IP onto the stack
  • push base pointer ebp onto the stack, as to be able to restore it later. I suppose this is only necessary because we are moving a value into ebp in the next instruction?
  • move value of current stack pointer esp into ebp for later use in memory adressing. Why do this? Why not just use the value of esp in the next two instructions directly (i.e. [esp + 8], [esp + 12])
  • get the two integer values we pushed onto the stack and save them in eax and edx. Obviously, we do this by pointing to a memory address each. How does the processor know how "far" it has to look until it fully read e.g. the integer 3? How does it know that it's a 32 or a 16 bit integer here? How exactly do the 8 and the 12 as the stack pointer offset come to be? What do they mean, e.g. does 8 mean an 8bit offset?
  • add the two values together into eax
  • restore ebp from the stack
  • return, i.e. popping eip containing the next instruction from the stack and jumping to it
  • add 8 (again, what unit?) to the stack pointer esp
    • now, with CDECL, the caller has to clean up the stack. I reckon this is supposed to have happened with this instruction. But how is simply advancing the pointer a way of cleaning the stack up? The 3 and 2 integer values have never been popped. If anything, I'd have thought decreasing the pointer value would do the trick, and not increasing it as done here
1

1 Answers

2
votes
  • The instructions with ebp and esp are a standard way of setting up a stack frame. You’re right, there’s no real need to do it, and many compilers don’t. One reason for it is to help a debugger trace the stack frames.
  • It knows to read 4 bytes from [ebp+8] and [ebp+12] because eax and edx are 4-byte registers. That size is encoded in the instructions.
  • x86 addresses are always in bytes. So the 8 and 12 offsets added to ebp are in bytes.
  • when 8 is added to esp, that is just a number, no units. But when esp is used to access memory, it is treated as holding a byte address. So, effectively, the 8 means 8 bytes.
  • The stack grows down. Push decrements the stack pointer. So adding to esp removes things from the stack. The values 2 and 3 are still there in memory, but since they’re below the stack pointer, they’re effectively gone. (And might actually be gone in some rare circumstances, like a debugger evaluating a function call using your stack, or a Windows SEH exception handler, or on non-Windows, a signal handler.)