This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Use of __STACK_SIZE and __STACK_END, CCS 4.2.0, MSP430

Other Parts Discussed in Thread: MSP430F2618

Perhaps someone can provide some insight here..

I am trying to implement a simple stack overflow check by writing an 0xAA55 pattern at the bottom 10 bytes (lowest addresses, last used) of the C Stack. I will add a check for this pattern (actually the top of the 10 bytes) in my dispatcher loop, so it is checked regularly to see if it is overwritten as the stack grows.

The compiler and linker generate labels __STACK_END (0x0600) and __STACK_SIZE (0x0050). I cannot find any specific way to identify the data types of these labels, but the debugger claims they are BOTH (void *), despite the clear intent that __STACK_SIZE is a LENGTH, not a pointer!

I can find the label __STACK_END used in the compiler library boot.c file. I cannot find ANY reference to __STACK_END ANYWHERE except in the .map file

In short, I cannot correctly access the value of __STACK_SIZE. Please see the attached screen capture. Note that all variables are visible in the 'Watch' window. __STACK_SIZE appears correct, but every attempt to access it is WRONG. I tried various data types because I am not sure what it really is.

This would have been a little easier to do if there were some way to completely turn off compiler optimization. Note all the idiotic code I had to add simply to PREVENT the optimizer from discarding my attempts to read __STACK_SIZE and __STACK_END. ALL optimization is supposedly turned off...NOT!

Note that the Watch window shows one of my variables in R13. According to the Compiler User's guide SLAU132E, page 102, the C Stack Pointer is R13, and it defaults to a 2048 byte stack size, which is pretty remarkable on processors that generally have less than 2K of RAM. I think this is actually boilerplate text from the manual for another processor (ARM? LEG?), but no one has bothered to correct it through multiple editions of the document. boot.c shows the SP register being used for the C Stack pointer.

BTW, this is just one of many bizarre screen captures I have collected from CCS 4.2.0 TI should be congratulated from bringing embedded software development tools to an astonishing new low. CCS (Computer Crashing Software?) is without question the buggiest, most non-deterministic software I have used in several decades of embedded system development. Something as simple as clicking on the {cancel} button during a compile sometimes results in in it completely locking out all input (WITHOUT stopping!), requiring the use of Task Manager to close and stop it.

 

 

  • LInker created symbols like __STACK_SIZE are not like ordinary global variables like this one.

    int global_value;

    In this case, global_value holds the address of a memory location and that memory location holds the value.  While you sometimes use the address of global_value, you usually use the value located at that address.  __STACK_SIZE does not correspond to a memory location.  Rather __STACK_SIZE itself holds the value you use.  In C this is commonly written like this ...

    #define STK_SIZE ((unsigned int) &_STACK_SIZE)

    Then you can use STK_SIZE as you are using __STACK_SIZE above.  The same technique applies to __STACK_END, and any other linker created symbol accessed from C.

    Side note for EABI/ELF users.  You would write ...

    #define STK_SIZE ((unsigned int) &__STACK_SIZE)    // for EABI

    Note both underscores are still at the start of __STACK_SIZE.  The compiler does not add an additional '_' as it does for COFF.

    Thanks and regards,

    -George

  • ....Thank you for the help, but...

    __STACK_END can be used as is, when treated as what it is - a pointer. No notation other than a cast to a pointer to unsigned char is required.

    The problem is __STACK_SIZE, which  the debugger thinks is a pointer to void, but I cannot find ANYTHING (see screen capture) that it works as. No matter what I try to cast it as, the value is always wrong - the SAME wrong value. Not so with __STACK_END.

    By simply replacing __STACK_SIZE with

    #define _DEFINED_STACK_SIZE  0x0050

    my routine for writing to the bottom of the stack works exactly as I expected it to. I will try what you have suggeseted, and see if the compiler will even eat it.

    There is something very strange and unconventional about __STACK_SIZE

  • __STACK_END and __STACK_SIZE are linker symbols, which are very different from C symbols. In C, a symbol name by itself represents the contents of an address. In the linker, a symbol name by itself represents the address, not the contents of that address.  Keep in mind that the C expression "&variable" has the same value as the expression "variable" in the assembler, linker, and linker command file.

    The linker does not have the concept of type for linker symbols.  All linker symbols are addresses, even if they conceptually represent integer values. When a linker symbol conceptually represents an array, you can express it quite naturally in C, but when the conceptual type is really an integer, you have to resort to a little trickery. 

    When you use a variable in C, the compiler automatically fetches the contents of the memory location &variable.  You usually don't want this for a symbol which is really a linker symbol; you need the address of the symbol.  The canonical way to access these linker symbols from C is as follows:

    #include <stdio.h>
    #include <stdint.h>
    
    extern char __STACK_END;  /* the type does not matter! */
    extern char __STACK_SIZE; /* the type does not matter! */
    
    int main(void)
    {
        void * stack_end = (void *)(uintptr_t)_symval(&__STACK_END); /* Yes, & is needed */
        unsigned stack_size = _symval(&__STACK_SIZE);
    
        printf("stack end = %p\n", stack_end);
        printf("stack size = %u\n", stack_size);
    
        return 0;
    }

    Conceptually, _symval is just a cast operation, but it should be used in this sort of situation.

    Why does the code take the address of __STACK_SIZE?  The linker needs to represent the value, but the only thing it has to work with are linker symbols, which represent addresses.  So, it pretends that something exists at that memory location and creates __STACK_SIZE with the value of that address.  To get at the address in C, we must use the address-of operator so that C will get the value of the address instead of automatically trying to fetch the contents of that address.

    You can also declare linker symbols as arrays, because the name of an array automatically has the address-of operator applied to it when the array is referenced:

    extern char __STACK_END[];
    
    void write_one_past_stack()
    {
        __STACK_END[0] = 0; /* Same as *(&__STACK_END + 0) = 0 */
    }

     

  • Thank you. That is much clearer, except, of course for the parts that are clear as mud.

    Specifically: where is _symval documented? I can find no mention of it in the CCS docs, MSP430 compiler docs, or MSP430 assembler docs. I can find no mention of it in the 'Embedded Processors Dustbin'. Nothing. Nada. I found a single reference to it in the library source file args_main.c. That is all! It does not appear to be part of a larger standard, either, as I can find nothing about it in the M$DN library, either.

    As it turns out, I really did need the stack overflow checking, because I am overflowing my stack. Considering that this is a common need - and it is mentioned in the Compiler documentation - WHY is there not a fully worked out example of doing so somewhere? For that matter, why isn't it built into the MCU?

     

     

     

  • I don't know if this helps, but here is the code that I used to fill stack and heap on an MSP430F2618.  Any suggested improvements are welcomed!  The documentation on these magic symbols is completely dismal...

    #include <stdint.h>

    #define STACK_FILL_PATTERN  0xBEEF  ///< Stack fill pattern for checking depth
    #define HEAP_FILL_PATTERN   0xCAFE  ///< Heap fill pattern for checking usage

    extern void * __STACK_END;      // End of stack
    extern int _stack;              // Lowest address of stack
    extern int _sys_memory;         // Start of heap
    extern void * __SYSMEM_SIZE;    // Size of heap

    /***************************************************************************//**
     * @brief Perform system pre-initialization
     *
     * Fills the stack and the heap with known patters so that the utilization of
     * each area can be monitored at runtime to detect overflows.  Uses registers
     * because we don't want to use more stack space than necessary.
     *
     * @return int  Always returns 1
     *
     ******************************************************************************/
    int _system_pre_init(void)
    {
        // Get lowest stack address
        register uint16_t * pMem = (uint16_t *)(&_stack);

        // Don't overwrite return address of caller, so subtract two words from end of stack
        register uint16_t * pEnd = (uint16_t *)(&__STACK_END) - 2;

        // Fill stack with known pattern to check usage during runtime
        while (pMem < pEnd)
        {
            *pMem++ = STACK_FILL_PATTERN;
        }

        // Get start of heap
        pMem = (uint16_t *)(&_sys_memory);

        // Get size of heap
        pEnd = (uint16_t *)(&__SYSMEM_SIZE);

        // Add start and size of heap to obtain end of heap
        pEnd = (uint16_t *)((uint16_t)pMem + (uint16_t)pEnd);

        // Fill heap with known pattern to check usage during runtime
        while (pMem < pEnd)
        {
            *pMem++ = HEAP_FILL_PATTERN;
        }

        return 1;
    }

  • Hi Scott - thank you for for submitting your example code. I did end up with something that worked, but I don't think it was quite like the example code (complete with undocumented macros/directives/whatever) that TI provided.

    It has been 6 months since I looked at this. I noticed that you did not need to use the mysterious __symval. What compiler are you using? I was using CCS 4.x

    regards

    Steve

  • Steve,

    I am also using CCS, now at V4.2.4.  I never did find info on the magical __symval "macro", "operator", or whatever it is.  Basically I had to work from the types that I saw in the debugger window, and eventually I was able to "force" the various symbols I had available to do what I needed.  Not elegant, just pragmatic.

    I have not implemented the depth checking routines yet, but they should not be too bad.  Just going to look for the end of the fill pattern for the stack, and if it's too low, I will output an error.  Which leads me to my other pet peeve, the lack of a decent example for formatted I/O to and from a UART.  There is a different thread going on that subject, but I basically can do printf()-like output and getchar()-like input, and even have transmit and receive interrupts going.

    Regards!

    Scott

  • For reasons that completely escape me, I have found that most MCU vendors do a pretty lousy job of providing usable library or sample code for serial I/O. Microchip provides library functions that access the UART, but no complete interrupt driven implementation. I think TI does about the same.

    I use interrupt driven serial I/O with a small collection of fixed function number-to-string conversion routines which are:

    adequate for the task, small, reliable, fast

    printf()- like functions tend to be big, slow, and error prone.

    If you would like to provide an offline email address I can send you some source code...

    regards

    Steve