Cortex M3 - Copying code from flash to ram without startup

Asked by fcayci on 2017-05-03

Hi all,

I am using STM32F107 chip (Cortex-M3) with arm-gcc-launchpad toolchain with mac. (arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors 6-2017-q1-update) 6.3.1 20170215 (release) [ARM/embedded-6-branch revision 245512])

The problem I see is that although I don't use any startup code and I don't have any function to copy the text section to ram, all the text section gets copied to the ram area. This becomes a problem when I use an initialized global array. The array gets copied to the end of the instructions in the ram area, however the processor thinks it starts from the ram base (which is what it should be - I think).

The question is, is there a way to tell the linker not to copy the text section to ram area? Also is this an expected behavior or is this some sort of bug?

Just to demonstrate here is what the flash are looks like with a simple C code that displays the array values from a port.

(gdb) x/64x 0x08000000
0x8000000 <vector_table>: 0x20008000 0x08000011 0x00000000 0x00000000
0x8000010 <main>: 0xb082b510 0xf04f4a13 0xf04f0300 0xe9c20400
0x8000020 <main+16>: 0x4a113400 0x699b4b10 0x0320f043 0x4b0f6193
0x8000030 <main+32>: 0x3222f04f 0x4b0d605a 0x3222f04f 0x2300601a
0x8000040 <main+48>: 0xe00b9301 0x490a4a09 0xf8519b01 0x60d33023
0x8000050 <main+64>: 0xf0004808 0x9b01f811 0x93013301 0x2b4f9b01
0x8000060 <main+80>: 0xe7ecd9f0 0x20000140 0x40021000 0x40011400
0x8000070 <main+96>: 0x20000000 0x00030d40 0x9001b082 0xe0029b01
0x8000080 <delay+8>: 0x3b019b01 0x9b019301 0xd1f92b00 0xb002bf00
0x8000090 <delay+24>: 0x00004770 0x00000001 0x00000002 0x00000003
0x80000a0: 0x00000004 0x00000005 0x00000006 0x00000007
0x80000b0: 0x00000008 0x00000009 0x0000000a 0x0000000b
0x80000c0: 0x0000000c 0x0000000d 0x0000000e 0x0000000f
0x80000d0: 0x00000010 0x00000011 0x00000012 0x00000013
0x80000e0: 0x00000014 0x00000015 0x00000016 0x00000017
0x80000f0: 0x00000018 0x00000019 0x0000001a 0x0000001b

This looks fine and the last part is the global initialized array. However if I look at the ram section I see:

(gdb) x/64x 0x20000000
0x20000000 <uint_array_init_glb>: 0x441c4c08 0x61232301 0x3b02f830 0x3b02f821
0x20000010 <uint_array_init_glb+16>: 0xf01368e3 0xd0fb0f01 0x0f14f013 0x3a01d101
0x20000020 <uint_array_init_glb+32>: 0xbe00d1f0 0x40022000 0x20008000 0x08000011
0x20000030 <uint_array_init_glb+48>: 0x00000000 0x00000000 0xb082b510 0xf04f4a13
0x20000040 <uint_array_init_glb+64>: 0xf04f0300 0xe9c20400 0x4a113400 0x699b4b10
0x20000050 <uint_array_init_glb+80>: 0x0320f043 0x4b0f6193 0x3222f04f 0x4b0d605a
0x20000060 <uint_array_init_glb+96>: 0x3222f04f 0x2300601a 0xe00b9301 0x490a4a09
0x20000070 <uint_array_init_glb+112>: 0xf8519b01 0x60d33023 0xf0004808 0x9b01f811
0x20000080 <uint_array_init_glb+128>: 0x93013301 0x2b4f9b01 0xe7ecd9f0 0x20000140
0x20000090 <uint_array_init_glb+144>: 0x40021000 0x40011400 0x20000000 0x00030d40
0x200000a0 <uint_array_init_glb+160>: 0x9001b082 0xe0029b01 0x3b019b01 0x9b019301
0x200000b0 <uint_array_init_glb+176>: 0xd1f92b00 0xb002bf00 0x00004770 0x00000001
0x200000c0 <uint_array_init_glb+192>: 0x00000002 0x00000003 0x00000004 0x00000005
0x200000d0 <uint_array_init_glb+208>: 0x00000006 0x00000007 0x00000008 0x00000009
0x200000e0 <uint_array_init_glb+224>: 0x0000000a 0x0000000b 0x0000000c 0x0000000d
0x200000f0 <uint_array_init_glb+240>: 0x0000000e 0x0000000f 0x00000010 0x00000011

There are around 40 bytes of data that I don't know what it is, then the vector table starts with 0x20008000 value and then I see the instructions. My array comes afterwards, but if you look at the labels, it thinks the array starts from 0x20000000 where it actually starts at roughly 0x200000bc area. (When executing the code, the first index of the array points to the ram location 0x20000000, thus the instructions get shown on the LEDs )

I have uploaded the files here:


Question information

English Edit question
GNU ARM Embedded Toolchain Edit question
No assignee Edit question
Solved by:
Last query:
Last reply:


Can you try using "AT>" rather than "AT >" in your linker script?

Best regards,

fcayci (fcayci) said : #2


I have changed it, and it looks the same.

I have also tried the other variation :

 _sidata = .;

 .data : AT ( _sidata )
  _sdata = .;
  . = ALIGN(4);
  _edata = .;
 } >ram

Which again didn't change the outcome.

   text data bss dec hex filename
    148 320 8 476 1dc array.elf

arm-none-eabi-nm -na array.elf
00000000 a array.c
00000008 A __bss_size
00000094 A __text_size
00000140 A __data_size
08000000 t .text
08000000 T vector_table
08000010 T main
08000078 T delay
08000094 T _etext
08000094 T _sidata
20000000 d .data
20000000 D _sdata
20000000 D uint_array_init_glb
20000140 b .bss
20000140 D _edata
20000140 B _sbss
20000140 B alpha
20000148 B _ebss


Tejas Belagod (belagod-tejas) said : #3

>The problem I see is that although I don't use any startup code and I don't have any function to copy the text section to ram, all the text section gets copied to the ram area.

I'd really like to know how this happens but for now, lets assume some embedded boot sequence is doing this. In this case, the LMA is the flash address map and the VMA becomes the RAM address map. You could place the data section such that the location counter points to the end of text for the data.

The AT (addr) that you have specified above is the VMA (RAM)

_sidata = .;

 .data : AT ( _sidata )

It should be the LMA which the Flash/ROM.

See the following links for more explanation:


fcayci (fcayci) said : #4


Thanks for the explanation. I could fix the problem by adding a function to copy the data section from rom to ram in the beginning of the code, but without that couldn't get it working properly.

> The AT (addr) that you have specified above is the VMA (RAM)
> _sidata = .;
> .data : AT ( _sidata )
> It should be the LMA which the Flash/ROM.

The _sidata symbol is at the Flash/ROM area as can be seen from the nm output above (08000094 T _sidata)

> You could place the data section such that the location counter points to the end of text for the data.

And from the above example this is exactly what I think I am doing with the above linker.

Here is the objdump output. The data LMA is at 08000094 and VMA is at 20000000 which looks correct to me.

 arm-none-eabi-objdump -h array.elf

array.elf: file format elf32-littlearm

Idx Name Size VMA LMA File off Algn
  0 .text 00000094 08000000 08000000 00010000 2**2
  1 .data 00000140 20000000 08000094 00020000 2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss 00000008 20000140 080001d8 00020140 2**3

arm-none-eabi-objdump -t array.elf

array.elf: file format elf32-littlearm

08000000 l d .text 00000000 .text
20000000 l d .data 00000000 .data
20000140 l d .bss 00000000 .bss
00000000 l d .comment 00000000 .comment
00000000 l d .ARM.attributes 00000000 .ARM.attributes
00000000 l df *ABS* 00000000 array.c
00000140 g *ABS* 00000000 __data_size
08000094 g .text 00000000 _sidata
08000094 g .text 00000000 _etext
20000140 g .bss 00000000 _sbss
20000000 g O .data 00000140 uint_array_init_glb
00000008 g *ABS* 00000000 __bss_size
20000000 g .data 00000000 _sdata
20000148 g .bss 00000000 _ebss
08000000 g O .text 00000010 vector_table
08000010 g F .text 00000068 main
08000078 g F .text 0000001a delay
00000094 g *ABS* 00000000 __text_size
20000140 g .data 00000000 _edata
20000140 g O .bss 00000008 alpha


First of all, how do you expect it to work if you *don't* initialize the .data section from flash? If you don't, the section (and thus your uint_array_init_glb[] will have undefined contents.

The fact that the undefined content looks a bit like the .text section shouldn't bother you at all. How do you know that it isn't remains from a previous run? If you load through GDB, it could potentially initialize RAM sections for you (I know it sets the $pc to your entry point). If you use a gdbserver or flashing tool like for example OpenOCD, it will use the RAM as a working buffer for flashing algorithm and data (this looks very much to be the case here).

In short; just ignore the content of your RAM and write a proper copy routine to initialize it from flash and zero .bss, otherwise it will surely not work.

fcayci (fcayci) said : #6

As I mentioned in my earlier comment, adding a proper copy routine works without any problems, and I was trying to figure out why the data gets copied to RAM area without any initialization code. However you have a good point about st-flash tool using RAM area as a temporary buffer. This might very well be the case. (It doesn't depend on GDB as it happens when I run it as well.)