Calling C++ Constructors From C++
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.