Calling C++ Constructors From C++: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
m (Loop through destructors in opposite order of the constructors)
m (Slightly improve this miserable article.)
Line 32: Line 32:
Actually porting the above code to C++ is rather simple, we'll start off by
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.
defining a function pointer typedef, and the declare some externs to the linker symbols.
<source lang="c">
<source lang="cpp">
/** A typedef for the default constructor function pointer type */
/** A typedef for the default constructor function pointer type */
typedef void (*function_pointer) (void);
typedef void (*function_pointer) (void);
Line 46: Line 46:
The only thing left to do, is simply to load and execute all the function pointers
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;
within the range, below is a snippet that does exactly this;
<source lang="c">
<source lang="cpp">
/** Executes all the constructors found in the ctor section */
/** Executes all the constructors found in the ctor section */
void executeConstructors()
void executeConstructors()
Line 52: Line 52:
// We don't need to divide by 4 (bytes per function pointer), as the
// We don't need to divide by 4 (bytes per function pointer), as the
// compiler figures this from the typedef.
// compiler figures this from the typedef.
u32int numberOfConstructors = (end_ctors - start_ctors);
uint32_t numberOfConstructors = (end_ctors - start_ctors);


// Loop though all the constructors,
// Loop though all the constructors,
// loading and executing them one at a time.
// loading and executing them one at a time.
for(u32int x = 0; x < numberOfConstructors; x++)
for(uint32_t x = 0; x < numberOfConstructors; x++)
{
{
function_pointer constructor = start_ctors[x];
function_pointer constructor = start_ctors[x];
Line 64: Line 64:
</source>
</source>
Similar code can be produced for the destructors;
Similar code can be produced for the destructors;
<source lang="c">
<source lang="cpp">
/** Executes all the destructors found in the dtor section */
/** Executes all the destructors found in the dtor section */
void executeDestructors()
void executeDestructors()
Line 70: Line 70:
// We don't need to divide by 4 (bytes per function pointer), as the
// We don't need to divide by 4 (bytes per function pointer), as the
// compiler figures this from the typedef.
// compiler figures this from the typedef.
u32int numberOfDestructors = (end_dtors - start_dtors);
uint32_t numberOfDestructors = (end_dtors - start_dtors);


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


* 1. Example function;
* 1. Example function;
<source lang="c">
<source lang="cpp">
extern "C" void func(void)
extern "C" void func(void)
{
{
Line 101: Line 101:
</source>
</source>
* 2. Finding the address
* 2. Finding the address
<source lang="c">
<source lang="cpp">
print_hex(&func);
print_hex(&func);
</source>
</source>

Revision as of 12:30, 4 January 2013

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.
    uint32_t numberOfConstructors = (end_ctors - start_ctors);

    // Loop though all the constructors,
    // loading and executing them one at a time.
    for(uint32_t 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.
    uint32_t numberOfDestructors = (end_dtors - start_dtors);

    // Loop though all the destructors backwards,
    // loading and executing them one at a time.
    for(uint_t 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