cancel
Showing results for 
Search instead for 
Did you mean: 

Char* in struct not being written to RAM and hard faults when pointer tries to change value

dboland
Associate II

I am encountering an issue where a hard fault occurs when the "char * pszCommand" variable is written to because it is trying to change data that is not in the RAM.

Here is the related code that gives me the hard fault:

typedef struct
{
	char * pszCommand; // command string
	void (* pfnHandler)(Parsed_t * ParsedCmd); // handler function
	int iParent; // index of command one level up in tree (if any)
	int nRequiredLength; // number of significant chars in command
	int nTreeLevel; // this commands depth in menu tree
	unsigned short bBranchEnd:1; // set if this command is the end of branch
	unsigned short bQueryAllowed: 1; // set if this command accepts queries
} CmdTreeEntry_t;


CmdTreeEntry_t * pTreeEntry;
// initialize the root entry
pTreeEntry = &g_CmdTree[0];
*pTreeEntry->pszCommand = ITEM_SEPARATOR;  <------ The hard fault occurs after execution of this line
pTreeEntry->iParent = -1;
pTreeEntry->nRequiredLength = 1;
pTreeEntry->nTreeLevel = 1;
pTreeEntry->bBranchEnd = FALSE;
pTreeEntry->bQueryAllowed = FALSE;

CmdTreeEntry_t g_CmdTree[] =
{
    /* command string, handler */
    { " ", NULL }, /* MUST BE FIRST ENTRY IN TABLE ! */

    { ":*CAL??",    calibration_query},
    { ":*CLS",      clear_status_cmd},
    .... (A bunch of commands)

    { NULL, NULL} /* MUST BE LAST ENTRY IN TABLE!!! */
};

I believe this is somehow related to the linker file not allocating the memory properly for the pszCommand pointer, but I am unable to find where the issue may be in the linker. 
Here is the linker file:

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */

_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Memories definition */
MEMORY
{
  RAM    (xrw)    : ORIGIN = 0x20000000,  LENGTH = 64K
  FLASH  (rx)     : ORIGIN = 0x8000000,   LENGTH = 512K
}

/* Sections */
SECTIONS
{
  /* The startup code into "FLASH" Rom type memory */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH

  /* The program code and other data into "FLASH" Rom type memory */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH

  /* Constant data into "FLASH" Rom type memory */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH

  .ARM.extab   : {
    . = ALIGN(4);
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } >FLASH

  .ARM : {
    . = ALIGN(4);
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
    . = ALIGN(4);
  } >FLASH

  .preinit_array     :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    . = ALIGN(4);
  } >FLASH

  .init_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(4);
  } >FLASH

  .fini_array :
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(4);
  } >FLASH

  /* Used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections into "RAM" Ram type memory */
  .data :
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */
    *(.RamFunc)        /* .RamFunc sections */
    *(.RamFunc*)       /* .RamFunc* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */

  } >RAM AT> FLASH

  /* Uninitialized data section into "RAM" Ram type memory */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss section */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM

  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM

  /* Remove information from the compiler libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}

This is the memory address of pTreeEntry that is being placed in RAM but the address of pszCommand is not in RAM.

dboland_0-1687445848394.png

Any help would be appreciated.

Thanks,

Dan

13 REPLIES 13

Or make a structure which allocates space? And strcpy() or strcat() into that given sufficient space.

typedef struct
{
	char Command[16]; // command string
	void (* pfnHandler)(Parsed_t * ParsedCmd); // handler function
	int iParent; // index of command one level up in tree (if any)
	int nRequiredLength; // number of significant chars in command
	int nTreeLevel; // this commands depth in menu tree
	unsigned short bBranchEnd:1; // set if this command is the end of branch
	unsigned short bQueryAllowed: 1; // set if this command accepts queries
} CmdTreeEntry_t;

Pointers could be used with malloc/free type use of dynamic memory, or from a pool, but got to watch how you free up resources, and not leak, etc.

You can create systems that don't fragment, but you need to release everything you allocate

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Pavel A.
Evangelist III

Maybe you're confusing char vs string constants -   ':' vs ":"

The first one is a char, the 2nd is a pointer (maybe even points to read-only memory).

Of course one cannot assign a pointer to element of a char array.

dboland
Associate II

I found a solution that works, thanks to a coworker of mine that had some helpful insight. I created a new function that will allocate RAM space for the character and copy it from ROM.

static char * ROMtoRAM(char pszCommandContents[]){
	size_t len = strlen(pszCommandContents) + 1;
	char * target = malloc(len);
	if (target == NULL) return NULL;
	memcpy(target, pszCommandContents, len);
	return target;
}

 With this function I can change where pTreeEntry->pszCommand is being pointed to and can actually write to the allocated memory.

pTreeEntry->pszCommand = ROMtoRAM(pTreeEntry->pszCommand);
*pTreeEntry->pszCommand = ITEM_SEPARATOR;

I think this is acceptable to use so that it doesn't hard fault.

Pavel A.
Evangelist III

:face_with_head_bandage: