16

I'm writing an embedded application and almost all of my RAM is used by global byte-arrays. When my firmware boots it starts by overwriting the whole BSS section in RAM with zeroes, which is completely unnecessary in my case.

Is there some way I can instruct the compiler that it doesn't need to zero-initialize certain arrays? I know this can also be solved by declaring them as pointers, and using malloc(), but there are several reasons I want to avoid that.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Maestro
  • 9,046
  • 15
  • 83
  • 116
  • 1
    Actually it shouldn't. Usually the linker only saves the size of the bss section and not the (empty) content of it. The firmware just clears the required memory. How do you plan to use the arrays if you don't reserve RAM for them? – fuz Feb 04 '13 at 11:46
  • @FUZxxl You are right, I thought it was copying zeroes from FLASH to RAM, but I see now that the bootloader just writes zeroes equal to the size of the BSS section to RAM. This seems still unnecessary, because I dont depend on most arrays to be zero-initialized, but it will not affect performance as much as I thought. I edited my question to reflect your info. – Maestro Feb 04 '13 at 11:51
  • 1
    I think this is toolchain dependant. In the toolchain I use I can instruct the linker to not initialize or load a section. This is actually mandatory with some sections (with data shared across multiple cores). – Makis Feb 04 '13 at 12:11
  • http://github.com/dwelch67 this is my normal operation (to not zero bss, not copy over .data) find and change your boot code (often named crt0.S in the gnu toolchain world) or just make your own. – old_timer Feb 04 '13 at 14:20

7 Answers7

11

The problem is that standard C enforces zero initialization of static objects. If the compiler skips it, it wouldn't conform to the C standard.

On embedded systems compilers there is usually a non-standard option "compact startup" or similar. When enabled, no initialization of static/global objects will occur at all, anywhere in the program. How to do this depends on your compiler, or in this case, on your gcc port.

If you mention which system you are using, someone might be able to provide a solution for that particular compiler port.

This means that any static/global (static storage duration) variable that you initialize explicitly will no longer be initialized. You will have to initialize it in runtime, that is, instead of static int x=1; you will have to write static int x; x=1;. It is rather common to write embedded C programs in this manner, to make them compatible with compilers where the static initialization is disabled.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • The zero-initialization bit makes sense to me. It's simple to wrap the boundary of `bss` in the linker script with symbols (like `__bss_start`, `__bss_end`, etc) that allow a simple loop to walk the range and write zeros. However, how do static/globals get initialized with their compile-time constant values? In the userspace case it makes sense that it _just works_ so long as the `.data` segment is copied into memory at the appropriate address, but how does this work on embedded platforms? Specifically causes where one has used `objcopy` to create a bin file (e.g. `objcopy -Obinary a.o a.bin`) – sherrellbc May 24 '18 at 19:29
  • @sherrellbc On embedded platforms there's code (the "CRT") running before main() is called, which clears the `.bss` and copy-down values from flash to `.data`. Roughly like in [this example](https://stackoverflow.com/questions/9535250/why-is-the-bss-segment-required/9535579#9535579). – Lundin May 25 '18 at 07:01
10

It turned out that the linker-script included in my toolchain has a special "noinit" section.

__attribute__ ((section (".noinit")))

/** Forces the compiler to not automatically zero the given global variable on startup, so that the current RAM contents is retained. Under most conditions this value will be random due to the behaviour of volatile memory once power is removed, but may be used in some specific circumstances, like the passing of values back after a system watchdog reset.

So all global variabeles marked with that attribute will not be zero-initialised during boot.

Gyom
  • 3,773
  • 5
  • 29
  • 38
Maestro
  • 9,046
  • 15
  • 83
  • 116
  • Careful though, you need to add this attribute to all "globals" but also all local static variables, and possibly also to plain local arrays/structs/unions that have incomplete/partial initialization lists (such as `int arr[10] = {0}`). The safest way is to ensure that you never initialize a variable anywhere, but always set them in runtime, as shown in my answer. – Lundin Feb 04 '13 at 15:41
  • " like the passing of values back after a system watchdog reset."- The safest way to do this would be to use an external flash memory that writes a particular value if the value at that address is at default(0xFF for most flash memories) and is compared to identify if a system rest has occurred or not. – AlphaGoku May 06 '16 at 07:07
2

The C standard REQUIRES global data to be initialized to zero.

It is possible that SOME embedded system manufacturers provide a way to bypass this option, but there are certainly many typical applications that would simply fail if the "initialize to zero" wasn't done.

Some compilers also allow you to have further sections, which may have other characteristics than the 'bss' section.

The other alternative is of course to "make your own allocation". Since it's an embedded system, I suppose you have control over how the application and data is loaded into RAM, in particular, what addresses are used for that.

So, you could use a pointer, and simply use your own mechanism for assigning the pointer to a memory region that is reserved for whatever you need large arrays for. This avoids the rather complex usage of malloc - and it gives you a more or less permanent address, so you don't have to worry about trying to find where your data is later on. This will of course have a small effect on performance, since it adds another level of indirection, but in most cases, that disappears as soon as the array is used as an argument to a function, as it decays to a pointer at that point anyways.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
2

There are a few workarounds like:

  • Deleting the BSS section from the binary or setting its size to 0 or 1. This will not work if the loader must explicitly allocate memory for all sections. This will work if the loader simply copies data to the RAM.
  • Declaring your arrays as extern in C code and defining the symbols (along with their addresses) either in assembly code in separate assembly files or in the linker script. Again, if memory must be explicitly allocated, this won't work.
  • Patching or removing the relevant BSS-zeroing code either in the loader or in the startup code that is executed in your program before main().
Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • To declare arrays inside some assembler goo seems like a very bad design idea to me. It would be far better and cleaner to do as your third option: to rewrite the initialization code which fills .bss and .data. I have done so a few times myself when the compiler had no support for it. But that assumes that you either have a bare bone CPU or an open-source RTOS. And if you don't, (lets say you have Embedded Linux or some such) then this low level real-time optimization is probably just pointless and dangerous. Though in my experience, most compilers have an option to remove the init code. – Lundin Feb 04 '13 at 15:38
  • @Lundin Either way, you must understand precisely what you're doing. – Alexey Frunze Feb 04 '13 at 15:59
2

All embedded compilers should allow a noinit segment. With the IAR AVR compiler the variables you don't want to be initialised are simply declared as follows:

__no_init uint16_t foo;

The most useful reason for this is to allow variables to maintain their values over a watchdog or brown-out reset, which of course doesn't happen in computer-based C programs, hence its omission from standard C.

Just search you compiler manual for "noinit" or something similar.

Paul Hills
  • 21
  • 1
1

Are you sure the binary format actually includes a BSS section in the binary? In the binary formats I've worked with BSS is simply a integer that tells the kernel/loader how much memory to allocate and zero out.

There definitely is no general way in C to get uninitialized global variables. This would be a function of your compiler/linker/runtime system and highly specific to that.

Art
  • 19,807
  • 1
  • 34
  • 60
  • You are right, I thought it was copying zeroes from FLASH to RAM, but I see now that the bootloader just writes zeroes equal to the size of the BSS section to RAM. This seems still unnecessary, because I dont depend on most arrays to be zero-initialized, but it will not affect performance as much as I thought. I edited my question to reflect your info. – Maestro Feb 04 '13 at 11:51
1

with gcc, -fno-zero-initialized-in-bss

asdff21
  • 19
  • 1
  • 2
    That's actually wrong. The `-fno-zero-initialized-in-bss` option means the compiler puts variables, that are initialized to 0, into the .data segment, not the .bss segment as it would normally do. That is an entirely different concern. – T-Bull Dec 02 '17 at 13:53