Please indicate the source if you want to reprint: http://blog.csdn.net/gaoxiangnumber1.
3.7 Procedures
3.7.1 Stack Frame Structure
IA32 programs make use of the program stack to support procedure calls. The machine uses the stack to pass procedure arguments, to store return information, to save registers for later restoration, and for local storage.
The portion of the stack allocated for a single procedure call is called a stack frame.
The topmost stack frame is delimited by two pointers, with register %ebp serving as the frame pointer, and register %esp serving as the stack pointer.
The stack pointer can move while the procedure is executing, so most information is accessed relative to the frame pointer.
Suppose procedure P (the caller) calls procedure Q (the callee). The arguments to Q are contained within the stack frame for P. When P calls Q, the return address within P where the program should resume execution when it returns from Q is pushed onto the stack, forming the end of P’s stack frame.
The stack frame for Q starts with the saved value of the frame pointer (a copy of register %ebp), followed by copies of any other saved register values.
Procedure Q also uses the stack for any local variables that cannot be stored in registers. This can occur for the following reasons:
. There are not enough registers to hold all of the local data.
. Some of the local variables are arrays or structures and hence must be accessed by array or structure references.
. The address operator ‘&’ is applied to a local variable, and hence we must be able to generate an address for it.
Q uses the stack frame for storing arguments to any procedures it calls. In Figure 3.21, within the called procedure, the first argument is positioned at offset 8 relative to %ebp, and the remaining arguments (assuming their data types require no more than 4 bytes) are stored in successive 4-byte blocks, so that argument i is at offset 4 + 4i relative to %ebp. Larger arguments (such as structures and larger numeric formats) require larger regions on the stack.
3.7.2 Transferring Control
The instructions supporting procedure calls and returns:
The call instruction has a target where the called procedure starts. In assembly code, the target of a direct call is given as a label, while the target of an indirect call is given by a * followed by an operand specifier using one of the formats described in Section 3.4.1.
A call instruction push a return address on the stack and jump to the start of the called procedure. The return address is the address of the instruction immediately following the call in the program, so that execution will resume at this location when the called procedure returns.
The ret instruction pops an address off the stack and jumps to this location. The proper use of this instruction is to have prepared the stack so that the stack pointer points to the place where the preceding call instruction stored its return address.
In above code, there is a call instruction with address 0x080483dc in main calls function sum. This is shown in Figure 3.22(a), with the indicated values for the stack pointer %esp and the program counter %eip. The effect of the call is to push the return address 0x080483e1 onto the stack and to jump to the first instruction in function sum, at address 0x08048394 (Figure 3.22(b)). The execution of function sum continues until it hits the ret instruction at address 0x080483a4. This instruction pops the value 0x080483e1 from the stack and jumps to this address, resuming the execution of main just after the call instruction in sum (Figure 3.22(c)).
The leave instruction can be used to prepare the stack for returning. It is equivalent to the following code sequence:
Register %eax is used for returning the value from any function that returns an integer or pointer.
3.7.3 Register Usage Conventions
The set of program registers are shared by all of the procedures. Although only one procedure can be active at a given time, we must make sure that when one procedure (the caller) calls another (the callee), the callee does not overwrite some register value that the caller planned to use later.
IA32 adopts a uniform set of conventions for register usage that must be respected by all procedures, including those in program libraries.
Registers %eax, %edx, and %ecx are classified as caller-save registers. When procedure Q is called by P, it can overwrite these registers without destroying any data required by P.
Registers %ebx, %esi, and %edi are classified as callee-save registers. This means that Q must save the values of any of these registers on the stack before overwriting them, and restore them before returning, because P (or some higher-level procedure) may need these values for its future computations.
Registers %ebp and %esp must be maintained according to the conventions described here.
int P(int x)
{
int y = x*x;
int z = Q(y);
return y + z;
}
Procedure P computes y before calling Q, but it must also ensure that the value of y is available after Q returns. It can do this by one of two means:
. It can store the value of y in its own stack frame before calling Q; when Q returns, procedure P can then retrieve the value of y from the stack. In other words, P, the caller, saves the value.
. It can store the value of y in a callee-save register. If Q, or any procedure called by Q, wants to use this register, it must save the register value in its stack frame and restore the value before it returns (in other words, the callee saves the value). When Q returns to P, the value of y will be in the callee-save register, either because the register was never altered or because it was saved and restored.
Either convention can be made to work, as long as there is agreement as to which function is responsible for saving which value.
3.7.4 Procedure Example
The stack frame for caller includes storage for local variables arg1 and arg2, at positions −4 and −8 relative to the frame pointer. These variables must be stored on the stack, since the code must associate an address with them.
This code saves a copy of %ebp and sets %ebp to the beginning of the stack frame (lines 2–3). It then allocates 24 bytes on the stack by decrementing the stack pointer. It initializes arg1 and arg2 to 534 and 1057, respectively (lines 5–6), and computes the values of &arg2 and &arg1 and stores these on the stack to form the arguments to swap_add (lines 7–10). It stores these arguments relative to the stack pointer, at offsets 0 and +4 for later access by swap_add. It then calls swap_add. Of the 24 bytes allocated for the stack frame, 8 are used for the local variables, 8 are used for passing parameters to swap_add, and 8 are not used for anything.
The compiled code for swap_add has three parts: the “setup,” where the stack frame is initialized; the “body,” where the actual computation of the procedure is performed; and the “finish,” where the stack state is restored and the procedure returns.
The following is the setup code for swap_add. Recall that before reaching this part of the code, the call instruction will have pushed the return address onto the stack.
Function swap_add requires register %ebx for temporary storage. Since this is a callee-save register, it pushes the old value onto the stack as part of the stack frame setup. At this point, the state of the stack is as shown on the right-hand side of Figure 3.24. Register %ebp has been shifted to serve as the frame pointer for swap_add.
Body code for swap_add:
Finishing code for swap_add:
This code restores the values of registers %ebx and %ebp, while also resetting the stack pointer so that it points to the stored return address, so that the ret instruction transfers control back to caller.
The following code in caller comes immediately after the instruction calling swap_add:
This code retrieves the values of arg1 and arg2 from the stack in order to compute diff, and uses register %eax as the return value from swap_add. Observe the use of the leave instruction to reset both the stack and the frame pointer prior to return. We have seen in our code examples that the code generated by gcc sometimes uses a leave instruction to deallocate a stack frame, and sometimes it uses one or two popl instructions. Either approach is acceptable.
Arguments are passed to a function on the stack, where they can be retrieved using positive offsets (+8, +12, …) relative to %ebp. Space can be allocated on the stack either by using push instructions or by subtracting offsets from the stack pointer. Before returning, a function must restore the stack to its original condition by restoring any callee-saved registers and %ebp, and by resetting %esp so that it points to the return address.
3.7.5 Recursive Procedures
The set-up code (lines 2–5) creates a stack frame containing the old version of %ebp, the saved value for callee-save register %ebx, and 4 bytes to hold the argument when it calls itself recursively, as illustrated in Figure 3.27.
It uses register %ebx to save a copy of n (line 6). It sets the return value in register %eax to 1 (line 7) in anticipation of the case where n ≤ 1, in which event it will jump to the completion code.
For the recursive case, it computes n − 1, stores it on the stack, and calls itself (lines 10–12). Upon completion of the code, we can assume (1) register %eax holds the value of (n − 1)! and (2) callee-save register %ebx holds the parameter n. It therefore multiplies these two quantities (line 13) to generate the return value of the function.
For both cases—the terminal condition and the recursive call—the code proceeds to the completion section (lines 15–17) to restore the stack and callee-saved register, and then it returns.
Please indicate the source if you want to reprint: http://blog.csdn.net/gaoxiangnumber1.
Chapter 3-05
最新推荐文章于 2023-02-24 22:04:01 发布