Calling _start() hangs my STM32F4

Asked by Chuck McManis

I'm doing a really basic example to prove to myself that I understand what is happening, and successfully proving to myself that I do not :-) At it's simplest I have written a 'blink' application which blinks LEDs on the Discovery board. I am compiling with minimal switches (-g -mcpu=cortex_m4 -mthumb ) I'm linking with minimum switches (-T blink.ld -Map blink.map -o blink.elf) I have two source files, startup.s which is the .section isr_vector in it and defines the ISR vectors, blink.c has main() and Reset_Handler which simply calls main(). The .ld script is a renamed copy of the share/arm-non-eabi/samples/ldscripts/gcc.ld script with the FLASH and RAM defines altered to reflect where FLASH and RAM are on the STM32F4.

Works fine, Reset_Handler is the defined ENTRY in gcc.ld and if it then calls main() the code runs as expected.

I wanted to verify that initialized data worked (or didn't). So I added a global 'int delay_timer = 100000;' to the blink.c file.

On reset it has random data in it. I can stop in GDB, initialize it, and the code works as expected. I can initialize it from Reset_Handler and the code works as expected, what I can't do is have Reset_Handler call _start() and get data initialized. Instead it spins in the hard fault area.

I called start because I've seen several references to 'if you call _start it is handled for you' but that isn't working for me, so I'm wondering what I'm missing. In the LD script is a commented out section which reads:

    /* To copy multiple ROM to RAM sections,
     * uncomment .copy.table section and,
     * define __STARTUP_COPY_MULTIPLE in startup_ARMCMx.S */
    /*
    .copy.table :
    {
        . = ALIGN(4);
        __copy_table_start__ = .;
        LONG (__etext)
        LONG (__data_start__)
        LONG (__data_end__ - __data_start__)
        LONG (__etext2)
        LONG (__data2_start__)
        LONG (__data2_end__ - __data2_start__)
        __copy_table_end__ = .;
    } > FLASH
    */

Uncommenting it causes the linker to fail on the LD script with 'invalid data statement', if it worked I could imagine that pre-initializing the data would be a simple act of copying from __copy_table_start__ for length __datalength__ but that isn't quite right. I delete the seperate etext2 stuff and that lets the linker pass, and my elf file gets a copy_table section (woot) and it seems to have useful data in it but that section doesn't get copied?

I feel like I'm really close on this, but it is just outside the documentation.
--Chuck

Question information

Language:
English Edit question
Status:
Answered
For:
GNU Arm Embedded Toolchain Edit question
Assignee:
No assignee Edit question
Last query:
Last reply:
Revision history for this message
Chuck McManis (cmcmanis) said :
#1

Probably bad form to add to my own question but thanks to other questions in similar areas here I decided to dump my own startup.s and used the startup_ARMCM4.S from the samples directory. If I edit the gcc.ld script to "fix" the problem with multiple text and bss sections defined, and I comment out the line in which has 'bl __START' because it complains __START is undefined, even after the #defined of __START to _start (which is very weird, are #defined variables not substituted in .S files?) and the defined SystemInit() to just call main() it works as I expect. Not exactly "out of the box" but the code seems to do the expected thing.

--Chuck

Revision history for this message
Terry Guo (terry.guo) said :
#2

Is it possible for you to share your startup.s, blink.c and gcc.ld? You can reduce them to a smaller one.

Revision history for this message
Chuck McManis (cmcmanis) said :
#3

Terry, all the sources are here: https://github.com/ChuckM/arm-blink-example

the only changes to gcc.ld (renamed to blink.ld) are the copy_table, zero_table stuff, and I had to comment out the __START in startup_ARMCM4.S to stop the error that __START was not defined.

Revision history for this message
Chuck McManis (cmcmanis) said :
#4

Ok, so I've played around with this and resolved it to my satisfaction. I the Reset_Handler is calling System_Init(). And I've updated it with the code that copies data around. So I've resolved my issue without having to call _start() and feel like I have a much better idea about how the code gets to where it is going.

Linkerscript provides -> Reset_Handler() as ENTRY point

startup_ARMCM4.S provide -> Reset_Handler() as implementation and uses the linker script provided data_start/data_end symbols to do the copy.

Reset_Handler by default hands off to SystemInit() which can then can call main().

What doesn't work is if I use the provided example code in startup_ARMCM4.S which has this code:

#ifndef __START
#define __START _start
#endif
    bl __START

For reasons that are unclear to me first, the linker complains that __START isn't defined (the #define should have changed it to _start but I'm wondering if cpp is being run on it. Note that in the ./samples/minimum makefile it uses a forced -D__START=main rather than rely on the startup script and in retarget the same thing is done.

However, I did try doing a make of retarget after getting rid of the -D__START and it correctly converted over to _start. So I'm sure there is some c/linker flag that I'm missing in my Makefile

The other confounding factor is that I've been trying to use libopencm3 (loc3) (I'm going to try ST Micro's Perhipheral library instead) to get the #defines for the various registers, loc3 has its own ideas about startup and interrupt vectors which I may be incompletely neutering.

Revision history for this message
Joey Ye (jinyun-ye) said :
#5

_start is defined as part of GCC library, which has no reason not defined. Did you use linker option to skip system libraries, which also ignores GCC libraries as well?

Can you help with this problem?

Provide an answer of your own, or ask Chuck McManis for more information if necessary.

To post a message you must log in.