The execution stack

As seen in the previous chapter, a bare-metal application starts executing with an empty stack area. The execution stack grows backwards, from the high address provided at boot towards lower addresses every time a new item is stored. The stack keeps track of the chain of function calls at all times by storing the branching point at each function call, but also serves as temporary storage during function executions. Variables within the local scope of each function are stored inside the stack while the function is executing. For this reason, keeping stack usage under control is one of the most critical tasks while developing an embedded system.

Embedded programming requires us to be aware at all times about stack usage while coding. Placing big objects in the stack, such as communication buffers or long strings, is in general not a good idea, considering that the space for the stack is always very limited. The compiler can be instructed to produce a warning every time the stack space required by a single function exceeds a certain threshold, as, for example, in this code:

void function(void)
{
char buffer[200];
read_serial_buffer(buffer);
}

If compiled with the GCC option -Wstack-usage=100, it will produce the following warning:

main.c: In function 'function':
main.c:15:6: warning: stack usage is 208 bytes [-Wstack-usage=]

That can be intercepted at compile time.

While this mechanism is useful to identify local stack overuses, it is not effective to identify all the potential stack overflows in the code, as the function calls may be nested and their stack usage added up. Our function uses 208 bytes of stack whenever it is invoked, 200 to host the buffer local variable in the stack, and 8 additional bytes to store two pointers: the origin of the call in the code section, that is stored as return point, and the frame pointer, which contains the old location of the stack pointer before the call.

By design, the stack grows every time a function is called and shrinks again when functions return. In a given case, it is particularly difficult to make estimations about the runtime stack usage, and that is the use of recursive functions. For this reason, the use of recursion in the code should be avoided whenever possible, or reduced to the minimum and kept under strict control otherwise, knowing that the memory area reserved to the stack in the target is small:

The stack pointer moves down when a function is called, to store frame pointers and local variables
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset