views:

551

answers:

2

I'm trying to program ARM using Eclipse + CDT + yagarto (gnu toolchain) + OpenOCD. In several sample projects (from yagarto site for example) I found linker scripts (*.ld) where a lot of linking information specified (along with sections definitions). Actually I haven't faced this files before (IAR doesn't need them), and I find them somewhat difficult to understand from a first glance. So my question is can I use one single such script file for my target processor (STR710FZ2T6) with all my projects or I have to get familiar in writing this scripts and write them for each project. If I can use single file for all projects for particular target processor can you please advice where I can find such universal one.

A: 

There is not a universal linker script. These scripts are very important, since they define where in memory (RAM or ROM) the various data and program sections will be placed. There is something equivalent in IAR compilers (xcl files if I remember correctly). You've obviously only used the default ones until now.

There is a nice document about STR7xx called "Using Open Source Tools for STR7xx Cross Development". You can find a link in yagarto homepage. I recommend that you have a look at it and try to understand how linker files work. There are also some other configuration files that you need to have some understanding of.

kgiannakakis
+2  A: 

My guess is every third person has a different script or solution. There are a number of problems that have to be solved, different linkers are going to solve those in different ways. I think GNU has made it way too difficult if not black magic.

For an embedded system you are often going to have a flash or eeprom or some other form of read only memory to boot from. Like other processors the ARM has a vector table to tell it essentially where the reset code is and interrupt, etc. So that table has to be in a specific place and you have to tell the linker to put it in that specific place (first).

One of the scripts I like to use is:

MEMORY
{
    bob (RX) : ORIGIN = 0x0000000, LENGTH = 32K
    joe (WAIL) : ORIGIN = 0x2000000, LENGTH = 256K
}

SECTIONS
{
    JANE : { startup.o } >bob
}

I usually use ram and rom as names instead of bob and joe but demonstrating here that it doesnt matter what the names are they are just labels.

Another variation on the theme:

MEMORY
{
    rom(RX)   : ORIGIN = 0x00000000, LENGTH = 0x8000
    ram(WAIL) : ORIGIN = 0x20000000, LENGTH = 0x2000
}

SECTIONS
{
    .text : { *(.text*) } > rom
}

The first one allows you to put the files on the linker command line in any order but you have to have the vector table in the startup.o file. The latter lets you use whatever filenames but the first file on the linker script needs to have the vector table.

arm-thumb-elf-gcc -Wall $(COPS) vectors.o putget.o blinker2.c -T memmap -o blinker2.elf

Or with ld directly

arm-thumb-elf-ld vectors.o putget.o blinker2.o -T memmap -o blinker2.elf

The RX tells the linker to put read and execute stuff in that memory section and the WAIL is basically everything else. If you only have one ram for example you can put all the flags RXWAIL on the line that tells where the ram is. Depending on your loader in that case you can rely on the elf file telling the loader where to branch to to start or you can simply make the entry point the beginning of the binary and the loader can be simpler. The arms (not the cortex-m3) have a branch instruction as the first vector for the reset vector so you can just pretend to build a vector table anyway for a ram solution and will just work.

The are a number of problems with this solution that dont happen to bother me. I initialize variables in my code, not during declaration.

This

int rx;

int main ( void )
{
  rx = 7;

instead of

int rx=7;

int main ( void )
{

I also never assume that a variable is zero when the code starts I always initialize it to something before I start. Your startup code plus linker script as a team can work together to make it easier to automate the resetting of the bss code and copying non-zero init data from rom to ram on boot. (that int rx=7; above requires some code that copies the value 7 from somewhere in rom and writes it to the memory location in ram allocated for the variable rx so that when main() starts the 7 is there.

My boot code is also quite simple as a result of this method:

.globl _start
_start:
    b reset
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang
    b hang

hang : b hang

reset:
    ldr sp,=0x10004000
    bl main
    b hang

You are going to see or read about solutions that allow the startup code and the linker script to work together to not have to hardcode stack pointers, heap space, things like that, again you can put a lot of work into complicated startup code and linker scripts to gain some automation and perhaps save some work, perhaps not. The automation, if/when working can and will reduce human error and that can be a good thing, also if you are switching chips often or trying to write one bit of code that works in a family of chips you may want this automation as well.

My bottom line is YES you can live with only one linker script for all of your ARM work. But you have to tailor your work to that script. You are likely not going to find one script that works with everyones example code. The more complicated the script the harder it will be to borrow. Yes, my scripts above can likely be done on the ld command line, but way back when (gcc 2.95) I couldnt get that to work so developed the minimal script above and have been using them ever since. Had to modify to the second script for some reason but with 4.x.x, certainly 4.4.x I am able to use either one.

dwelch