170 likes | 294 Views
Accessing parameters from the stack and calling functions. Outline. 1. STACK architecture - STACK Memory - PUSH and POP 2. Procedure Call - Caller and Called Functions - CALL and RET 3. Parameter Passing - In Caller Function ^ Push Parameters from right-most to left
E N D
Outline 1. STACK architecture - STACK Memory - PUSH and POP 2. Procedure Call - Caller and Called Functions - CALL and RET 3. Parameter Passing - In Caller Function ^ Push Parameters from right-most to left ^ CALL CALLED_FUNC ^ Continue processing in Caller Function - In CALLED_FUNC ^ Save Registers that may be used ^ POP parameters ^ Do processing ^ Keep the result at EAX ^ Restore saved registers ^ RET
Stack Manipulation Instructions • Hardware supported stack in memory • Stack grows down - i.e. stack grows from high address to low address • Stack manipulation instructions are used to: – place parameters on the stack before calling a procedure – save register values inside a procedure – reserve space for temporary variable inside a procedure
Memory • Each byte has a unique address in Memory • All pointers are 32 bits long Memory byte ptr[eax] X X+1 X+2 X+3 X+4 X+5 X+6 X+7 X+8 . . . word ptr[eax+4] dword ptr[eax+ebx*2] mov eax,X (X is address of variable!) mov ebx, 0x00000004
Push instruction • push REGISTER • Decrements (by 4) the stack pointer register (ESP) • Copies the value in REGISTER to the top of the stack push EDI ESP = ESP - 4 bottom of the stack (higher address) content of the stack and ESP after push
Pop instruction • pop REGISTER • Copies the value at the top of the stack to REGISTER • Increments (by 4) the stack pointer register (ESP) ESP pop EDI bottom of the stack (higher address) content of the stack and ESP before pop
Control flow instructions(subroutine call/return) • call my_procedure • Pushes the current code location (the value of IP register) onto the stack • Performs an unconditional jump to the code location indicated by ”my_procedure” • ret • Pops the code location off the stack • Puts it to IP (instruction pointer) register (thus performing unconditional jump to the retrieved location) • how are parameters passed to the procedure? • Using the stack, last parameter is stored first on the stack • how to return value? • Using EAX, put the value to be returned into the EAX register
• a procedure that calls another procedure: caller • procedure that has been called: callee
The caller should: •Before calling a procedure: • push to the stack: ECX, EDX <– caller-saved registers • pass parameters to the procedure: push to the stack in reverse order (the last parameter is pushed first) • execute call (to perform the procedure call) •After the procedure returns: • the return value is in EAX • remove parameters from the stack (to restore the stack to its state before call was performed) • pop from the stack: EDX, ECX (Note the reverse order)
The callee should : •At the beginning of procedure: • push to the stack: EBX, EDI, ESI, EBP (if you are going to use particular register) <– callee-saved registers •When the procedure is done: • the return value should be placed in EAX • pop from the stack: EBP, ESI, EDI, EBX(in the reverse order) • execute ret instruction (return to the caller)
Example 1 • Saving registers used inside the function on the stack • Restoring the saved registers from the stack • How parameters are passed on the stack • Pushed on the stack from right to left • The return address of the procedure is also on the stack!! • Accessing parameters • When inside the “firstpass” function, the stack looks like the table in the up right corner (on the next page) • To access the a location on the stack, use an instruction like mov REG, dword ptr[ESP+OFFSET] • The “ret” instruction • Return value in the “eax” register
int main(void) { int arr[] = {0,1,2,3,4}; int sum = firstpass(arr,5); printf(" The sum is = %d\n",sum); return 0; } __declspec(naked) int firstpass (int *arr, int len) { __asm { /* Save the registers that may be modified by the called function*/ push ecx push edx /* put the parameters on the stack for the add2nums function */ push edx // edx contains length push ecx // ecx contains array mov edx, dword ptr[esp + 24] /* load the len parameter from the stack */ mov ecx, dword ptr[esp + 20] /* load the arr parameter from the stack */ call add2nums /* pop the parameters of the stack after the call – to restore the stack*/ pop ecx pop edx /* restore the registers from the stack*/ pop edx pop ecx ret } }
Note: __declspec(naked) before the declaration of the function • Tells C compiler not to insert prolog/epilog code • That means that: • function parameters are not copied from stack to the parameter variables; local variables are not allocated • have to retrieve function parameters from stack • registers are not saved to stack in the beginning of the function and are not restored at the end • have to do it yourself • return statement cannot be used • you need to explicitly insert ret Assembly instruction at the end of your function • you need to return the value by putting it in EAX
__declspec(naked) int add2nums(int *array, int length) { __asm { /* Save the registers used in this function to the stack Do not save eax */ /* need not to push ebx, esi, ebp because we are not going to use those */ push edi xor eax,eax xor edi,edi /* this accesses the "array" parameter on the stack */ mov ebx, dword ptr[esp + 8] /* load the "length" parameter from the stack */ mov edx, dword ptr[esp + 12] loop1: cmp edx,edi je all_done /* copies i-th element or an array */ mov ecx, dword ptr[ebx+4*edi] /* the value returned by this function is put in eax */ add eax,ecx inc edi jmp loop1 all_done: /* restore the saved registers from the stack */ pop edi ret } }
Example 2 __declspec(naked) int add_func(int param1) { __asm{ // 1. Push register values to the stack to save PUSH ebx; PUSH ecx; // 2. Access param1 MOV ebx, dword ptr[esp+12]; MOV ecx, ebx; ADD ecx, 5; // 3. The return value should be placed in EAX MOV eax, ecx; // 4. Pop the register values from the stack POP ecx; POP ebx; // 5. Return to the caller RET; } } void main() { int result = 0; __asm{ // 1. Push register values to the stack PUSH eax; PUSH ebx; PUSH ecx; MOV ebx, 10; // 2. Pass parameter(s) to the procedure PUSH ebx; // 3. Execute call CALL add_func; // 4. Remove parameter(s) from the stack POP ebx; // 5. EAX has the return value MOV result, eax; // 6. Pop register values in reverse order POP ecx; POP ebx; POP eax; } }
Example 2 void main() { int result = 0; __asm{ // 1. Push register values to the stack PUSH eax; PUSH ebx; PUSH ecx; MOV ebx, 10; // 2. Pass parameter(s) to the procedure PUSH ebx; // 3. Execute call CALL add_func; // 4. Remove parameter(s) from the stack POP ebx; // 5. EAX has the return value MOV result, eax; // 6. Pop register values in reverse order POP ecx; POP ebx; POP eax; } }
__declspec(naked) int add_func(int param1) { __asm{ // 1. Push register values to the stack to save PUSH ebx; PUSH ecx; // 2. Access param1 MOV ebx, dword ptr[esp+12]; MOV ecx, ebx; ADD ecx, 5; // 3. The return value should be placed in EAX MOV eax, ecx; // 4. Pop the register values from the stack POP ecx; POP ebx; // 5. Return to the caller RET; } }