How does stack look in function calls




















Notice that the new stack frame starts almost immediately after the frame for main. There may be a small amount of additional space required to deal with return values or register saves at the low level, but in our high-level view this doesn't matter too much and we will always start the stack frames as close to each other as possible. Notice also that the difference in the locations is 8 bytes: this is because the variable x in main is a double so takes 8 bytes of space whereas all the other variables seen so far take 4 bytes as they are integers.

Control now starts at the beginning of mogrify but will eventually return to main at which point it will resume execution on line 13 completing that line and moving forward. Finally, notice that the parameters a,b to mogrify have values defined. This is as a result of main calling the function and passing actual values in for those parameters. The local variable tmp does not yet have known value associated with it as the first line of mogrify has not yet executed.

After executing one line of mogrify , the local variable tmp takes on a value due to its assignment. The second line of mogrify is to return a value to the calling function. This has two effects. Analyzing the code for printf is not pertinent to gaining a basic understanding of the function call stack so we'll presume printf does its business, puts something on the screen like.

Control resumes in main at the next relevant line of code. At line 16 , another function is called which pushes another stack frame onto the call stack. Space on the stack is re-used by subsequent function calls. This has implications for the values of variables that are not assigned values which we may discuss later. Line 19 calls mogrify again with different arguments. Control in main is suspended at a different location than the first call to mogrify line 19 this time but control begins again in mogrify at its first line line 4.

Notice that the first argument to mogrify in this call is a little interesting: it was passed as a double with value 8. The compiler has automatically inserted low-level instructions to caste the 8-byte floating point value to a 4-byte integer value. Most of the time this is nice convenience which saves programmers the trouble of writing such code but it can create subtle bugs if the programmer did not intend for such a conversion to happen.

This is a somewhat more involved example. The computation doesn't do anything particularly interesting but is a good demonstration of how the stack can grow and shrink as one function calls another function.

Little explanation is given in each step so pay careful attention to which line is being executed and remember that after a return is executed, control returns to the previous function and a stack frame is popped off. It is common to want to swap the values of two variables. The alignment is at the highest address of the space reserved by the sub rsp, x instruction, and the final local variable in the function is assigned at the next lower address after that and within the assignment for that primitive data type itself it starts at the lowest address of that assignment and works towards the higher addresses, bytewise, because it is little endian , such that the first primitive type array cell, variable etc.

This is shown in the following diagram for a different random example code to the above, that does not call any functions still using x64 Windows cc :. If you remove the call to func , it only reserves 24 bytes, i. The alignment is at the start of the frame. When a function pushes something to the stack or reserves space on the stack by decrementing the rsp , rsp needs to be aligned, regardless of whether it is going to call another function or not.

On gcc 32 bit cdecl and 64 bit system V calling conventions, rbp is used, and the new rbp points to the first byte after the old rbp only if compiling using -O0 , because it is saved to the stack on -O0 , otherwise, rbp will point to the first byte after the return address. On these calling conventions, if compiling using -O0 , it will, after callee saved registers, store register parameters to the stack, and this will be relative to rbp and part of the stack reservation done by the rsp decrement.

Data within the stack reservation done by the rsp decrement is accessed relative rbp rather than rsp , unlike Windows x64 cc. On the Windows x64 calling convention, it stores parameters that were passed to it in registers to the homespace that was assigned for it if it is a varargs function or compiling using -O0.

If it is not a varargs function then on -O1 , it will not write them to the homespace but the homespace will still be provided to it by the calling function, this means that it actually accesses those variables from the register rather from the homespace location on the stack after it stores it there, unlike O0 which saves them to the homespace and then accesses them through the stack and not the registers.

If a function call is placed in the function represented by the previous diagram, the stack will now look like this before the callee function's prologue starts Windows x64 cc :. Orange indicates the part that the callee can freely arrange arrays and structs remain contiguous of course, and work their way towards higher addresses, each element being little endian , so it can put the variables and the return value allocation in any order, and it passes a pointer for the return value allocation in rcx for the callee to write to when the return type of the function it is calling cannot be passed in rax.

On -O0 , if the return value cannot be passed in rax , there is also an anonymous variable created as well as the return value space and as well as any variable it is assigned to, so there can be 3 copies of the struct. Blue indicates the part the callee must provide in exact order for the calling convention of the callee the parameters must be in that order, such that the first stack parameter from left to right in the function signature is at the top of the stack, which is the same as how cdecl which is a 32 bit cc orders its stack parameters.

The alignment for the callee can however be in any location, although I've only ever seen it to be between the locals and callee pushed registers. If the function calls multiple functions, the call is in the same place on the stack for all the different possible callsites in the function, this is because the prologue caters for the whole function, including all calls it makes, and the parameters and homespace for any called function is always at the end of the allocation made in the prologue.

Arrays are passed by reference regardless of their size. So if you need to use rcx as the pointer to the return value allocation then if the first parameter is an array, the pointer will be passed in rdx , which will be a pointer to the local variable that is being passed.

In this case, it does not need to copy it to the stack as a parameter because it's not passed by value. The pointer however is passed on the stack when passing by reference if there are no registers available to pass the pointer in.

Stack Overflow for Teams — Collaborate and share knowledge with a private group. Create a free Team What is Teams? Collectives on Stack Overflow. Learn more. How exactly does the callstack work? Ask Question. Asked 7 years, 5 months ago. Active 5 months ago. Viewed 29k times. So, I wonder what exactly happens behind the scenes? Another related question. Improve this question. Christoph Christoph LIFO only matters for reserving space on the stack. You can always access any variable that is at least on your stack frame declared inside the function even if it is under a lot of other variables — VoidStar.

Why don't you disassemble a simple function after compiling with -O0 and look at the generated instructions? It's pretty, well, instructive ;-. You'll find that the code makes good use of the R part of the RAM; it accesses addresses directly at will.

You can think of a variable name as an offset to an address register the stack pointer. As the others said, the stack is just LIFO with respect to stacking good for recursion etc. It's not LIFO with respect to accessing it. Access is completely random. You can make your own stack data structure using an array, and just storing the index of the top element, incrementing it when you push, decrementing it when you pop.

If you did this, you'd still be able to access any individual element in the array at any time without pushing or popping it, just like you always can with arrays. Approximately the same thing is happening here.

They bear little resemblance to stack and heap in data structures' terminology, so calling them the same is very confusing. Show 13 more comments. Active Oldest Votes. This graphic from Wikipedia shows what the typical call stack is structured like 1 : Add the offset of a variable we want to access to the address contained in the frame pointer and we get the address of our variable.

The function prologue consists of the first three operations: Base pointer is pushed onto the stack. The stack pointer is saved in the base pointer The stack pointer is subtracted to make room for local variables. So far so good. Finally, The return value of main is stored in EAX: 0. That is because of the implicit return statement. You might also see xorl rax rax instead of movl.

Improve this answer. Lastly, we'll examine two assembly programs and understand the whole picture of function calls. A function's arguments aka. Local variables are data storage that a function uses while processing that is thrown away when it returns. It's knid of like a scratch pad of paper.

Functions get a new piece of paper every time they are activated, and they have to throw it away when they are finished processing. The return address is an "invisible" parameter in that it isn't directly used during the function.

The return address is a parameter which tells the function where to resume executing after the function is completed. This is needed because functions can be called to do processing from many different parts of our program, and the function needs to be able to get back to wherever it was called from.

In most programming languages, this parameter is passed automatically when the function is called. In assembly language, the call instruction handles passing the return address for you, and ret handles using that address to return back to where you called the function from. The return value is the main method of transferring data back to the main program. Most programming languages only allow a sinlge return value for function.

The way that the variables are stored and the parameters and return values are transferred by the computer varies from language to language. This variance is known as a language's calling convention , because it describes how functions expect to get and receive data when they are called. In this post, I'll follow C programming language calling convention.

Each computer program that runs uses a region of memory called the stack to enable functions to work properly. Machine uses the stack to pass function arguments, to store return information, to save registers for later restoration, and for local variables. The portion of the stack allocated for a single function call is called a stack frame.

In other words, for each function call, new space i. The computer's stack lives at the very top addresses of memory. As the name suggests, stack is a stack data structure with the "top" of the stack growing from the high value addresses towards low values addresses. Pointer here means that the stack register contains an address in memory instead of a regular value. We will dig more detail about these later on. The function parameters are pushed on the stack before the function is called.

The parameters are pushed from right to left. Then, the frame pointer that is the previous value of the EBP register is placed on the stack. Next, the locally declared variables. Then the buffers are allocated for temporary data storage. There are two CPU registers that are important for the functioning of the stack which hold information that is necessary when calling data residing in the memory. ESP is modifiable either directly or indirectly. This instruction causes the stack to shrink by 12 bytes.

That causes the stack to grow by 12 bytes. Keep in mind that it may seem confusing. In fact, the bigger the ESP value, the smaller the stack size and vice versa because the stack grows downwards in memory as it gets bigger and vice versa.

For example:. In addition to the stack pointer, which points to the top of the stack lower numerical address ; it is often convenient to have a stack frame pointer FP which holds an address that point to a fixed location within a frame. Looking at the stack frame, local variables could be referenced by giving their offsets from ESP.

However, as data are pushed onto the stack and popped off the stack, these offsets change, so the reference of the local variables is not consistent. Because the way stack grows, actual parameters have positive offsets and local variables have negative offsets from FP as shown below. Let examine the following simple C program. And the memory layout will look something like this:. Figure 2: Function call: The memory layout. The EBP register is a static register that points to the stack bottom.

The bottom of the stack is at a fixed address. More precisely the EBP register contains the address of the stack bottom as an offset relative to the executed function. Depending on the task of the function, the stack size is dynamically adjusted by the kernel at run time. This new value of ESP held by EBP becomes the reference base to local variables that are needed to retrieve the stack section allocated for the new function call. As mentioned before, a stack grows downward to lower memory address.

The stack pointer ESP last address on the stack not the next free available address after the top of the stack. The first thing a function must do when called is to save the previous EBP so it can be restored by copying into the EIP at function exit later. This code is called the procedure prolog. Upon function exit, the stack must be cleaned up again, something called the procedure epilog.

PUSH adds an element at the top of the stack. POP , in contrast, removing the last element at the top of the stack. Other instructions used in s tack manipulation are listed in the following table.



0コメント

  • 1000 / 1000