Linker Script for C++ on SAM7

sven-de wrote on Tuesday, August 02, 2011:

along my journey into embedded OO I stumbled upon another problem: The linker script doesn’t supply a .ctors-section and won’t link global objects. I consulted these two ressources:
But at the end of the day I realised, that I’d really need some time to understand how linker-script, startup and low-level-init work within as well as together. So I just copied this between prog: and .data:

.ctors :
		PROVIDE(__ctors_start__ = .);
		PROVIDE(__ctors_end__ = .);
	} >flash
	.dtors :
		PROVIDE(__dtors_start__ = .); 
		PROVIDE(__dtors_end__ = .);
	} >flash

But got more errors. I’ve a weak understanding what the code in the two above ressources does, but have absolutely no clue about those (sparsely commented) files supplied with FreeRTOS. So I tend to swap atmel-rom.ld, boot.s und syscalls.c with those from the second ressource. Is that possible, or do they currently contain anything specific to FreeRTOS I have to take care of? Or are there even more files involved, such as Cstartup_SAM7.c and Cstartup.s79 in SRCAtmel?

Is there any other way to achieve this without having to go deeply into GNU ld, and start-up procedures?

rtel wrote on Tuesday, August 02, 2011:

The linker script has nothing FreeRTOS specific as far as I know.

The start up file must set up a stack for both Supervisor and IRQ modes, as a minimum (FIQ too if you use that), and install vPortYieldProcessor() as the SWI interrupt handler.

There has been lots of discussion here on the topic of C++ in the past, and some C++ projects posted to the FreeRTOS Interactive site too.


sven-de wrote on Tuesday, August 02, 2011:

Richard, your response time makes me once again forget, that you aren’t earning a single dime with this support (except those few bucks I paid for the eBook). Thanks!

I suppose  with “start up file” you mean boot.s which apparently sets vPortYieldProcessor() as SoftwareInterrupt. But there’s another file ./SrcAtmel/Cstartup.s97 which does the same. May I assume, since it’s not linked, called or compiled anywhere, that this one is obsolete?

I was a bit confused to find LowLevelInit()-funtion in ./SrcAtmel/C_startup_SAM7.c_. All documentation I read until now treats low-level-init and startup-code as two different things - perhaps the naming could be changed with the next update of this demo.

Am I right, that syscalls.c has nothing to do with this? In 2009 you said that FreeRTOS doesn’t even use of any of it’s functions…

When this is my lucky day Richard Damon will soon come around the corner and post a platinum grade linker-script for C++ :wink:

rtel wrote on Tuesday, August 02, 2011:

Which directory are you getting these files from?  Just from the file name, I would say that Cstartup.s97 is probably an (old) IAR V.4x source file, and therefore should not be built with GCC.  It is a long time since I did any SAM7 stuff, but I think the demos in the FreeRTOS download are for IAR, unless I did an Eclipse version at some time too.


sven-de wrote on Tuesday, August 02, 2011:

Yes, I’m using the GCC/Eclipse demo which has SrcAtmel/ right in RTOSDemo/. And your guess is probably right since the head-comment in Cstartup.s97 mentions IAR.

rtel wrote on Tuesday, August 02, 2011:

Yes - ignore the .s79 file then - it is just part of the standard files received from Atmel, but not used in that particular demo.


sven-de wrote on Monday, August 08, 2011:

Hi, currently I’m swopping the demo’s linker-script and startup-code with the aforementioned code by Martin Thomas (second link).  While doing this I stumbled upon this section in the original linker-script of the SAM7 GCC-Demo:

	prog : 
	} >flash

It’s the prog that puzzles me. From other linkerscripts I’d expect an .text (note the dot) in it’s location instead. Since I couldn’t find anything about this on the net, I assume it’s some sort of custom section (which doesn’t matter as long it’s places in the right location). But is there any reason for not using the common .text here?

(sorry for questions this far from the actual rtos)

johandc wrote on Tuesday, August 09, 2011:

Here’s a script that works for C++.

    sram  : ORIGIN = 0x00000000,	LENGTH = 0x1000
    rom   : ORIGIN = 0x40000000,	LENGTH = 0x400000
    flash : ORIGIN = 0x48000000,	LENGTH = 0x400000
    ram   : ORIGIN = 0x50100000,	LENGTH = 0x100000
	. = 0;
	_sfixed = .;
    .text :
        . = 0x100;
        . = ALIGN(8);
        PROVIDE (__init_array_start = .);
		PROVIDE (__init_array_end = .);
    } >rom
    .ARM.extab (NOLOAD) : {
    	*(.ARM.extab* .gnu.linkonce.armextab.*)
    } >rom
    __exidx_start = .;
  	.ARM.exidx (NOLOAD) : {
  		*(.ARM.exidx* .gnu.linkonce.armexidx.*)
  	} > rom
   __exidx_end = .;
   _efixed = .;
    .data : {
		_data_lma = LOADADDR(.data);
        _data_start = .;
		_data_end = .;
    } >ram AT> rom
	.sram 0x40 : {
		_sram_lma = LOADADDR(.sram);
		_sram_start = .;
		_sram_end = .;
	} >sram AT> rom
    .bss (NOLOAD) : {
        _szero = .;
        . = ALIGN(8);
        _ezero = .;
    } >ram
    __heap_start__ = .;
    __heap_end__ = 0x50200000 - 8 - 40000;
    .ram_persist __heap_end__ (NOLOAD) : {
    } > ram
    _sstack = 0x50200000 - 8;
Then before starting a C++ function, or even before main do this:
	/* C++ static constructors */
	extern void (*__init_array_start []) (void) __attribute__((weak));
	extern void (*__init_array_end []) (void) __attribute__((weak));
	int count = __init_array_end - __init_array_start, i;
	for (i = 0; i < count; i++)

sven-de wrote on Monday, September 05, 2011:

Thank you all for your kind help. I finally took the abovementioned files from Martin Thomas an merged them mit the demo from Acutally I completely replaced the linker script, kept CstartupSAM7.c and just joined the two startup-files boot.s and Cstartup.S which resulted in the code below. It’s working.

//*-         ATMEL Microcontroller Software Support  -  ROUSSET  -
//* The software is delivered "AS IS" without warranty or condition of any
//* kind, either express, implied or statutory. This includes without
//* limitation any warranty or condition with respect to merchantability or
//* fitness for any particular purpose, or against the infringements of
//* intellectual property rights of others.
//*- File source          : Cstartup.s
//*- Object               : Generic CStartup for KEIL and GCC
//*- Compilation flag     : None
//*- 1.0 18/Oct/04 JPP    : Creation
//*- 1.1 21/Feb/05 JPP    : Set Interrupt
//*- 1.1 01/Apr/05 JPP    : save SPSR 
//* This file (along with the linker script and Cstartup_SAM7.c) was taken from
//* Martin Thomas due to it's support for C++. Originally it also allowed to
//* place parts of code as well as the vectors in ROM. This functionality was
//* removed by Sven Gruener in Aug/2011 while adapting it to FreeRTOS
.print "ROM-Version: Vectors at start of Code, just variables in RAM"
//*- Exception vectors 
//*- These vectors can be read at address 0 or at RAM address
//*- They ABSOLUTELY requires to be in relative addresssing mode in order to
//*- guarantee a valid jump. For the moment, all are just looping.
//*- If an exception occurs before remap, this would result in an infinite loop.
//*- To ensure if a exeption occurs before start application to infinite loop.
.print "Vectors in section .vectorg -> .text"
.section .vectorg, "ax"
			LDR     PC,Reset_Addr		/* 0x00 Reset handler */    
			LDR     PC,Undef_Addr		/* 0x04 Undefined Instruction */
			LDR     PC,SWI_Addr			/* 0x08 Software Interrupt */
			LDR     PC,PAbt_Addr		/* 0x0C Prefetch Abort */
			LDR     PC,DAbt_Addr		/* 0x10 Data Abort */
			NOP                 		/* 0x14 reserved  */
			LDR     PC,[PC,#-0xF20]		/* 0x18 IRQ */
			LDR		PC,FIQ_Addr			/* 0x1c FIQ */
Reset_Addr:		.word	InitReset
Undef_Addr:		.word	Undef_Handler
SWI_Addr:		.word	vPortYieldProcessor      /* in portISR.c */
PAbt_Addr:		.word	PAbt_Handler
DAbt_Addr:		.word	DAbt_Handler
/*IRQ_Addr:		.word	IRQ_Handler_Entry	*/
FIQ_Addr:		.word	FIQ_Handler
Undef_Handler:	B		Undef_Handler
PAbt_Handler:	B		PAbt_Handler
DAbt_Handler:	B		DAbt_Handler
FIQ_Handler:	B		FIQ_Handler
/*--- End of exception vectors ----------------------------------------------*/
.section .init, "ax"
.global _startup
.func   _startup
	.word	__TOP_STACK 
/*- Low level Init (PMC, AIC, ? ....) by C function AT91F_LowLevelInit
/*- minumum C initialization, call  AT91F_LowLevelInit( void) */
            .extern   AT91F_LowLevelInit
			ldr     sp, .RAM_TOP	/* temporary stack in internal RAM (**) */
/*--Call Low level init function in ABSOLUTE through the Interworking	*/
			ldr		r0,=AT91F_LowLevelInit
			mov		lr, pc
			bx		r0
//*- Stack size and location Definition
//*- Interrupt Stack requires 2 words x 8 priority level x 4 bytes when using
//*- the vectoring. This assume that the IRQ management.
//*- The Interrupt Stack must be adjusted depending on the interrupt handlers.
//*- The System stack size is not defined and is limited by the free internal
//*- SRAM.
	/* Stack Sizes */
   	.equ  UND_STACK_SIZE, 0x00000004
    .equ  ABT_STACK_SIZE, 0x00000004
    .equ  FIQ_STACK_SIZE, 0x00000004
    .equ  IRQ_STACK_SIZE, 0X00000400
    .equ  SVC_STACK_SIZE, 0x00000400
	/* Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs */
    .equ  MODE_USR, 0x10            /* User Mode */
    .equ  MODE_FIQ, 0x11            /* FIQ Mode */
    .equ  MODE_IRQ, 0x12            /* IRQ Mode */
    .equ  MODE_SVC, 0x13            /* Supervisor Mode */
    .equ  MODE_ABT, 0x17            /* Abort Mode */
    .equ  MODE_UND, 0x1B            /* Undefined Mode */
    .equ  MODE_SYS, 0x1F            /* System Mode */
    .equ  I_BIT, 0x80               /* when I bit is set, IRQ is disabled */
    .equ  F_BIT, 0x40               /* when F bit is set, FIQ is disabled */
//*- Setup the stack for each mode
    mov     r0, sp /* see (**) */
	msr   CPSR_c, #MODE_UND|I_BIT|F_BIT /* Undefined Instruction Mode */
    mov   sp, r0
    sub   r0, r0, #UND_STACK_SIZE
    msr   CPSR_c, #MODE_ABT|I_BIT|F_BIT /* Abort Mode */
    mov   sp, r0
    sub   r0, r0, #ABT_STACK_SIZE
    msr   CPSR_c, #MODE_FIQ|I_BIT|F_BIT /* FIQ Mode */
    mov   sp, r0
    sub   r0, r0, #FIQ_STACK_SIZE
    msr   CPSR_c, #MODE_IRQ|I_BIT|F_BIT /* IRQ Mode */
    mov   sp, r0
    sub   r0, r0, #IRQ_STACK_SIZE
    msr   CPSR_c, #MODE_SVC|I_BIT|F_BIT /* Supervisor Mode */
    mov   sp, r0
    sub   r0, r0, #SVC_STACK_SIZE
    msr   CPSR_c, #MODE_SYS|I_BIT|F_BIT /* System Mode */
    mov   sp, r0
	/* We want to start in supervisor mode.  Operation will switch to system
	mode when the first task starts. */
				mov     sp, r0			/* Init stack Sup */
//*- Relocation of .data section (ROM->RAM)
/* Relocate .data section (Copy from ROM to RAM) 
   This will also copy the .vectmapped and .fastrun */
                LDR     R1, =_etext
                LDR     R2, =_data
                LDR     R3, =_edata
LoopRel:        CMP     R2, R3
                LDRLO   R0, [R1], #4
                STRLO   R0, [R2], #4
                BLO     LoopRel
//*- Clear .bss section (Zero init)
                MOV     R0, #0
                LDR     R1, =__bss_start__
                LDR     R2, =__bss_end__
LoopZI:         CMP     R1, R2
                STRLO   R0, [R1], #4
                BLO     LoopZI
//*- call C++ constructors of global objects
		LDR 	r0, =__ctors_start__
		LDR 	r1, =__ctors_end__
		CMP 	r0, r1
		BEQ 	ctor_end
		LDR 	r2, [r0], #4
		STMFD 	sp!, {r0-r1}
		MOV 	lr, pc
/*		MOV 	pc, r2 */
		BX r2 /* mthomas 8/2006 */
		LDMFD 	sp!, {r0-r1}
		B 		ctor_loop
//*- call main()
		ldr	lr,=exit
		ldr	r0,=main
		bx	r0
        .size   _startup, . - _startup
/* "exit" dummy added by mthomas to avoid sbrk write read etc. needed
   by the newlib default "exit" */
        .global exit
        .func   exit
        b    .
        .size   exit, . - exit