Like I mentioned in the previous post there is something more sophisticated to organize the memory for our program, the linker scripts allows us to do that. lets go back to the most simple example, again…
unsigned long var1;
unsigned long var2 = 34;
int main( void )
{
return 0;
}
As you remember the previous code will be organized by the compiler in three different sections .text for the code, .data for initialized global variables & .bss for non initialized global variables, just like this
$ arm-none-eabi-gcc -c main.c -o main.o -mcpu=cortex-m0plus
$ arm-none-eabi-objdump -h main.o
main.o: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000000c 00000000 00000000 00000034 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000004 00000000 00000000 00000040 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000004 00000000 00000000 00000044 2**2
ALLOC
3 .comment 0000001f 00000000 00000000 00000044 2**0
CONTENTS, READONLY
4 .ARM.attributes 0000002c 00000000 00000000 00000063 2**0
CONTENTS, READONLY
Now is time for a little experiment. Enter the linker script. Create a file and named linker.ld with the following content. What you are seeing is a declaration of a output sections we called code, inside this we put all the information belong to object file main.o.
SECTIONS
{
code : { main.o }
}
link it… but, this time using the linker file we just created using the flag -T<name of the file.ld>
.
$ arm-none-eabi-gcc main.o -o main.elf -nostdlib -e main -mcpu=cortex-m0plus -Tlinker.ld
$ arm-none-eabi-objdump -h main.elf
main.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
0 code 0000005e 00000000 00000000 00010000 2**2
CONTENTS, ALLOC, LOAD, CODE
Wait!!!!, where all the sections went??, what happened is that we create an output section named code and we put all the contents from object file main.o inside ( just like a previously said ), it is easy to verify, just pay attention to column Size, add all the sections and you will see the result is 0x5e. But there is a more effective way to verify what I’m saying. Generating a Map file.
$ arm-none-eabi-gcc main.o -o main.elf -nostdlib -e main -mcpu=cortex-m0plus -Tlinker.ld -Wl,-Map=main.map
Open the new generated file main.map, its content should look like down below. What a wonderful piece of information we have. Focus your attention from line 9 to line 24. We can see all the input sections from the main.o file inside the output section code we just declare in linker.ld, in the second column we can found the starting address and notice are one after the other no overlapping. Next column shows the section Size and finally we can observe the symbols ( functions and variables ) stored in each input section. Look at this!!, we can finally verify var2 is in .data and var1 is in .bss . Go ahead and declare more variables and functions and you will see what happens.
Memory Configuration
Name Origin Length Attributes
*default* 0x0000000000000000 0xffffffffffffffff
Linker script and memory map
LOAD main.o
code 0x0000000000000000 0x5e
main.o()
.text 0x0000000000000000 0xc main.o
0x0000000000000000 main
.data 0x000000000000000c 0x4 main.o
0x000000000000000c var2
.bss 0x0000000000000010 0x4 main.o
0x0000000000000010 var1
.comment 0x0000000000000014 0x1e main.o
0x1f (size before relaxing)
.ARM.attributes
0x0000000000000032 0x2c main.o
OUTPUT(main.elf elf32-littlearm)
LOAD linker stubs
.glue_7 0x0000000000000060 0x0
.glue_7 0x0000000000000060 0x0 linker stubs
.glue_7t 0x0000000000000060 0x0
.glue_7t 0x0000000000000060 0x0 linker stubs
.vfp11_veneer 0x0000000000000060 0x0
.vfp11_veneer 0x0000000000000060 0x0 linker stubs
.v4_bx 0x0000000000000060 0x0
.v4_bx 0x0000000000000060 0x0 linker stubs
But this is not exactly what we want, we need somehow be able to set the different sections into specific addresses, we need more control, more granularity. OK lets do it in an orderly fashion, this time put only the actual code ( the .text section ) from main.o inside the section code we declare, just like this
SECTIONS
{
code : { main.o (.text) }
}
Take a look at the header information, now we can see the rest of the sections but not the .text because it is inside output section code
$ arm-none-eabi-gcc main.o -o main.elf -nostdlib -e main -mcpu=cortex-m0plus -Tlinker.ld -Wl,-Map=main.map
$ arm-none-eabi-objdump -h main.elf
main.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
0 code 0000000c 00000000 00000000 00010000 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000004 0000000c 0000000c 0001000c 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000004 00000010 00000010 00010010 2**2
ALLOC
3 .comment 0000001e 00000000 00000000 00010010 2**0
CONTENTS, READONLY
4 .ARM.attributes 0000002c 00000000 00000000 0001002e 2**0
CONTENTS, READONLY
Open the main.map file, do not pay attention to the weird sections ( .glue7, etc.. ) focus your attention on the sections .data, .text and .bss. Only .text is inside the output code section ( line 10 to 13 )
Memory Configuration
Name Origin Length Attributes
*default* 0x0000000000000000 0xffffffffffffffff
Linker script and memory map
LOAD main.o
code 0x0000000000000000 0xc
main.o(.text)
.text 0x0000000000000000 0xc main.o
0x0000000000000000 main
OUTPUT(main.elf elf32-littlearm)
LOAD linker stubs
.data 0x000000000000000c 0x4
.data 0x000000000000000c 0x4 main.o
0x000000000000000c var2
.glue_7 0x0000000000000010 0x0
.glue_7 0x0000000000000010 0x0 linker stubs
.glue_7t 0x0000000000000010 0x0
.glue_7t 0x0000000000000010 0x0 linker stubs
.vfp11_veneer 0x0000000000000010 0x0
.vfp11_veneer 0x0000000000000010 0x0 linker stubs
.v4_bx 0x0000000000000010 0x0
.v4_bx 0x0000000000000010 0x0 linker stubs
.bss 0x0000000000000010 0x4
.bss 0x0000000000000010 0x4 main.o
0x0000000000000010 var1
.comment 0x0000000000000000 0x1e
.comment 0x0000000000000000 0x1e main.o
0x1f (size before relaxing)
.ARM.attributes
0x0000000000000000 0x2c
.ARM.attributes
0x0000000000000000 0x2c main.o
One more time in your linker script lets declare more output sections one per input section from the main.o file and specify the starting address for each of them
SECTIONS
{
code 0x1000 : { main.o (.text) }
dato 0x2000 : { main.o (.data) }
vars 0x3000 : { main.o (.bss) }
}
The header looks like this: ( pay attention to VMA addresses )
arm-none-eabi-objdump -h main.elf
main.elf: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
0 code 0000000c 00001000 00001000 00001000 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 dato 00000004 00002000 00002000 00002000 2**2
CONTENTS, ALLOC, LOAD, DATA
2 vars 00000004 00003000 00003000 00002004 2**2
ALLOC
3 .comment 0000001e 00000000 00000000 00002004 2**0
CONTENTS, READONLY
4 .ARM.attributes 0000002c 00000000 00000000 00002022 2**0
CONTENTS, READONLY
And the map file like this: ( again, pay attention to starting address column )
NOTE: We decided to remove the unnecessary information to make things more easy to explain
Memory Configuration
Name Origin Length Attributes
*default* 0x0000000000000000 0xffffffffffffffff
Linker script and memory map
LOAD main.o
code 0x0000000000001000 0xc
main.o(.text)
.text 0x0000000000001000 0xc main.o
0x0000000000001000 main
:
:
dato 0x0000000000002000 0x4
main.o(.data)
.data 0x0000000000002000 0x4 main.o
0x0000000000002000 var2
vars 0x0000000000003000 0x4
main.o(.bss)
.bss 0x0000000000003000 0x4 main.o
0x0000000000003000 var1
OUTPUT(main.elf elf32-littlearm)
LOAD linker stubs
:
:
This is actually the way we can place the compiled code in the microcontroller memory space, just think about it, you can take the variables and group them into one section and placed right where the RAM memory start, well it is not so straight forward but you are basically getting the idea.