AnsweredAssumed Answered

GCC bug or FreeRTOS bug?

Question asked by Jive Tihs on Sep 21, 2017
Latest reply on Sep 22, 2017 by David Littell

I spent 3 hours debugging this. I'm using STM32L051 with FreeRTOS (initial code generated by STM32CubeMX v4.22.1). GCC version is the one that's coming with Ubuntu 16.04 by default:

 

~$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (15:4.9.3+svn231177-1) 4.9.3 20150529 (prerelease)
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

I'm using the -Os optimization flag. I can't turn off the optimization because the binary ends up being bigger than my flash.

 

I faced strange hard fault errors with some values of my tasks' stack sizes. After some debugging I realized that the top of the stack address of some of my tasks was invalid. Some more debugging and I found the offending part.

 

Look at this line of the function prvInitialiseNewTask in tasks.c of FreeRTOS' code where the pxTopOfStack variable corrupts:

 

    {
        pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
        pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception.  Avoiding casts between pointers and integers is not practical.  Size differences accounted for using portPOINTER_SIZE_TYPE type. */

        /* Check the alignment of the calculated top of stack is correct. */
        configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
    }

 

 

And the disassembly (with my comments):

 

; ulStackDepth == r2 == 0x00000200
;                 r5 == 0x2000085c

        807 [1]            pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
0x80030d4  <+0x0018>        44 4e        ldr    r6, [pc, #272]    ; (0x80031e8 <xTaskCreateStatic+300>)     ; r6 == 0x3fffffff
        605 [1]                pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
0x80030d6  <+0x001a>        25 63        str    r5, [r4, #48]    ; 0x30
        807 [2]            pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
0x80030d8  <+0x001c>        92 19        adds    r2, r2, r6                                                ; r2 == 0x400001ff
0x80030da  <+0x001e>        92 00        lsls    r2, r2, #2                                                ; r2 == 0x000007fc
0x80030dc  <+0x0020>        aa 18        adds    r2, r5, r2                                                ; r2 == 0x20001058
        808 [1]            pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception.  Avoiding casts between pointers and integers is not practical.  Size differences accounted for using portPOINTER_SIZE_TYPE type. */
0x80030de  <+0x0022>        07 25        movs    r5, #7
0x80030e0  <+0x0024>        aa 43        bics    r2, r5
0x80030e2  <+0x0026>        01 92        str    r2, [sp, #4]

 

 

I added a cast to uint32_t and then back to a pointer in the first line and now it's fixed:

    {
        pxTopOfStack = ( StackType_t * ) ( ( uint32_t )pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ) );
        pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception.  Avoiding casts between pointers and integers is not practical.  Size differences accounted for using portPOINTER_SIZE_TYPE type. */

          /* Check the alignment of the calculated top of stack is correct. */
          configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
    }

 

The disassembly:

 

; ulStackDepth == r2 == 0x00000200
;                 r5 == 0x2000085c

0x80030d4  <+0x0018>        01 3a        subs     r2, #1
        807 [1]             pxTopOfStack = ( StackType_t * ) ( ( uint32_t )pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ) );
0x80030d6  <+0x001a>        aa 18        adds     r2, r5, r2                                                 ; r2 == 0x20000a5b
        605 [1]                 pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer;
0x80030d8  <+0x001c>        25 63        str     r5, [r4, #48]     ; 0x30
        808 [1]             pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 MISRA exception.  Avoiding casts between pointers and integers is not practical.  Size differences accounted for using portPOINTER_SIZE_TYPE type. */
0x80030da  <+0x001e>        07 25        movs     r5, #7
0x80030dc  <+0x0020>        aa 43        bics     r2, r5
0x80030de  <+0x0022>        01 92        str     r2, [sp, #4]

 

 

 

So to sum up, pxNewTCB->pxStack is a pointer to uint32_t, casting it to uint32_t helped to fix the bug in the calculation process.

 

Is this a GCC bug or maybe mixing a pointer and uint32_t in an arithmetic expression is undefined behavior and then this is a FreeRTOS bug?

 

The funny thing is that after fixing this I'm facing another hard fault in another place. Gone debugging...

Outcomes