Using embOS API in C++ constructor of global objects

From SEGGER Wiki
Jump to: navigation, search

Introduction

Global objects of C++ classes need to be constructed by the runtime initialization. The runtime initialization code calls the C++ constructors before main().

#include "RTOS.h"
#include "stdlib.h"

class foo {
public:
  foo(void) {
    OS_MUTEX_Create(&m);  // Called before OS_Init()
  }
private:
  OS_MUTEX m;
};

static foo MyFoo;

int main(void) {
  OS_Init();   // Initialize embOS
  OS_Start();  // Start embOS
  return 0;
}

embOS API like OS_MUTEX_Create() must be called after OS_Init() only but in the above example it will actually be called before main() and with it before OS_Init(). This example will result in OS_Error() with the error number OS_ERR_INIT_NOT_CALLED (165).

Toolchains provide different solutions for this problem:

IAR EWARM

The IAR linker option --manual_dynamic_initialization can be used to skip the C++ dynamic initialization phase in the startup code. The application must then call __iar_dynamic_initialization() (declared in iar_dynamic_init.h) after OS_Init().

#include "RTOS.h"
#include "stdlib.h"
#include "iar_dynamic_init.h"

class foo {
public:
  foo(void) {
    OS_MUTEX_Create(&m); // Called after OS_Init()
  }
private:
  OS_MUTEX m;
};

static foo MyFoo;

int main(void) {
  OS_Init();                        // Initialize embOS
  __iar_dynamic_initialization();   // Late initialization of constructors
  OS_Start();                       // Start embOS
  return 0;
}

Embedded Studio

Automatic Initialization

The SEGGER Linker automatically calls the constructor of global objects of C++ classes. With a global class object, such as the following, the constructor for the instance is linked into the section .ctors or into init_array.

class foo {
public:
  foo(void) {
  }
};

static foo MyFoo;

The linker script defines a block for all constructors and automatically does the initialization when there is at least one constructor to call.

  define block ctors                          { section .ctors,     section .ctors.*, block with         alphabetical order { init_array } };
  define block dtors                          { section .dtors,     section .dtors.*, block with reverse alphabetical order { fini_array } };

  initialize by calling __SEGGER_init_ctors   { block ctors }; // Call constructors for global objects which need to be constructed before reaching main (if any). Make sure this is done after setting up heap.

Manual Initialization

With Embedded Studio the solution is to remove initialize by calling __SEGGER_init_ctors from the linker script and add the following function:

extern void (*__ctors_start__[])(void);
extern void (*__ctors_end__[])(void);

void init_ctors(void);
void init_ctors(void) {
  void (**f)(void);
 
  for (f = __ctors_start__; f < __ctors_end__; f++) {
    (*f)(); 
  }
}

init_ctors() must then be called in the application after OS_Init():

#include "RTOS.h"
#include "stdlib.h"

class foo {
public:
  foo(void) {
    OS_MUTEX_Create(&m); // Called after OS_Init()
  }
private:
  OS_MUTEX m;
};

static foo MyFoo;

int main(void) {
  OS_Init();    // Initialize embOS
  init_ctors(); // Late initialization of constructors
  OS_Start();   // Start embOS
  return 0;
}