Function Calling Convention(基于棧的函數(shù)調(diào)用慣例)2012-10-15 17:35:24| 分類: 匯編 | 標簽:c++ 函數(shù)調(diào)用 匯編 操作系統(tǒng) 編程 |字號 訂閱 http://www.cs./~evans/cs216/guides/x86.html#callingCalling ConventionTo allow separate programmers to share code and develop libraries for use by many programs, and to simplify the use of subroutines in general, programmers typically adopt a common calling convention. The calling convention is a protocol about how to call and return from routines. For example, given a set of calling convention rules, a programmer need not examine the definition of a subroutine to determine how parameters should be passed to that subroutine. Furthermore, given a set of calling convention rules, high-level language compilers can be made to follow the rules, thus allowing hand-coded assembly language routines and high-level language routines to call one another.In practice, many calling conventions are possible. We will use the widely used C language calling convention. Following this convention will allow you to write assembly language subroutines that are safely callable from C (and C++) code, and will also enable you to call C library functions from your assembly language code. The C calling convention is based heavily on the use of the hardware-supported stack. It is based on the push, pop, call, and ret instructions. Subroutine parameters are passed on the stack. Registers are saved on the stack, and local variables used by subroutines are placed in memory on the stack. The vast majority of high-level procedural languages implemented on most processors have used similar calling conventions. The calling convention is broken into two sets of rules. The first set of rules is employed by the caller of the subroutine, and the second set of rules is observed by the writer of the subroutine (the callee). It should be emphasized that mistakes in the observance of these rules quickly result in fatal program errors since the stack will be left in an inconsistent state; thus meticulous care should be used when implementing the call convention in your own subroutines. Stack during Subroutine CallA good way to visualize the operation of the calling convention is to draw the contents of the nearby region of the stack during subroutine execution. The image above depicts the contents of the stack during the execution of a subroutine with three parameters and three local variables. The cells depicted in the stack are 32-bit wide memory locations, thus the memory addresses of the cells are 4 bytes apart. The first parameter resides at an offset of 8 bytes from the base pointer. Above the parameters on the stack (and below the base pointer), the call instruction placed the return address, thus leading to an extra 4 bytes of offset from the base pointer to the first parameter. When the ret instruction is used to return from the subroutine, it will jump to the return address stored on the stack. Caller RulesTo make a subrouting call, the caller should:
The code below shows a function call that follows the caller rules. The caller is calling a function _myFunc that takes three integer parameters. First parameter is in EAX, the second parameter is the constant 216; the third parameter is in memory location var. Note that after the call returns, the caller cleans up the stack using the add instruction. We have 12 bytes (3 parameters * 4 bytes each) on the stack, and the stack grows down. Thus, to get rid of the parameters, we can simply add 12 to the stack pointer.push [var] ; Push last parameter firstpush 216 ; Push the second parameterpush eax ; Push first parameter lastcall _myFunc ; Call the function (assume C naming)add esp, 12 The result produced by _myFunc is now available for use in the register EAX. The values of the caller-saved registers (ECX and EDX), may have been changed. If the caller uses them after the call, it would have needed to save them on the stack before the call and restore them after it. Callee RulesThe definition of the subroutine should adhere to the following rules at the beginning of the subroutine:
Example The subroutine prologue performs the standard actions of saving a snapshot of the stack pointer in EBP (the base pointer), allocating local variables by decrementing the stack pointer, and saving register values on the stack..486.MODEL FLAT.CODE PUBLIC _myFunc_myFunc PROC ; Subroutine Prologuepush ebp ; Save the old base pointer value.mov ebp, esp ; Set the new base pointer value.sub esp, 4 ; Make room for one 4-byte local variable.push edi ; Save the values of registers that the functionpush esi ; will modify. This function uses EDI and ESI. ; (no need to save EBX, EBP, or ESP) ; Subroutine Bodymov eax, [ebp+8] ; Move value of parameter 1 into EAXmov esi, [ebp+12] ; Move value of parameter 2 into ESImov edi, [ebp+16] ; Move value of parameter 3 into EDImov [ebp-4], edi ; Move EDI into the local variableadd [ebp-4], esi ; Add ESI into the local variableadd eax, [ebp-4] ; Add the contents of the local variable; into EAX (final result) ; Subroutine Epiloguepop esi ; Recover register valuespop edimov esp, ebp ; Deallocate local variablespop ebp ; Restore the caller's base pointer valueret _myFuncENDPEND In the body of the subroutine we can see the use of the base pointer. Both parameters and local variables are located at constant offsets from the base pointer for the duration of the subroutines execution. In particular, we notice that since parameters were placed onto the stack before the subroutine was called, they are always located below the base pointer (i.e. at higher addresses) on the stack. The first parameter to the subroutine can always be found at memory location [EBP+8], the second at [EBP+12], the third at [EBP+16]. Similarly, since local variables are allocated after the base pointer is set, they always reside above the base pointer (i.e. at lower addresses) on the stack. In particular, the first local variable is always located at [EBP-4], the second at [EBP-8], and so on. This conventional use of the base pointer allows us to quickly identify the use of local variables and parameters within a function body. The function epilogue is basically a mirror image of the function prologue. The caller's register values are recovered from the stack, the local variables are deallocated by resetting the stack pointer, the caller's base pointer value is recovered, and the ret instruction is used to return to the appropriate code location in the caller. |
|