Calling C++ Constructors From C++

From OSDev.wiki
Revision as of 17:53, 4 July 2012 by osdev>Virtlink (Loop through destructors in opposite order of the constructors)
Jump to navigation Jump to search
Difficulty level

Medium
Kernel Designs
Models
Other Concepts
In this tutorial we will learn, how to move the code for calling static object con-/de-structors from assembly into C++. This tutorial is a followup on C++ Bare Bones

Preface

Currently we have some assembly code, that takes care of calling the static objects constructors and destructors namely;

    mov  ebx, start_ctors               ; call the constructors
    jmp  .ctors_until_end
.call_constructor:
    call [ebx]
    add  ebx,4
.ctors_until_end:
    cmp  ebx, end_ctors
    jb   .call_constructor

for constructors, and;

    mov  ebx, end_dtors                 ; call the destructors
    jmp  .dtors_until_end
.call_destructor:
    sub  ebx, 4
    call [ebx]
.dtors_until_end:
    cmp  ebx, start_dtors
    ja   .call_destructor

for destructors.

Porting to C++

Actually porting the above code to C++ is rather simple, we'll start off by defining a function pointer typedef, and the declare some externs to the linker symbols.

/** A typedef for the default constructor function pointer type */
typedef void (*function_pointer) (void);
/** Externs to the linker symbols */
/** Constructors */
extern function_pointer start_ctors[];
extern function_pointer end_ctors[];
/** Destructors */
extern function_pointer start_dtors[];
extern function_pointer end_dtors[];

The only thing left to do, is simply to load and execute all the function pointers within the range, below is a snippet that does exactly this;

/** Executes all the constructors found in the ctor section */
void executeConstructors()
{
    // We don't need to divide by 4 (bytes per function pointer), as the
    // compiler figures this from the typedef.
    u32int numberOfConstructors = (end_ctors - start_ctors);

    // Loop though all the constructors,
    // loading and executing them one at a time.
    for(u32int x = 0; x < numberOfConstructors; x++)
    {
        function_pointer constructor = start_ctors[x];
        constructor();
    }
}

Similar code can be produced for the destructors;

/** Executes all the destructors found in the dtor section */
void executeDestructors()
{
    // We don't need to divide by 4 (bytes per function pointer), as the
    // compiler figures this from the typedef.
    u32int numberOfDestructors = (end_dtors - start_dtors);

    // Loop though all the destructors backwards,
    // loading and executing them one at a time.
    for(u32int x = numberOfDestructors - 1; x >= 0; x--)
    {
        function_pointer destructor = start_dtors[x];
        destructor();
    }
}

This will effectively execute the constructors and destructors.

Testing

A crude hack, which can be used to check whether the above code is actually working can be produced by the following method;

  • 1. Create a function with the correct prototype ('void func(void)')
  • 2. Find the address of this function ('print_hex(&func);')
  • 3. Hardcode the found function address into the ctor list in the linker script.

Below is a concrete example of this;

  • 1. Example function;
extern "C" void func(void)
{
    print("extern "C" void func(void) CALLED");
}
  • 2. Finding the address
print_hex(&func);

Say this returns '0x00100990'

  • 3. Hardcoding into the linker script;
ENTRY (loader)

SECTIONS
{
    ...
    .rodata ALIGN (0x1000) :
    {
        start_ctors = .;
        *(SORT(.ctors.*))  /* Note the "SORT" */
        LONG(0x00100990)   /* The hardcoded function address */
        end_ctors = .;

        ...
    }
    ...
}

When the static object constructor calling code is run, it will then call the 'func()' function.


See Also

Articles