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 (https://ohse.de/uwe/articles/gcc-attributes.html#var):

” 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 (http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_21.html):

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

NOLOAD is described as

(NOLOAD)
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.

SECTIONS {
  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:

MEMORY {
   NormalRAM: ORIGIN = ....., LENGTH = ....
   BattRAM  : ORIGIN = ....., LENGTH = ....
}

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

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

Summary

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 http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_node/ld_21.html). (NOLOAD) is needed as well for special memory areas like battery buffered RAM not to be erased.

Happy Not-Initializing 🙂

4 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.

    Peter

    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

    Like

  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).

    Like

What do you think?

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

WordPress.com Logo

You are commenting using your WordPress.com 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