Just like a stack datatype in. NET, Java or any other programming language. But if that's the case, then what happens after line X? But then it would shoot itself in the foot, because it needs c and d in the next line. From how I understand things, this would mean that the parameters in doSomething are essentially pointing to the same memory address like a and b in foo. But then again this means that there is no pop up the stack until we get to a and b happening.
Those two cases make me think that I haven't fully grasped how exactly the stack works and how it strictly follows the LIFO rules. The call stack could also be called a frame stack. The things that are stacked after the LIFO principle are not the local variables but the entire stack frames "calls" of the functions being called.
The local variables are pushed and popped together with those frames in the so-called function prologue and epiloguerespectively. Inside the frame the order of the variables is completely unspecified; Compilers "reorder" the positions of local variables inside a frame appropriately to optimize their alignment so the processor can fetch them as quickly as possible.
The crucial fact is that the offset of the variables relative to some fixed address is constant throughout the lifetime of the frame - so it suffices to take an anchor address, say, the address of the frame itself, and work with offsets of that address to the variables. Such an anchor address is actually contained in the so-called base or frame pointer which is stored in the EBP register. The offsets, on the other hand, are clearly known at compile time and are therefore hardcoded into the machine code.Function calls in the C programming language make heavy use of the stack.
When functions are called, they create a so-called stack of frames. This stack frame is used to allocate memory for local variables and intermediate values. The stack frame also contains the previous frame pointer and typically the link register. The animation below shows the execution of the C function add 1,2. When this function is called the r0 register has the value 1and the r1 register holds the value 2.
Understanding how function call works
When the add function returns it will have the value 3 which will be stored in the r0 register, overwriting the first argument when the function was called. As the C Code executes we can see the corresponding assembly code.
Note, there are typically multiple instructions for one line of C code. The right most diagram shows the stack and what is pushed onto the stack. These are some deep details. Feel free to skip to other example below to get a better understanding. These 3 words hold a value for fplr and the local variable int c.
Depending on the optimization and what is done the frame could be bigger and the values of int aand int b could be stored in the frame. Because this function call requires a bl we have to previously push lr onto the stack so that the program counter can be restored. The frame gives us a way to protect what is local to our function when other functions are called since values can be stored outside of registers.
We then restore r3 from the frame into r0 where the return of the function is stored. This frame is then popped off the stack leaving us with the frame of 3 words that add has put on the stack. Before the animation, the caller of add executed a bl add instruction which would store in the lr the instruction right after the C code of add 1,2. We can disassemble this example to see the instructions using gdb.
I find the disassemble function in gdb much better than looking at the. This is especially present when arguments to functions are pushed into the frame and pull back without being modified. The best way to do this is to compile the example and run gdb. The registers can be viewed with info registers.
Before the animation, the caller of add executed a bl add instruction which would store in the lr the instruction right after the C code of add 1,2 What you need to Know First To understand how the stack of frames works the following is required knowledge. The stack starts at high memory and goes lower. The sp register: The sp register stands for stack pointer which stores the value of the top of the stack. A push will decrement the stack pointer by 1 word or 4 bytes on a bit ARM machine and store the value where the sp is pointing to.
The pop instruction will restore values from the stack into registers and increment the stack pointer. The fp register.In this part we will look into a special memory region of the process called the Stack. Additionally, we will go through the implementation, types and differences of functions in ARM. This part of the memory gets allocated when a process is created. We use Stack for storing temporary data such as local variables of some function, environment variables which helps us to transition between the functions, etc.
Before we look into a practical example it is import for us to know that the Stack can be implemented in various ways. First, when we say that Stack growswe mean that an item 32 bits of data is put on to the Stack. The actual location where the next 32 bit piece of information will be put is defined by the Stack Pointer, or to be precise, the memory address stored in the SP register.
If the SP is currently pointing to the last item in the stack Full stack implementation the SP will be decreased in case of Descending Stack or increased in case of Ascending Stack and only then the item will placed in the Stack. In our examples we will use the Full descending Stack. At the beginning, the Stack Pointer points to address 0xbefff6f8 could be different in your casewhich represents the last item in the Stack. At this moment, we see that it stores some value again, the value can be different in your case :.
After executing the first MOV instruction, nothing changes in terms of the Stack. Then, the contents of R0 are stored to the new address specified by SP. When we now examine the updated memory location referenced by SP, we see that a 32 bit value of integer 2 is stored at that location:. The instruction MOV r0, 3 in our example is used to simulate the corruption of the R0.
We then use POP to restore a previously saved value of R0. So when the POP gets executed, the following happens: first, 32 bits of data are read from the memory location 0xbefff6f4 currently pointed by the address in SP. The register R0 contains integer value 2 as a result. Please note that the following gif shows the stack having the lower addresses at the top and the higher addresses at the bottom, rather than the other way around like in the first illustration of different Stack variations.
The reason for this is to make it look like the Stack view you see in GDB. We will see that functions take advantage of Stack for saving local variables, preserving register state, etc. To keep everything organized, functions use S tack Framesa localized memory portion within the stack which is dedicated for a specific function.
A stack frame gets created in the prologue more about this in the next section of a function. While the actual contents of the Stack Frame may vary, the ones outlined before are the most common. Finally, the Stack Frame gets destroyed during the epilogue of a function. In the screenshot below we can see a simple illustration of a Stack Frame through the perspective of GDB debugger.
After reading a few articles that always explained that the general structure would be:. I have an example program that calls a function with three arguments and has two buffers as local variables:. I took a look at the assembler code of the program and was surprised not to find what I expect when the function is called. I expected something along the lines of:. Why use the registers here when we can just push to the stack???
And in the actual function that we call:. As far as I can see, 48 bytes are reserved for the stack frame right? And afterwards, using the registers from the function call, the arguments to the function are copied to the end of the stack. So it would look something like this:. I assume the buffers are somewhere between the args and the old BP.
But I'm really not sure where exactly Can someone help me outline what is happening here? Is this something that is processor dependant? I'm using an intel i7. There are a couple of issues. Agner Fog maintains a lot of excellent documentation, including one on calling conventions.
Part of the ABI also specifies that the stack must be byte aligned prior to a function call. If you recompile with -fno-stack-protectoryou'll get a better idea of what's going on. Furthermore, because the function doesn't do anything, it's not a particularly good example. It stores the arguments needlesslyrequiring 12 bytes.12.2.3 Stack Frame Organization
I might be wrong on this - I haven't got the spec at hand. So that's either 36 or 40 bytes. Call alignment then requires a byte alignment for 48 bytes. I think it would be more instructive to turn off the stack protection and examine the stack frame for this leaf function, and consult the x ABI spec for the alignment requirements of local variables, etc.Other compilers choose to maintain separate code and data stacks.
If you want to see the code GCC actually uses to generate the frame it's obviously there in the source somewhere - good luck on locating it though! Is there any chance you could post a small example of a function call to add two numbers and return the result please?
I tried myself and the result doesn't seem to look right. It may be that the code I expected to see was optimised out. Most likely the function was inlined. But even without inlining, the function would not use any stack frame. Each compiler will have a code generation model, and will adhere to it for parameter passing, return value, and stack frame.
That allows various routines to be interchangeable. Now, if you are ASM-only for your app then you can invent whatever you want for parameter passing etc. You could pass an 8-bit parameter in R16 for a number of routines, and for others pass it on the hardware stack. Similarly, internal to routines you could use scratch registers for the purpose of local variables, or you could use the hardware stack, or you could use a software stack, or some combination--hand have it be different each time.
For learning, I'd say to use one or a few compilers as models and then go from there. Then why are you bothering with stack frames at all? They are basically a concept in C to make room somewhere for variables that just exist while a function is in context.
An Asm program need not have the concept of "local variables" at all. The Asm programmer can just set aside as many machine registers as he needs for things that may be "local". I guess there is an issue if locals are in use and you then cal a function that itself wants to use "locals" but it would just push the machine registers to be be used.
Quote: Then why are you bothering with stack frames at all? My application allows users to define their own functions and link them. Then eventually it may be used for teaching techniques such as stack frames. I have implemented them before but a long time ago for a compiler. Thanks everyone for links and notes. I have implemented the first piece of pseudo code using ASM and it's getting close to working. Quote: I think I can figure rest out now OK but just in case it's not obvious, in that second example that Stefan gave which you might call the "normal" way?
Thanks for that, I had that bit working, I think I need to modify the pseudo code example that I first posted. It wants to subtract values from the Y index to get to local variables.
I'm really pleased with the result so far. This stuff will be made public, it would be great if you could all check it out when it's ready in case I have missed something obvious. Skip to main content. Stack Frames Example Assembly Code. Log in or register to post comments. Go To Last Post.
Level: Wannabe. Posts: 57 View posts. Posted by joandunning : Mon. Sep 21, - PM. Fivestar widget 1 2 3 4 5.Understanding assembly language is crucial for system programming.
Some nasty defects of the system can only be solved by digging into the assembly level of the program. In this post, I'll revisit call stack concept as a way to understand how function call works under the cover of high-level language. In addition, this post belongs to part of future work mentioned in my post back in January.
Before we jump into the actual material. I want to briefly revisit the various ways for assembly language accessing the data in memory i. Now, let's bring our main course onto the table: understanding how function works.
I'll first clear up some terms we will use during the explanation. Then, we'll take a look at the stack and understand how it supports function calls. 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 conventionbecause 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. Specifically, the stack register now contains the address, which has the top value of the stack in it. When we talk about function calls, what we really care about is the topmost stack frame because that's the memory region that is associated with our current function calls. CSAPP 2nd edition has a nice picture about what the whole stack looks like:.
If some texts i.A few months ago I've written an article named Where the top of the stack is on x86which aimed to clear some misunderstandings regarding stack usage on the x86 architecture.
The article concluded with a useful diagram presenting the stack frame layout of a typical function call. In this article I will examine the stack frame layout of the newer bit version of the x86 architecture, x64 .
x86 Disassembly/Functions and Stack Frames
Windows uses a somewhat different ABI, and I will mention it briefly in the end. I have no intention of detailing the complete x64 calling convention here.
Since some of x86's registers have special implicit meanings and aren't really used as general-purpose most notably ebp and espthe effective increase is even larger than it seems.
There's a reason I'm mentioning this in an article focused on stack frames. The relatively large amount of available registers influenced some important design decisions for the ABI, such as passing many arguments in registers, thus rendering the stack less useful than before . According to the ABI, the first 6 integer or pointer arguments to a function are passed in registers. The first is placed in rdithe second in rsithe third in rdxand then rcxr8 and r9.
Only the 7th argument and onwards are passed on the stack. So the first 6 arguments are passed via registers. But other than that, this doesn't look very different from what happens on x86 except this strange "red zone". What is that all about? Put simply, the red zone is an optimization. Code can assume that the bytes below rsp will not be asynchronously clobbered by signals or interrupt handlers, and thus can use it for scratch data, without explicitly moving the stack pointer.
The last sentence is where the optimization lays - decrementing rsp and restoring it are two instructions that can be saved when using the red zone for data. However, keep in mind that the red zone will be clobbered by function calls, so it's usually most useful in leaf functions functions that call no other functions.
Recall how myfunc in the code sample above calls another function named utilfunc. This was done on purpose, to make myfunc non-leaf and thus prevent the compiler from applying the red zone optimization.
Looking at the code of utilfunc :. This is indeed a leaf function. Let's see how its stack frame looks when compiled with gcc :. Since utilfunc only has 3 arguments, calling it requires no stack usage since all the arguments fit into registers. In addition, since it's a leaf function, gcc chooses to use the red zone for all its local variables. Thus, esp needs not be decremented and later restored to allocate space for this data.
The base pointer rbp and its predecessor ebp on x86being a stable "anchor" to the beginning of the stack frame throughout the execution of a function, is very convenient for manual assembly coding and for debugging . However, some time ago it was noticed that compiler-generated code doesn't really need it the compiler can easily keep track of offsets from rspand the DWARF debugging format provides means CFI to access stack frames without the base pointer.
This is why some compilers started omitting the base pointer for aggressive optimizations, thus shortening the function prologue and epilogue, and providing an additional register for general-purpose use which, recall, is quite useful on x86 with its limited set of GPRs.