GNU Linker, can you NOT Initialize my Variable?

my students sometimes are afraid to ask questions, although I urge them ask any question. In my opinion there are no ‘dumb’ questions: only questioning things let us think and learn new things. I see that many readers of this blog are *not* afraid to comment or ask questions. The WordPress statistics shows 5’687 questions/comments for this blog (thank you all!), and the spam filter protected me from 202,341 items (ok, these *are* dumb) :-).

The ‘question of the week’ comes from Andy. That question caused me some serious head scratching, but the same time I have learned something important and useful for my next project: how to tell the ARM GNU linker *not* to initialize variables?

GNU ARM Embedded Linker Options

GNU ARM Embedded Linker Options

To Initialize or Not, That’s the Question: bss and data

I recommend to read my post about “text, data and bss: Code and Data Size Explained“. With that article I explain that

uint32_t globVariable;

will add 4 bytes to the bss section (uninitialized data).

And if I have initialized data like

uint32_t globInitializedVar = 0x12345678;

then this will add 4 bytes to the data section (initialized data), plus there will be 4 bytes allocated in FLASH for the initialization data (0x12345678).

My current appliation for a Freescale K20 gives me with printsize:

   text       data        bss        dec        hex    filename
  21004        288       3584      24876       612c    FRDM-K20_CDC.elf

So my FLASH size is 21004+288 bytes, and my RAM needs are 280+3584 bytes. So far, so good, and makes sense.

Not Initialized Data with Section __attribute__

The interesting thing is when I add this to my application (and of course reference/use it):

static unsigned char myBuffer[4096] __attribute__((section (".m_data_20000000")));

I’m using here a section attribute to allocate the variable in a named section, see “Defining Variables at Absolute Addresses with gcc“.

Then the sizes reported is:

   text       data        bss        dec        hex    filename
  21016       4384       3584      28984       7138    FRDM-K20_CDC.elf

Wow! The data size increased by 4K! Remember: that 4 KByte gets added to the FLASH for the initialization data. I would have expected that it would end up in the bss area, but it does not. So even as I have *not* initialized that variable, the linker creates a 4 KByte array of zeros in FLASH to initialize it :-(. Really surprising, unexpected and bad as it increases the FLASH footprint for no reason.

I was hoping that maybe ‘printsize’ is just wrong, but when I checked the two S19 files produced, there is indeed FLASH memory allocated for this 😦 :

4 KByte of Zeros for initialization data

4 KByte of Zeros for initialization data

Section Attribute and Data Initialization

So I started to dig more into how the __attribute__ section topic. And after googling and searching, I have found this (

” You may only use the `section’ attribute with a fully initialized global definition because of the way linkers work. The linker requires each object be defined once, with the exception that uninitialized variables tentatively go in the `common’ (or `bss’) section and can be multiply “defined”. You can force a variable to be initialized with the `-fno-common’ flag or the `nocommon’ attribute.”

So this means that if I use the __attribute__ with section, the variable is treated like an initialized variable.

Not initializing data by linker: NOLOAD

The solution to this is to use (NOLOAD) in the linker script (

secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
  { contents } >region :phdr =fill

NOLOAD is described as

The `(NOLOAD)’ directive will mark a section to not be loaded at run time. The linker will process the section normally, but will mark it so that a program loader will not load it into memory. For example, in the script sample below, the ROM section is addressed at memory location `0′ and does not need to be loaded when the program is run. The contents of the ROM section will appear in the linker output file as usual.

  ROM  0  (NOLOAD)  : { ... }

That’s the solution!

💡 Microchip does a nice job: Their compiler implements the ‘noload’ attribute at compile time with __attribute__((section (“SectionName”), noload)), but not GNU ARM 😦

So I add (NOLOAD) to my linker file section:

NOLOAD in Linker File

NOLOAD in Linker File

And with this, I get what I expect: the variable is added to bss and not to data:

   text       data        bss        dec        hex    filename
  21016        288       7680      28984       7138    FRDM-K20_CDC.elf


(NOLOAD for Battery Buffered RAM

The (NOLOAD) is needed as well if I have other kind of RAM I need to prevent from beeing initialized, such as battery buffered RAM. In such a case, I can have a linker file like this template:

   NormalRAM: ORIGIN = ....., LENGTH = ....
   BattRAM  : ORIGIN = ....., LENGTH = ....

  ... other sections [.text, .data. ,bss, ....] here

   .uninit (NOLOAD) : { ...... } >BattRAM


Variables using the a named section attribute are treated by the linker like initialized varibles which consume FLASH memory for the initialization data. To prevent the linker from initializing the data, the (NOLOAD) attribute has to be used in the linker file (see (NOLOAD) is needed as well for special memory areas like battery buffered RAM not to be erased.

Happy Not-Initializing 🙂


15 thoughts on “GNU Linker, can you NOT Initialize my Variable?

  1. Erich,

    Thank you for this article. I had just spent the final half of last Friday afternoon trying to find the answer for exactly this issue for some fixed RAM areas in my current project.

    The gcc linker documentation has the required information but lacks a lot of cross references needed to find it quickly.


    Liked by 1 person

  2. Hi, thank you for digging into this. I just tried it and got *slightly* unexpected result: I get reported 0 for “data” (arm-none-eabi-size –totals), despite that there are initialized globals. The shared thing between “data” (where .data section and all initialized data contents (sub-sections?) ) reside and my custom section for non-initialized variables is same MEMORY (region) name.
    It seems that despite this report, the things are OK (arm-none-eabi-nm gives me):
    1ffffa10 D __data_end__
    1ffff8c0 D __data_start__
    where these are address names put before & after .data section & exported by my linker script. Probably my linker is not OK, because my .bin became 512M


  3. Thank you! Again (at least the third solution to problems during my early development with this new Kinetis processor and KDS, found on mcuoneclipse).


  4. the linker files supplied with KSDK2.1 had no “noinit” section defined , I discovered on sourceforge there is a linker file example for KLx that has a .noinit section using (NOLOAD)

    .noinit (NOLOAD):
    . = ALIGN(4);
    _noinit = .;

    *(.noinit .noinit.*)

    . = ALIGN(4) ;
    _end_noinit = .;
    } > RAM

    /* Mandatory to be word aligned, _sbrk assumes this */
    PROVIDE ( end = _end_noinit ); /* was _ebss */
    PROVIDE ( _end = _end_noinit );
    PROVIDE ( __end = _end_noinit );
    PROVIDE ( __end__ = _end_noinit );

    I replaced “RAM” with “m_data” and then added the section attribute as per this example :
    unsigned char rebootflag __attribute__ ((section (“.noinit”))); // seems to work


  5. Thank you so much for this, you saved my day.

    I’m developing a project on STM32F4 development board with 8 megs of external SDRAM.
    Using VS2015 + VisualGDB + GNU c compiler. Since I added SDRAM support to my project, linking became 30x times slower (from 1sec to approx 30 sec). It’s just such a waste of time when you debug a lot!
    Being baffled for several days, I finally realized that it’s probably about initializing the arrays located in SDRAM section.
    Finally, addind (NOLOAD) to lds file did the job, linking is rocket-fast again.

    .esdram (NOLOAD) :
    } > ESDRAM

    Thank you so much!


  6. Pingback: Using Multiple Memory Regions with the FreeRTOS Heap | MCU on Eclipse

  7. Thanks – very useful information! I was able to successfully reduce the size of my elf and hex files using this approach. However when I use objcopy to create a binary file from the srec or elf build output, the binary file still apparently has the initialized m_data_20000000 section in it. I have several large static arrays that I manually place in the m_data_20000000 section using __attribute__((section (“.m_data_20000000”))). If I open the binary file I see a large section of 0’s. If I remove the arrays from my code the section of 0’s goes away.

    I’ve tried the ‘remove section’ option on objcopy, but it didn’t help. I’m confused as to how objcopy “recreates” this section of data given that it no longer appears in the srec/elf files that are being used to create the binary.

    Thanks for any insight you might be able to provide!


What do you think?

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s