EmbDev.net

Forum: ARM programming with GCC/GNU tools Linking order and startup files


von Oliver B. (irq)


Rate this post
useful
not useful
Hi,

I'm comparing linker scripts for an ARM chip and wonder how to integrate 
startup code properly. It is generated from an assembler file which is 
transformed into object code like the other c-files.

After compiling the linker is called in the Makefile:

$(CC) $(THUMB) $(CFLAGS) $(ALLOBJ) --output $@ $(LDFLAGS)

The used variables should be self-explaining, $(AllOBJ) referres to all 
object files meaning that of the previous assembler and c-files, which 
are placed in the same directory.

Now I'm asking myself, what the output (ELF) does look like - is the 
startup code really executed first and especially WHY ?

Maybe I could get it working by trial and error but I would really be 
interested how it actually works. Can you tell me or do you know 
ressources regarding this issue?

THANKS!


-- -- -- --

Further information: I'm comparing two ld-scripts, the first one seems 
to ignore startup related stuff and the second one includes it manually:

First:
--
1
.text :
2
    {
3
        . = ALIGN(0x80); /* PM0056, Rev.1 (4/2009), 4.3.3 */
4
        _isr_vectorsflash_offs = . - 0x08000000;
5
        KEEP(*(.isr_vectorsflash))
6
        . = ALIGN(4);
7
        *(.text)                   /* code */
8
        *(.text.*)                 /* remaining code (-ffunction-sections)*/
9
        *(.stub .gnu.linkonce.t.*)
10
        *(.rodata .rodata.* .gnu.linkonce.r.*) /* read-only data (constants) */
11
        *(.glue_7 .glue_7t)        /* redundant for thumb2 ?! */
12
        . = ALIGN(4);
13
        _etext = .;
14
        /* This is used by the startup in order to initialize the .data secion */
15
        _sidata = _etext;
16
    } >FLASH

Second:
--
1
.text :
2
   {
3
      KEEP(*(vector vector.*))
4
      __startup_code__ = .;
5
            $(TargetDir)startup.o (.text)         /* Startup code */
6
      __startup_code_end__ = .;
7
      *(.text .text.*)
8
      *(.gnu.linkonce.t.*)
9
      *(.glue_7)
10
      *(.glue_7t)
11
      *(.gcc_except_table)
12
      . = ALIGN(4);
13
      *(.rodata .rodata*)
14
      *(.gnu.linkonce.r.*)
15
      . = ALIGN(4);
16
      _etext = .;
17
   } > FLASH

von Martin T. (mthomas) (Moderator)


Rate this post
useful
not useful
> Now I'm asking myself, what the output (ELF) does look like
The GNU tools offer some tools to "look" into the elf-file. I'm usually 
just using the disassembly to verify things. It can be generated with 
objdump (make calls objdump to generated it as *.lss when using my 
"WinARM makefile template).

> - is the startup code really executed first
First of all the Controller looks for the reset-address and "jumps" 
(sets PC) to this address. Since you are using a Cortex-M3 the 
reset-address is expected at Address 0x00000004 (after the initial stack 
pointer address at 0x00000000). It's not important where the 
startup-code is located as long as it starts at the address given at 
0x04.

>and especially WHY ?
Because it's address of the startup is given.

> Maybe I could get it working by trial and error but I would really be
> interested how it actually works.
"The prove of the pudding is the eating." (Samba Documentation)

> Can you tell me or do you know
> ressources regarding this issue?
gcc Manual, binutils/ld Manual, Cortex M3 Technical 
Reference/Programming Guide

> -- -- -- --
>
> Further information: I'm comparing two ld-scripts, the first one seems
> to ignore startup related stuff and the second one includes it manually:
Why do you think "startup related stuff" is ignored?

> First:
> --
>
1
> .text :
2
>     {
3
>         . = ALIGN(0x80); /* PM0056, Rev.1 (4/2009), 4.3.3 */
4
>         _isr_vectorsflash_offs = . - 0x08000000;
5
>         KEEP(*(.isr_vectorsflash))
6
>         . = ALIGN(4);
7
>         *(.text)                   /* code */
8
>         *(.text.*)                 /* remaining code
9
> (-ffunction-sections)*/
10
>         *(.stub .gnu.linkonce.t.*)
11
>         *(.rodata .rodata.* .gnu.linkonce.r.*) /* read-only data
12
> (constants) */
13
>         *(.glue_7 .glue_7t)        /* redundant for thumb2 ?! */
14
>         . = ALIGN(4);
15
>         _etext = .;
16
>         /* This is used by the startup in order to initialize the .data
17
> secion */
18
>         _sidata = _etext;
19
>     } >FLASH
20
>
I know this script from somewhere...
As you can see the all important input section with the reset-vector 
(.isr_vectorsflash) is located first in the FLASH output-section. Since 
the exceptions table is the only element in the input-section and the 
reset-handler's address is the 2nd Element in the table the controller 
finds the pointer to the startup-code. In this case the startup-code is 
just treated as any other code (.text) since it's not important where it 
is located.

> Second:
> --
>
1
> .text :
2
>    {
3
>       KEEP(*(vector vector.*))
4
>       __startup_code__ = .;
5
>             $(TargetDir)startup.o (.text)         /* Startup code */
6
>       __startup_code_end__ = .;
7
>       *(.text .text.*)
8
>       *(.gnu.linkonce.t.*)
9
>       *(.glue_7)
10
>       *(.glue_7t)
11
>       *(.gcc_except_table)
12
>       . = ALIGN(4);
13
>       *(.rodata .rodata*)
14
>       *(.gnu.linkonce.r.*)
15
>       . = ALIGN(4);
16
>       _etext = .;
17
>    } > FLASH
18
>
Here the input-section with the vector-table is named vector instead of 
.isr_vectorsflash in the previous example but the concept is the same: 
The Reset-Hander Address in the vector still hast to point the the 
startup machine-code. The only "real" difference I see is, that the 
.text input-section in the object file startup.o are explicitly placed 
right after the vector-table in memory (see binutils/ld manual). The 
remaining code in the .text input-section(s) follows. I can't tell why 
this approach has been chosen without looking into the reset of the 
code.

von Oliver B. (irq)


Rate this post
useful
not useful
Thank you for your explanations. It took me some time but now the basic 
principles of linking are a bit clearer to me.

I wrote my own very little linker script that works fine now, but I 
needed a little tweak:

The startup code I use is startup_stm32f10x_md.s, taken 100% from the 
STM Standard Peripherial Lib. As I said, it works fine, but I had to 
edit the isr_vector section:
1
.section  .isr_vector,"a",%progbits
2
.type  g_pfnVectors, %object
3
.size  g_pfnVectors, .-g_pfnVectors
4
    
5
    
6
g_pfnVectors:
7
  .word  _estack
8
  .word  (Reset_Handler+1)  <- PLUS 1 HERE

The disassembly showed me an address, that pointed directly to 
Reset_Handler, that didn't work. I rememberred this site:

http://209.85.135.132/search?q=cache:Sn58U6UOeBUJ:wiki.fosstronics.com/arm_cortex-m3/stm32/minimal-c-program+fosstronics+minimal&cd=1&hl=de&ct=clnk&gl=de&client=firefox-a

Which says, the initial PC must be the address + 1. I tried baking a 
cake without a recipe - and it tasted yummi... Knowing WHY this time, 
but not how to fix it properly. I do not guess STM has a bug in its 
code.

Here is my linker script:
1
MEMORY
2
{
3
  RAM      (RWX) : ORIGIN = 0x20000000, LENGTH = 20K
4
  FLASH    (RX)  : ORIGIN = 0x08000000, LENGTH = 128K
5
}
6
7
_estack = ORIGIN(RAM)+LENGTH(RAM);
8
9
SECTIONS
10
{
11
  .text :
12
  {
13
    KEEP(*(.isr_vector))
14
    *(.text)
15
    *(.text.*)
16
    _sidata = .;
17
  } >FLASH
18
19
  .data  : AT ( _sidata )
20
  {
21
    _sdata = . ;
22
    *(.data)
23
    _edata = . ;
24
  } >RAM
25
26
  .bss (NOLOAD):
27
  {
28
    _sbss = .;
29
    *(.bss .bss.*)
30
    *(COMMON)
31
    . = ALIGN(4);
32
     _ebss = . ;
33
  } >RAM
34
}

von Martin T. (mthomas) (Moderator)


Rate this post
useful
not useful
Oliver Behr wrote:
>...
>
1
> .section  .isr_vector,"a",%progbits
2
> .type  g_pfnVectors, %object
3
> .size  g_pfnVectors, .-g_pfnVectors
4
> 
5
> 
6
> g_pfnVectors:
7
>   .word  _estack
8
>   .word  (Reset_Handler+1)  <- PLUS 1 HERE
9
>
>
> The disassembly showed me an address, that pointed directly to
> Reset_Handler, that didn't work. I rememberred this site:
>
> 
http://209.85.135.132/search?q=cache:Sn58U6UOeBUJ:wiki.fosstronics.com/arm_cortex-m3/stm32/minimal-c-program+fosstronics+minimal&cd=1&hl=de&ct=clnk&gl=de&client=firefox-a

Even if it works the guide on this page is really minimal. Too minimal 
in my opinion.
- When using C the startup should at least copy data and zero-init.bss. 
Not needed in the code on the page since there is nothing in bss and 
data but as it is now it's not a good starting-point.
- It has been written >1e8 times: don't call ld directly use the 
frontend for linking too.

Is expect the main reason for the problem are missing options to the 
linker, maybe the linker does not "know" that it has to use thumb-style 
addressing and uses ARM-mode addressing instead. Since the options used 
for compiling/linking are not given: have -mcpu=cortex-m3 and -mthumb 
been passed as options for linking too?

von Oliver B. (irq)


Rate this post
useful
not useful
> Is expect the main reason for the problem are missing options to the
> linker, maybe the linker does not "know" that it has to use thumb-style
> addressing and uses ARM-mode addressing instead. Since the options used
> for compiling/linking are not given: have -mcpu=cortex-m3 and -mthumb
> been passed as options for linking too?

Hm, I played around with my compiler/linker options (copy'n paste from 
other Makefiles), but had no success. Yes, the m-options are supplied - 
at least to gcc. Is the "+1" thumb specific?

Here is my Makefile:
1
AFLAGS = -mcpu=cortex-m3 -mthumb -Wa,-gdwarf-2
2
CFLAGS = -mcpu=cortex-m3 -mthumb -O0 -gdwarf-2
3
LFLAGS = -T simple.ld -Wl,--gc-sections 
4
5
all:
6
  arm-elf-gcc -c $(AFLAGS) startup_stm32f10x_md.s
7
  arm-elf-gcc -c $(CFLAGS) main.c
8
  arm-elf-gcc    $(CFLAGS) startup_stm32f10x_md.o main.o $(LFLAGS)

Please log in before posting. Registration is free and takes only a minute.
Existing account
Do you have a Google/GoogleMail account? No registration required!
Log in with Google account
No account? Register here.