Linker and Stack Painting Questions

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

I'm currently working on a rather large project for a SAM4E16C (128Kb RAM). In order to get a rough idea on how much stack my program is using I am using a small stack painter. Therefor, I have included the following symbols from the linker-script into my program and extracted the section addresses:

 

// Linker-Symbols
extern uint32_t _srelocate;
extern uint32_t _erelocate;
extern uint32_t _sbss;
extern uint32_t _ebss;
extern uint32_t _sstack;
extern uint32_t _estack;
extern uint32_t __ram_end__;

// Linker-Section-Addresses:
#define DATA_START_ADDR (&_srelocate)
#define DATA_END_ADDR (&erelocate)
#define BSS_START_ADDR (&_sbss)
#define BSS_END_ADDR (&_ebss)
#define STACK_START_ADDR (&_sstack)
#define STACK_END_ADDR (&_estack)
#define RAM_END_ADDR (&__ram_end__)

 

Based on this stumbled across the following questions:

 

1. In the build output Atmel Studio prints 3584 Bytes as the size of the data-section and 75344 Bytes as the size of the bss-section. However, if I subtract the start and end address coming from the linker symbols, I only get 2872 Bytes for data and 42572 Bytes for bss. Why is there such a huge difference?

 

2. From the linker symbols I can see that the stack is placed directly after the bss-section in RAM (BSS_END_ADDR = STACK_START_ADDR + 1), leaving space after the stack. I would prefer to place the stack at the end of the RAM. Is this possible and, if yes, how can I do this?

 

3. In the linker script that I'm currently using (see below, I think it’s the default script for this chip), there is no section for the heap. However, my code does not fail even though it does some dynamic memory allocation. Is the heap section added somewhere else or is the free memory automatically reserved for the heap? Would it make sense to add a heap section to the linker script or is it okay the way it is? Would it still be okay if I move the stack at the end of the RAM?

 

4. In the following you can see my stack painter and stack counter functions (it is based on the AVR version from here: https://www.avrfreaks.net/forum/soft-c-avrgcc-monitoring-stack-usage). Do you see any problems with the implementation or do you have any improvement suggestions?

 

#define STACK_CANARY 0xc5

uint32_t StackPaint() {

	// Read the current stack pointer.
	uint32_t currentstackPointer = __get_MSP();
	// Create a pointer.
	volatile uint8_t *p = currentstackPointer;
	// Calculate the current stack size.
	uint32_t initStackSize = (uint32_t)(&_estack) - currentstackPointer;

	// Paint the stack.
	while(p >= &_sstack) {
		*p = STACK_CANARY;
		p--;
	}

	// Return current stack size.
	return initStackSize;
}

uint32_t StackCount(){

	// Create a pointer.
	volatile const uint8_t *p = &_sstack;
	uint32_t       c = 0;

	// Count remaining canaries.
	while(*p == STACK_CANARY && p <= &_estack) {
		p++;
		c++;
	}

	// Return remaining canaries.
	return c;
}

 

Thank you in advance!

 

Marc

Attachment(s): 

This topic has a solution.
Last Edited: Fri. Sep 17, 2021 - 08:59 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1. 75344  includes the stack maybe? You can always check the output .lss  file for actual section sizes (or readelf -S on the .elf).

2. You can move the stack but you would have to change the heap implementation (and/or the linker script):

extern caddr_t _sbrk(int incr)
{
    static unsigned char *heap = NULL;
    unsigned char *prev_heap;
    int ramend = (int)&__ram_end__;

    if (heap == NULL) {
        heap = (unsigned char *)&_end;
    }
    prev_heap = heap;

    if (((int)prev_heap + incr) > ramend) {
        return (caddr_t) -1;	
    }

    heap += incr;

    return (caddr_t) prev_heap;
}

 

 

3. The heap starts at _end and goes to the end of ram (if you also have the above _sbrk implementation in syscalls.c).

4. Looks ok (I have not tested it).

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just note also that, e.g., this expression 

DATA_END_ADDR - DATA_START_ADDR

for computing the size of a segment is pointer arithmetic, you will not a get a byte size from that when you have uint32_t types for _srelocate etc. Change to uint8_t to fix that.

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey Lars!

 

Thank you for your answers!

 

You are absolutely right with your assumption regarding 1. The size for data-section given by Atmel Studio includes the stack. This gives me some extra space in RAM, as I have previously thought the stack would require additional space besides the memory given for the data-section. With this extra space it is also possible for me to keep the RAM structure, with the stack located before the heap. Nevertheless, thank you for the code to change the heap implementation. I'm sure this will become handy in future projects.

 

Regarding 3: I've had a look on _end and it makes perfectly sense what you are saying. So the section between _end and __ram_end__ is automatically used for the heap? There is no need to list the heap in the linker script?

 

Thank you for the hint regarding the pointer arithmetic. Luckily, I did the subtraction of the memory addresses manually. :-)

 

Marc