C++: Difference between revisions

Jump to navigation Jump to search
216 bytes added ,  26 days ago
m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
(Removed old link)
m (Bot: Replace deprecated source tag with syntaxhighlight)
Line 21:
Below you will find an example of an implementation in respectively GCC.
 
<sourcesyntaxhighlight lang="cpp">
extern "C" void __cxa_pure_virtual()
{
// Do nothing or print an error message.
}
</syntaxhighlight>
</source>
 
Or, if you happen to use Visual Studio:
 
<sourcesyntaxhighlight lang="cpp">
int __cdecl _purecall()
{
// Do nothing or print an error message.
}
</syntaxhighlight>
</source>
 
If, during runtime, your kernel detects that a call to a pure virtual function couldn't be made, it calls the above functions. These functions should actually never be called, because without hacks, or through undefined behaviour of your kernel, it is not possible to instantiate a class that doesn't define all pure virtual functions.
Line 47:
Global objects must have their constructors called before they are used. Usually, they are called by the start-up code (which you just disabled). So, in order to be able to use them, you have to write your own start-up code for them. All objects have a constructor and a destructor. When an executable is loaded into memory and the program jumps straight to the entry point, the constructors of global objects will not have been called. One solution is to do this manually. You could put this code first when your C++ entry point is called:
 
<sourcesyntaxhighlight lang="cpp">
object1.object1();
object2.object2();
object3.object3();
// ...
</syntaxhighlight>
</source>
 
Global or static objects have to be constructed by the environment before they are available to C++. Care should be taken if global/static objects need '''new''' and '''delete''' in their constructors. In this case it is best to construct these objects only after your kernel heap is ready for use (and you have access to dynamic memory allocation). Not doing so can cause an object to attempt to allocate memory via the non-working '''new''' operator. This also simplifies the storing of the destructor functions in '''__cxa_atexit''', because you don't have to use a static and fixed-size structure.
Line 64:
In the example implementation of '''__cxa_atexit''', the '''__atexit_funcs[ATEXIT_MAX_FUNCS]''' array acts as the table. This is why the '''__cxa_atexit''' function is defined as:
 
<sourcesyntaxhighlight lang="cpp">
int __cxa_atexit(void (*destructor) (void *), void *arg, void *__dso_handle);
</syntaxhighlight>
</source>
 
So that the '''destructor''' function pointer is the handle for a destructor function and '''arg''' is the single argument it may take. Finally, '''__dso_handle''' is a handle for the DSO (Dynamic Shared Object).
Line 72:
So summarized, you are required to define two functions and one symbol in order to use global objects in your C++ files:
 
<sourcesyntaxhighlight lang="cpp">
void *__dso_handle;
 
int __cxa_atexit(void (*destructor) (void *), void *arg, void *dso);
void __cxa_finalize(void *f);
</syntaxhighlight>
</source>
 
After you have called the objects constructor GCC automatically calls the function
 
<sourcesyntaxhighlight lang="cpp">
int __cxa_atexit(void (*destructor) (void *), void *arg, void *dso);
</syntaxhighlight>
</source>
 
This function should save all three parameters and if successful return zero, on failure non-zero. When your kernel exits you should call '''__cxa_finalize(0)'''. According to the ABI specification, calling this with 0 as the parameter instead of the address of a function (to be called and removed from the list) causes ''all'' destructors in the list to be called and removed from the list.
Line 89:
Since you will be calling this function from your Assembly source right after your kernel exits, you could use the following code:
 
<sourcesyntaxhighlight lang="asm">
; This is NASM source, mind you.
sub esp, 4
Line 97:
 
add esp, 4
</syntaxhighlight>
</source>
 
The following is tested, working, fully commented source that gives a more detailed explanation than the source previously found here. It also highlights what improvements can be implemented and where they can be inserted. To use it, just include '''icxxabi.h''' in any '''one''' file of your C++ kernel source (preferably the file where your kernel's main statements begin).
 
'''File: icxxabi.h'''
<sourcesyntaxhighlight lang="cpp">
#ifndef _ICXXABI_H
#define _ICXXABI_H
Line 133:
 
#endif
</syntaxhighlight>
</source>
 
 
'''File: icxxabi.cpp'''
<sourcesyntaxhighlight lang="cpp">
 
#include "./icxxabi.h"
Line 242:
};
#endif
</syntaxhighlight>
</source>
 
=== Visual C++ ===
Line 355:
''Note that these are only stubs to get the code compiled, and you should implement them yourself. Simply add a mutex-like guard with a test-and-set primitive.''
 
<sourcesyntaxhighlight lang="cpp">
namespace __cxxabiv1
{
Line 382:
}
}
</syntaxhighlight>
</source>
 
The actual code emitted by GCC to call a local static variable's constructor looks something like this:
Line 458:
In order to use placement new, you need special overloads of the new and delete operators defined in scope. Fortunately, the required definitions are simple and can be inlined in a header file (the C++ standard puts them in a header called '''new''').
 
<sourcesyntaxhighlight lang="cpp">
inline void *operator new(size_t, void *p) throw() { return p; }
inline void *operator new[](size_t, void *p) throw() { return p; }
inline void operator delete (void *, void *) throw() { };
inline void operator delete[](void *, void *) throw() { };
</syntaxhighlight>
</source>
 
The above implementation can potentially be unsafe for allocating memory since your kernel does not mark the memory that was allocated as being used. Placement new is hardly ever used, and if you wish to read an object from a specified address in memory, it is usually easier to create a pointer to that address.
Line 469:
You never call placement delete explicitly (it's only required for certain implementation detail reasons). Instead, you simply invoke your object's destructor explicitly.
 
<sourcesyntaxhighlight lang="cpp">
apic->~APIC();
</syntaxhighlight>
</source>
 
== RTTI (Run-Time Type Information) ==
Cookies help us deliver our services. By using our services, you agree to our use of cookies.

Navigation menu