LwIP stdlib rand() function failing

Hi,

I’m trying to get LwIP going on an STH7. The initialisation of the network functions calls the stdlib function rand() in a number of places. The function fails and you end up in the syscalls module:

void _exit (int status)
{
	_kill(status, -1);
	while (1) {}		/* Make sure we hang here */
}

LwIP Initialisation gets called from the default task which I named monitorTask as a static task as it runs all the time.

I am using the cmsis calling protocol as it is built in to the ST code generator.

Below are the relevant extracts from main.c.

/* Definitions for monitorTaskHnd */
osThreadId_t monitorTaskHndHandle;
uint32_t monitorTaskHndBuffer[ 4096 ];  
osStaticThreadDef_t monitorTaskHndControlBlock;
const osThreadAttr_t monitorTaskHnd_attributes = {
  .name = "monitorTaskHnd",
  .cb_mem = &monitorTaskHndControlBlock,
  .cb_size = sizeof(monitorTaskHndControlBlock),
  .stack_mem = &monitorTaskHndBuffer[0],
  .stack_size = sizeof(monitorTaskHndBuffer),
  .priority = (osPriority_t) osPriorityNormal,
};...

int main(void)
{ ...
	uint32_t randNo = rand();
...
}

void monitorTask(void *argument)
{

  uint32_t randNo = rand();
  /* init code for LWIP */
  MX_LWIP_Init();
  /* USER CODE BEGIN 5 */
  /* Infinite loop */
  for(;;)
  {
    osDelay(100);
  }
  /* USER CODE END 5 */
}

To take LwIP out of the equation I called rand() from within the main function and then at the start of the monitor task.

rand() works when called from main, but errors out when called from the task.

I tried turning off interrupts as rand is not guaranteed re-entrent but it sill failed to the
_exit (int status) function.

The disassembly of and stepping shows it fails at the assert line

          rand:
900347fc:   ldr     r3, [pc, #88]   @ (0x90034858 <rand+92>)
900347fe:   push    {r4, lr}
90034800:   ldr     r4, [r3, #0]
90034802:   ldr     r3, [r4, #48]   @ 0x30
90034804:   cbnz    r3, 0x90034834 <rand+56>
90034806:   movs    r0, #24
90034808:   bl      0x90034680 <malloc>
9003480c:   mov     r2, r0
9003480e:   str     r0, [r4, #48]   @ 0x30
90034810:   cbnz    r0, 0x9003481c <rand+32>
90034812:   ldr     r3, [pc, #72]   @ (0x9003485c <rand+96>)
90034814:   ldr     r0, [pc, #72]   @ (0x90034860 <rand+100>)
90034816:   movs    r1, #82 @ 0x52
90034818:   bl      0x90034638 <__assert_func>    /* <==  ****** Fails here ********/

I reckon the problem has something to do with the heap, but I don’t understand what it might be.

Has anyone any ideas.

Rob

Seems like malloc is failing to allocate memory. You have couple of options -

  1. LWIP allows overriding LWIP_RAND and most likely, your implementation of it is calling rand. You can change that to use any TRNG (assuming one is available on your hardware).
  2. You can use GCC linker warp to swap rand with another implementation which uses TRNG.
  3. If you want to use the newlib version of rand, you need to do the following -
    • One of the auto-generated source file will have sbrk or brk implementation which needs to be fixed. Please share the definition so that we can look.
    • Implement malloc_lock and malloc_unlock to ensure that malloc is thread safe.

Hi,

Thanks for that.

  1. I’m doing the first of your suggestions at the moment.

  2. I don’t understand your second suggestion about the linker script.

  3. The sysmem.c auto generated file has the following _sbrk code:

/* Includes */
#include <errno.h>
#include <stdio.h>

#include <sys/types.h> // Added as a workaround for Bugzilla 158966

/* Variables */
extern int errno;
register char * stack_ptr asm("sp");

/* Functions */

/**
 _sbrk
 Increase program data space. Malloc and related functions depend on this
**/
caddr_t _sbrk(int incr)
{
	extern char end asm("end");
	static char *heap_end;
	char *prev_heap_end;

	if (heap_end == 0)
		heap_end = &end;

	prev_heap_end = heap_end;
	if (heap_end + incr > stack_ptr)
	{
		errno = ENOMEM;
		return (caddr_t) -1;
	}

	heap_end += incr;

	return (caddr_t) prev_heap_end;
}

Here is good post explaining that - c++ - How to wrap functions with the `--wrap` option correctly? - Stack Overflow.

Try using the following -

/* Includes */
#include <errno.h>
#include <stdio.h>

#include <sys/types.h> // Added as a workaround for Bugzilla 158966

/* Variables */
extern int errno;
register char * stack_ptr asm("msp"); // <-- This is the line I changed.

/* Functions */

/**
 _sbrk
 Increase program data space. Malloc and related functions depend on this
**/
caddr_t _sbrk(int incr)
{
	extern char end asm("end");
	static char *heap_end;
	char *prev_heap_end;

	if (heap_end == 0)
		heap_end = &end;

	prev_heap_end = heap_end;
	if (heap_end + incr > stack_ptr)
	{
		errno = ENOMEM;
		return (caddr_t) -1;
	}

	heap_end += incr;

	return (caddr_t) prev_heap_end;
}

Hi,

Thanks for that I will give it a try.

Regards
Rob

Hi aggarg,

The change to the sbrk fcn didn’t compile:

**** Building Selected Files of configuration Debug for project STM32H750B-DK 
Info: Internal Builder is used for build
arm-none-eabi-gcc "...
"Application\\User\\sysmem.o" 
..\Application\User\sysmem.c:57:17: error: invalid register name for 'stack_ptr'
   57 | register char * stack_ptr asm("msp");
      |                 ^~~~~~~~~
11:12:43 Build Failed. 1 errors, 0 warnings. (took 633ms)

It didn’t like the register name “msp”.

Don’t you hate computers!

Regards
Rob

/* Includes */

#include <errno.h>

#include <stdio.h>

#include <sys/types.h> // Added as a workaround for Bugzilla 158966

/* Variables */

extern int errno;

register char * stack_ptr asm("msp");

/* Functions */

/**

_sbrk

Increase program data space. Malloc and related functions depend on this

**/

caddr_t _sbrk(int incr)

{

extern char end asm("end");

static char *heap_end;

char *prev_heap_end;

if (heap_end == 0)

heap_end = &end;

prev_heap_end = heap_end;

if (heap_end + incr > stack_ptr)

{

errno = ENOMEM;

return (caddr_t) -1;

}

heap_end += incr;

return (caddr_t) prev_heap_end;

}

Apologies. Try this -

/* Includes */
#include <errno.h>
#include <stdio.h>

#include <sys/types.h> // Added as a workaround for Bugzilla 158966

/* Variables */
extern int errno;

/* Functions */

/**
 _sbrk
 Increase program data space. Malloc and related functions depend on this
**/
caddr_t _sbrk(int incr)
{
	extern char end asm("end");
	static char *heap_end;
	char *prev_heap_end;
	char * stack_ptr;

	if (heap_end == 0)
		heap_end = &end;

	__asm volatile ("MRS %0, msp" : "=r" (stack_ptr) );

	prev_heap_end = heap_end;
	if (heap_end + incr > stack_ptr)
	{
		errno = ENOMEM;
		return (caddr_t) -1;
	}

	heap_end += incr;

	return (caddr_t) prev_heap_end;
}

Also, which version of STM32CubeIDE did you use to generate this project? The version 1.14.0 seems to generate _sbrk which does not use stack pointer and therefore, would not have this issue.

Hi aggard,

I generated the project using the TouchGFX builder I think it uses 6.12.0 but I’m not sure. The updates for all ST’s tools come thick and fast so it is hard for me, (and I think them) to keep up.

I will give your sugggestion a try. I am in the middle of doing another “clean” buld using TouchGFX as I have butchered my previous attemp.

Regards
Rob

or you maybe could try this:

put it in a source file into your project and overwrite the normal malloc/new functions

i had crashes when i used the STL and some containers malloced memory in my tasks. it came to obscure crashes. with this i could fix it.

but to be honest: after reading the sbrk stuff here i’m not quite sure if it was just by coincidence and if this is realy the suggested way to handle the dynamic memory

#include "FreeRTOS.h"

static uint32_t u32_totalAllocated = 0;

void *operator new( size_t size )
{
  u32_totalAllocated += size;
  //log_debug( "Mem::new %.3d total allocated: %d", size, u32_totalAllocated );
  return pvPortMalloc( size );
}

void *operator new[]( size_t size )
{
  //log_debug( "Mem::new[] %.3d total allocated: %d", size, u32_totalAllocated );
  return pvPortMalloc( size );
}

void operator delete( void *ptr )
{
  // log_debug("delete");
  vPortFree( ptr );
}

void operator delete[]( void *ptr )
{
  // log_debug("delete[]");
  vPortFree( ptr );
}