You copied the Doc URL to your clipboard.

C++ initialization, construction and destruction

The C++ Standard places certain requirements on the construction and destruction of objects with static storage duration, and the ARM C++ compiler uses the .init_array area to achieve this.

The .init_array area is a const data array of self-relative pointers to functions. For example, you might have the following C++ translation unit, contained in the file test.cpp:

struct T
} t;
int f()
    return 4;
int i = f();

This translates into the following pseudocode:

      AREA ||.text||, CODE, READONLY
 int f()
   return 4;
 static void __sti___8_test_cpp
   // construct 't' and register its destruction
   __aeabi_atexit(T::T(&t), &T::~T, &__dso_handle);
   i = f();
      AREA ||.init_array||, DATA, READONLY
      DCD __sti___8_test_cpp - {PC}
      AREA ||.data||, DATA
  t   % 4
  i   % 4

This pseudocode is for illustration only. To see the code that is generated, compile the C++ source code with armcc -c --cpp -S.

The linker collects each .init_array from the various translation units together. It is important that the .init_array is accumulated in the same order.

The library routine __cpp_initialize__aeabi_ is called from the C library startup code, __rt_lib_init, before main. __cpp_initialize__aeabi_ walks through the .init_array calling each function in turn. On exit, __rt_lib_shutdown calls __cxa_finalize.

Usually, there is at most one function for T::T(), mangled name _ZN1TC1Ev, one function for T::~T(), mangled name _ZN1TD1Ev, one __sti__ function, and four bytes of .init_array for each translation unit. The mangled name for the function f() is _Z1fv. There is no way to determine the initialization order between translation units.

Function-local static objects with destructors are also handled using __aeabi_atexit.

.init_array sections must be placed contiguously within the same region for their base and limit symbols to be accessible. If they are not, the linker generates an error.