C++: Difference between revisions

Jump to navigation Jump to search
4,235 bytes added ,  14 years ago
[unchecked revision][unchecked revision]
Line 79:
</source>
 
<tt>f</tt> is a function-pointer to the destructor, <tt>p</tt> is the parameter for the destructor and <tt>d</tt> is the "home DSO" (DSO = dynamic shared object). This function should save all three parameters and if successful return zero, on failure nonzero. When your kernel exits you should call <tt>__cxa_finalize(0)</tt>. According to the ABI specification, calling this with (int) 0 as the parameter instead of the address of a function to be called and removed from the list causes <em>all</em> destructors in the list to be called and removed from the list.
 
Since you will be calling this function from your assembly source, right after your kernel exits, you could use the following code to do so:
<source lang="cpp">
 
void __cxa_finalize(void *d);
<source lang="asm">
; This is NASM source, mind you.
sub esp, 4
mov [esp], dword 0x0
 
call __cxa_finalize
 
add esp, 4
</source>
 
The following is tested, working, fully commented source that gives a more detailed explanation than the source previously found here. It also hilights what improvements can be implemented on itself, where they can be inserted.
with d = 0 in order to destroy all with <tt>__cxa_atexit</tt> registered objects. Objects, which were registered first with <tt>__cxa_atexit</tt>, must be destroyed last by <tt>__cxa_finalize</tt>. You must provide the symbol <tt>__dso_handle</tt> in your executable. Only the address of this symbol is needed, because GCC calls <tt>__cxa_atexit</tt> with <tt>&__dso_handle</tt>.
 
'''File: icxxabi.h'''
<source lang="cpp">
#ifndef _ICXXABI_H
extern "C"
#define _ICXXABI_H
{
int __cxa_atexit(void (*f)(void *), void *p, void *d);
void __cxa_finalize(void *d);
};
 
#define ATEXIT_MAX_FUNCS 128
void *__dso_handle; /*only the address of this symbol is taken by gcc*/
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned uarch_t;
 
struct objectatexit_func_entry_t
{
/*
void (*f)(void*);
* Each member is at least 4 bytes large. Such that each entry is 12bytes.
void *p;
* 128 * 12 = 1.5KB exact.
void *d;
**/
} object[32] = {0};
void (*destructor_func)(void *);
unsigned int iObject = 0;
void *obj_ptr;
void *dso_handle;
};
 
int __cxa_atexit(void (*f)(void *), void *pobjptr, void *ddso);
void __cxa_finalize(void *f);
 
#ifdef __cplusplus
};
#endif
 
#endif
</source>
 
'''File: icxxabi.cpp'''
<source lang="cpp">
 
#include "./icxxabi.h"
 
atexit_func_entry_t __atexit_funcs[ATEXIT_MAX_FUNCS];
uarch_t __atexit_func_count = 0;
 
void *__dso_handle = 0;
 
int __cxa_atexit(void (*f)(void *), void *objptr, void *dso)
{
if (iObject__atexit_func_count >= 32ATEXIT_MAX_FUNCS) {return -1;};
__atexit_funcs[__atexit_func_count].destructor_func = f;
object[iObject].f = f;
__atexit_funcs[__atexit_func_count].obj_ptr = objptr;
object[iObject].p = p;
__atexit_funcs[__atexit_func_count].dso_handle = dso;
object[iObject].d = d;
__atexit_func_count++;
++iObject;
return 0; /*I would prefer if functions returned 1 on success, but the ABI says...*/
return 0;
};
 
void __cxa_finalize(void *f)
/* This currently destroys all objects */
void __cxa_finalize(void *d)
{
uarch_t i = __atexit_func_count;
unsigned int i = iObject;
vga_boot icxxabi;
for (; i > 0; --i)
if (!f)
{
{
--iObject;
/*
object[iObject].f(object[iObject].p);
* According to the Itanium C++ ABI, if __cxa_finalize is called without a
}
* function ptr, then it means that we should destroy EVERYTHING MUAHAHAHA!!
}
*
* TODO:
* Note well, however, that deleting a function from here that contains a __dso_handle
* means that one link to a shared object file has been terminated. In other words,
* We should monitor this list (optional, of course), since it tells us how many links to
* an object file exist at runtime in a particular application. This can be used to tell
* when a shared object is no longer in use. It is one of many methods, however.
**/
//You may insert a prinf() here to tell you whether or not the function gets called. Testing
//is CRITICAL!
while (--i)
{
if (__atexit_funcs[i].destructor_func)
{
/* ^^^ That if statement is a safeguard...
* To make sure we don't call any entries that have already been called and unset at runtime.
* Those will contain a value of 0, and calling a function with value 0
* will cause undefined behaviour. Remember that linear address 0,
* in a non-virtual address space (physical) contains the IVT and BDA.
*
* In a virtual environment, the kernel will receive a page fault, and then probably
* map in some trash, or a blank page, or something stupid like that.
* This will result in the processor executing trash, and...we don't want that.
**/
(*__atexit_funcs[i].destructor_func)(__atexit_funcs[i].obj_ptr);
};
};
return;
};
 
for ( ; i >= 0; )
{
/*
* The ABI states that multiple calls to the __cxa_finalize(destructor_func_ptr) function
* should not destroy objects multiple times. Only one call is needed to eliminate multiple
* entries with the same address.
*
* FIXME:
* This presents the obvious problem: all destructors must be stored in the order they
* were placed in the list. I.e: the last initialized object's destructor must be first
* in the list of destructors to be called. But removing a destructor from the list at runtime
* creates holes in the table with unfilled entries.
* Remember that the insertion algorithm in __cxa_atexit simply inserts the next destructor
* at the end of the table. So, we have holes with our current algorithm
* This function should be modified to move all the destructors above the one currently
* being called and removed one place down in the list, so as to cover up the hole.
* Otherwise, whenever a destructor is called and removed, an entire space in the table is wasted.
**/
if (__atexit_funcs[i].destructor_func == f)
{
/*
* Note that in the next line, not every destructor function is a class destructor.
* It is perfectly legal to register a non class destructor function as a simple cleanup
* function to be called on program termination, in which case, it would not NEED an
* object This pointer. A smart programmer may even take advantage of this and register
* a C function in the table with the address of some structure containing data about
* what to clean up on exit.
* In the case of a function that takes no arguments, it will simply be ignore within the
* function itself. No worries.
**/
(*__atexit_funcs[i].destructor_func)(__atexit_funcs[i].obj_ptr);
__atexit_funcs[i].destructor_func = 0;
/*
* Notice that we didn't decrement __atexit_func_count: this is because this algorithm
* requires patching to deal with the FIXME outlined above.
**/
};
};
};
</source>
 
Anonymous user
Cookies help us deliver our services. By using our services, you agree to our use of cookies.

Navigation menu