What Is The Difference Between A Link Register And A Frame Pointer?
Stack frames
A really quick explanation of stack frames and frame pointers
February 16, 2018
Understanding Frame Pointers
Each function has local memory associated with it to agree incoming parameters, local variables, and (in some cases) temporary variables. This region of memory is called a stack frame and is allocated on the process' stack. A frame pointer (the ebp annals on intel x86 architectures, rbp on 64-bit architectures) contains the base address of the function's frame. The code to access local variables within a function is generated in terms of offsets to the frame arrow. The stack pointer (the esp register on intel x86 architectures or rsp on 64-bit architectures) may change during the execution of a part every bit values are pushed or popped off the stack (such as pushing parameters in preparation to calling some other office). The frame arrow doesn't change throughout the function.
Hither's what happens during part (at that place might be slight differences amidst languages/architectures)
-
Button the current value of the frame pointer (ebp/rbp). This saves information technology so we can restore it afterward.
-
Move the current stack pointer to the frame pointer. This defines the outset of the frame.
-
Subtract the space needed for the function's data from the stack pointer. Recall that stacks abound from loftier retentiveness to low memory. This puts the stack pointer past the space that will be used by the function and then that anything pushed onto the stack now volition non overwrite useful values.
-
Now execute the lawmaking for the function. References to local variables will be negative offsets to the frame pointer (e.thousand., "movl $123, –8(%rbp)").
-
On leave from the office, copy the value from the frame pointer to the stack pointer (this clears upward the space allocated to the stack frame for the part) and popular the old frame pointer. This is accomplished past the "leave" pedagogy.
-
Return from the procedure via a "ret" education. This pops the return value from the stack and transfers execution to that address.
Basic instance
Let's consider the following set of functions in a file called endeavour.c
void bar(int a, int b) { int x, y; x = 555; y = a+b; } void foo(void) { bar(111,222); }
We'll compile it via
gcc -South -m32 try.c
The -S option tells the compiler to create an assembler file. The -m32 option tells the compiler to generate code for a 32-flake architecture. In this example, it keeps the numbers smaller and we don't have to worry about specifying -no-reddish-zone (see more details, below).
gcc chooses to use the mov didactics (movl) instead of button because the Intel x86 instruction ready doesn't accept an educational activity to push button constant values onto the stack. Adjusting the stack so moving the required parameters into the proper places as negative offsets accomplishes the same thing.
The generated code is (removing lines that contain directives to the linker):
bar: pushl %ebp movl %esp, %ebp subl $xvi, %esp movl $555, -four(%ebp) movl 12(%ebp), %eax movl 8(%ebp), %edx addl %edx, %eax movl %eax, -viii(%ebp) go out ret foo: pushl %ebp movl %esp, %ebp subl $8, %esp movl $222, 4(%esp) movl $111, (%esp) call bar leave ret
We can comment the code and trace it by starting at foo():
bar: # --------- first of the function bar() pushl %ebp # save the incoming frame pointer movl %esp, %ebp # set the frame pointer to the electric current peak of stack subl $xvi, %esp # increase the stack past 16 bytes (stacks grow down) movl $555, -4(%ebp) # x=555 a is located at [ebp-4] movl 12(%ebp), %eax # 12(%ebp) is [ebp+12], which is the second parameter movl 8(%ebp), %edx # 8(%ebp) is [ebo+viii], which is the showtime parameter addl %edx, %eax # add them movl %eax, -viii(%ebp) # shop the result in y leave # ret # foo: # --------- start of the function foo() pushl %ebp # save the current frame arrow movl %esp, %ebp # set the frame pointer to the current top of the stack subl $8, %esp # increase the stack past viii bytes (stacks abound down) movl $222, 4(%esp) # this is effectively pushing 222 on the stack movl $111, (%esp) # this is finer pushing 111 on the stack call bar # call = push the instruction pointer on the stack and branch to foo leave # washed ret #
Let'southward see what happens. In foo(), nosotros need to prepare the stack for ii parameters that volition be sent to bar(). The compiler would similar to practice
push $222 push $111
but those instructions don't exist on the IA–32 compages so instead, the compiler generates code to decrease eight from the stack arrow, making the stack grow by 8 bytes (enough to hold two 32-flake values). It and then uses stack offset addressing to place the values 111 and 222 on the stack (see figure 1).
So foo calls bar. This pushes the return address onto the stack so information technology looks like this when execution starts at bar (figure 2):
On entry to bar(), we save the previous value of ebp, and fix the frame pointer to the height of the stack (the current position of the stack pointer). Then we abound the stack past subtracting sixteen from the stack pointer. Stacks on intel architectures grow from loftier retentiveness to low memory, so the top of the stack (the latest contents) are in low retentivity. The stack now looks similar the one shown in effigy 3. We take a stack frame for the role bar that holds local data for this example of the function. Negative offsets of the frame pointer %ebp (toward the top of the stack, into lower memory) will refer to local data in bar. Positive offsets of %ebp will allow us to read incoming parameters.
Now we're ready to execute the picayune logic of the role. Nosotros set local variable ten to 555. This variable is the very next fix of four bytes after the saved ebp. The next statement adds the two parameters and stores the result into the local int y. The lawmaking for this is to read the value of b (which is [ebp+12]) and store it into register %eax. The value of a (which is [ebp+8]) is read into register %edx. The two values are added and the result is stored in y, which is [ebp–8]. Figure 4 shows the position of the parameters and local variables.
When we're done, we call "go out", which sets the stack pointer to the value of the frame pointer (%ebp) and pops the saved value of the frame pointer (the one the office foo was using). At present the stack pointer is pointing to the return address within foo that was saved when the call educational activity was executed and our frame is effectively deallocated. The ret pedagogy pops the stack and transfers control back to foo right after the phone call bar educational activity.
You might be wondering why the stack was adapted by 16 bytes instead of the eight that was needed to hold x and y. I don't know. That seems to be a multiple that gcc uses. If you allocate two more local ints, the frame remains the same size. If y'all allocate another int, the compiler grows the stack by 32 bytes.
More details nearly how frames are used
-
gcc (and other compilers) uses registers for the start few (6) parameters and these are copied into areas inside the function's frame]
-
As an optimization, the intel x86–64 architecture allows functions to use infinite on the stack without adjusting the stack arrow if that space is <= 128 bytes. Interrupt handlers are guaranteed to not modify this region. You can search for "red zone" to read near this if you're interested. The gcc compiler can be told to ignore this via a -mno-cherry-zone option.
-
Since the compiler can go along track of what'south going on with the stack at any signal in fourth dimension, the frame arrow isn't strictly necessary. Yous can compile code to use the stack pointer exclusively with the -fomit-frame-pointer option to gcc.
Exploiting buffer overflow
Past exploiting a buffer overflow, y'all tin can write capricious data onto the stack. This means that you can change the render address of a function and also change the data past that render accost - the local variables of previous functions. In a bones code injection assault, you can change the render address to the accost of the buffer that you lot overwrote with code of your choosing. You now injected lawmaking into the program. In a uncomplicated return-oriented-programming set on, you change the render accost to the accost of a library role such as system() and insert data on the stack to make give system() the parameters you desire (e.yard., a command to execute). Note that the code illustrated in a higher place is non vulnerable to buffer overflow since we're using scalars (just ints) instead of arrays.
References
- Call stack
- Stack frame layout on x86–64
- Buffer overflow: the part stack
- x86 Disassembly/Functions and Stack Frame
- Blood-red zone
- x86 exit instruction
What Is The Difference Between A Link Register And A Frame Pointer?,
Source: https://www.cs.rutgers.edu/~pxk/419/notes/frames.html
Posted by: shawstookins.blogspot.com
0 Response to "What Is The Difference Between A Link Register And A Frame Pointer?"
Post a Comment