3

I would like to allocate numerous class in static memory and NOT use the heap for an embedded project using C++. Currently when we do the following to a class in a separate CPP file devoted to statically allocated classes:

Config cfg(spi);

This gets allocated on the heap from what I can tell. Stepping through the assembly code, I see malloc is eventually called. The stack trace looks like the following:

malloc()
__register_exitproc()
__static_initialization_and_destruction_0()
_GLOBAL__sub_I_periodic()
__libc_init_array
<reset vector>

The Config class looks like this:

class Config
{
   public:
      Config(SPIDriver &spi);
      virtual ~Config();
   private:
      SPIDriver *_spi;
}

The implementation then looks like this:

Config::Config(SPIDriver &spi)
: _spi(&spi) {}
Config::~Config() {_spi = NULL;}

Is there any way to force GCC to place this in static memory and NOT on the heap? Thanks in advance!

trincot
  • 317,000
  • 35
  • 244
  • 286
Nick
  • 258
  • 4
  • 14
  • 1) One does not allocate *classes*, one allocates *objects*. 2) In this statement: `Config cfg(spi);`, the object might be statically or dynamically allocated according to context. Is that line a file scope? Or in a function? – Robᵩ Jan 29 '13 at 17:00
  • If the stack trace is to be believed, the `malloc` call is for internal runtime state to arrange for the object's destructor to be called at the right time, not for the `Config` object itself. You may find http://sourceware.org/ml/newlib/2008/msg00560.html instructive. – zwol Jan 29 '13 at 17:07
  • Sorry, I was lazy. All of the objects are instantiated at file scope in a single file. From previous embedded C++ projects, these get allocated in static memory and not on the heap. – Nick Jan 29 '13 at 18:40
  • I am using: gcc version 4.6.3 (Sourcery CodeBench Lite 2012.03-56) with newlib. I'm not sure what version of newlib it is. – Nick Jan 29 '13 at 18:41
  • Tangentially related: you might want to look at C++11 `constexpr`. That will allow the static objects to constructed at compile time, when used right. – hyde Jan 30 '13 at 07:12
  • You have a member `SPIDriver *_spi`, I don't see how it's memory is allocated. – vonbrand Jan 30 '13 at 12:29
  • @Robᵩ, the compiler will allocate space for static or file scope objects in the data section (what OP is presumably calling "static memory", and the objects local to a function on the stack. Unless created by `new` or its ilk it should not be in the heap. – vonbrand Jan 30 '13 at 12:32

3 Answers3

4

I finally tracked down my problem. Zack is right, __register_exitproc is being called and that is registering the destructors into a linked list. That is why malloc is called, so allocate space for the linked list. Our program is runs on bare metal ARM (Cortex M4). So the classes will never be destroyed because power will be lost when we power down. Luckily __register_exitproc is a weak function, so I created another version of this function to do nothing. There wasn't any problems with dynamic memory allocation, that was working, but for some reason in __register_exitproc, it was trying to load a 32-bit value into the ARM GPR from a memory location with an odd (literally) address. This causes an Hard Fault exception to be thrown. By overriding __register_exirproc we prevent this exception from occuring. Plus we get rid of the dynamic memory allocation on system startup.

Hope this helps someone else. As always, thanks or the help!!!

Nick
  • 258
  • 4
  • 14
3

All your mess is really goes from fact, that you don't know if cfg was allocated on heap or wasn't. Just calling malloc somewhere on startup means nothing -- libc calls malloc several times even for void program.

Let me explain you how to determine where your data is.

Lets start with your code, some modified:

#include "stdlib.h"

class Config
{
   public:
     Config(int spi) {_spi = &spi;}
     virtual ~Config() {;}
   private:
     int *_spi;
};

Config cfg(2);

int
main(void)
{
  int *x = new int(2);
  return 0;
}

Now lets compile it with g++ -g -O0 statstorage.cpp -static

You got a.out file. Now we must look where your data are actually are. First call gdb with command line like this:

echo -e "start\nstep\np/x &cfg\np/x x\nquit" > .gdbinit && gdb a.out

You will got output like this:

$1 = 0x6add50
$2 = 0x6c5670

Where 0x6add50 is address of your cfg, and 0x6c5670 is address of dynamically allocated x pointer.

Now lets call readelf, like readelf -S a.out

And look where sections are. Output is (for my pc) 38 sections. All we need is Address field and Size field. I have:

                                Address               Size
[23] .bss              ...  00000000006add00 ... 0000000000014d38
[24] __libc_freeres_pt ...  00000000006c2a38 ... 0000000000000030

And __libc_freeres_pt is the last section with the biggest non-null address.

We can see, that 0x6c5670 is outside loaded binary (in heap itself), and cfg is inside bss section.

This yields, taht cfg was allocated statically.

Konstantin Vladimirov
  • 6,791
  • 1
  • 27
  • 36
0

If you haven't "got" malloc [and related gubbins], then you will need to implement your own global new function, and use, for example a large array of char as your "heap".

here is a previous question that explains how you write your own new/delete functions. You should be able to also write a malloc and free function to achieve the same thing for C style allocations.

The alternative is of course to rewrite how the glibc or whatever library you are using works. May also be an option if you are only doing really simple things. But my guess is that sooner or later you WILL need dynamic allocation, so I think writing your own [or adapting existing ones] is your best choice.

Community
  • 1
  • 1
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227