I have a question.
What are the _programming_ implications of writing a kernel in c++
instead of c other than having to implement 'new' and 'delete'
subroutines?
Is there additional code that the os programmer needs to write in
order to have a c++ kernel up and running? For example, does rtti or
polymorphism require some bits from libstdc++?
I would guess it would be true for rtti since static_cast<>() and such
functions would need to be implemented also... But Tim Robsinson
mentions OS support being needed for rtti and exceptions... whats that
about?
(http://groups.google.ca/groups?q=rtti+robinson+group:alt.os.development&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=9ft2bm%24480%241%40newsg2.svr.pol.co.uk&rnum=2)
How about polymorphism? Is there anything else other implication from
the programmer's point of view?
Thanks,
Pat Mahoney
I wrote a C++ kernel but so far I didn't need rtti or the C++ kind of
exceptions. Indeed, they would require some
additional support from the OS, but I did not go that far yet. It looks like
you'll need to implement some compiler
specific functions (I use gcc)
Instead I used set_jump-like exceptions (sometimes called structured
exception handling) which works fine for
me at the kernel level. I did not want to use C++ exception handling because
it introduces some additional runtime overhead.
For something similar to rtti I simply use C++ virtual functions (I know
it's not ideal, but you can make it work)
> Is there additional code that the os programmer needs to write in
> order to have a c++ kernel up and running? For example, does rtti or
> polymorphism require some bits from libstdc++?
I did not implement any libstdc++ stuff
> How about polymorphism? Is there anything else other implication from
> the programmer's point of view?
One thing you'll need to remember is to link in the methodtables that gcc
generates for C++ classes. Typically an OS kernel
would have a custom link script, which needs sections like
*(.gnu.linkonce.d.*)
*(.gnu.linkonce.t.*)
Don't worry, the system will kindly remind you if you forget to do this
(symptom: virtual methods calls fail...)
One other thing you might have to provide support for is initialization of
static variables. gcc generates functions like
static_initialization_and_destruction
(or something like that) which get called before the traditional main()
function and after. My solution was to avoid using static variables that
require these
initializers (not all static variables do, for example simple types or
uninitialized pointers don't)
Bye,
Jeroen vB
No implications, it is a question of pure taste.
Personally, I despise <static_cast> and similar operators. :-)
Max
If you don't use RTTI, the runtime support needed for a kernel in
C++ is minimal. Do you really need RTTI? I don't use RTTI, so I
don't know what's required to support it.
Nothing special is required for polymorphism, at least for
single inheritance. You just need to make sure that constructors
get called for objects with virtual methods, to set up the vtable.
I don't know about multiple inheritance.
So to summarize, getting a kernel written in C++ with no RTTI and
single inheritance to run is pretty easy, at least with the GNU
compiler and linker (which is what I'm assuming you're going to use).
-Dave
> Nothing special is required for polymorphism, at least for
> single inheritance. You just need to make sure that constructors
> get called for objects with virtual methods, to set up the vtable.
> I don't know about multiple inheritance.
The compiler should generate the call to the constructor for you (GCC does).
Multiple inheritance more of a can of worms - in our system, it just seemed
to "work" (eg. deriving a UI object from the Widget baseclass and the
Thread baseclass to provide background processing facilities to a UI
widget).
In GCC, you only really need to implement the following functions to get C++
code to play nice:
void *__builtin_new(unsigned long a) : a wrapper for malloc(), should memset
the memory to zero as well.
void __builtin_delete(void *p) : a wrapper for free()
void *__builtin_vec_new(unsigned long a) : exactly the same as
__builtin_new(), but called for vector allocationing object arrays.
void __builtin_vec_free(void *p) : exactly the same as __builtin_free(),
but called for vector de-allocationing object arrays.
void __pure_virtual(void) : called when a pure virtual function is invoked
(print out some error messages, it's a bug in your kernel).
Oh yeah - and one other important thing. Your startup code must invoke all
the constructors for global / staticly allocated objects:
typedef void (*CXXFPTR)(void);
extern CXXFPTR __CTOR_LIST__[];
extern CXXFPTR __DTOR_LIST__[];
void __do_global_ctors(void)
{
int f;
DbgPrint("__CTOR_LIST__=");
DbgPrintHex((unsigned long)__CTOR_LIST__);
DbgPrint("\n__CTOR_LIST__[0]=");
DbgPrintHex((unsigned long)__CTOR_LIST__[0]);
DbgPrint("\n__CTOR_LIST__[1]=");
DbgPrintHex((unsigned long)__CTOR_LIST__[1]);
DbgPrint("\n__CTOR_LIST__[2]=");
DbgPrintHex((unsigned long)__CTOR_LIST__[2]);
if(__CTOR_LIST__[0]==0)
{
DbgPrint("\nno C++ globals, skipping static constructors\n");
return;
}
DbgPrint("\nrunning static constructors: count=%d\n",__CTOR_LIST__[0]);
for(f=1; __CTOR_LIST__[f]; f++)
{
DbgPrint(" @ 0x");
DbgPrintHex((unsigned long)__CTOR_LIST__[f]);
DbgPrint("\n");
__CTOR_LIST__[f]();
}
}
In my link script, there's a block that looks like:
PROVIDE (__CTOR_LIST__ = .);
.ctors :
{
*(.ctors)
} >code
This embeds a table of constructors in the binary, and exports the symbol
__CTOR_LIST__ as a pointer to it. The function given above will then invoke
all the global constructors from the list.
Laters,
Craig.
--
You wish.
Anybody know why GCC generate the code for constructors two time ? Both
copy seem to be the same, weird!
Bye
Umberto Giacobbi
They claim it's a 'feature', not a bug. Apparently there's different kinds
of constructors, memory allocating ones and initializing ones.
Same for destructors. I never quite understood why either, I even wrote a
small script to filter some of them out of my kernel since they're mostly
not used.
"In some cases the compiler might do better" it was said in response to all
the bug reports coming in. So hopefully there will be improvement
in future releases of GCC, because at the moment it's generating those
stupid things for even the most trivial of cases
Jeroen vB
The only thing they could do is to call the static initialization from
the allocating one. But when you turn on optimization, the compiler will
inline the code, so you end up with two constructors.
Viktor
ps:
If you can't turn this off with -O0, then it's hardwired into the compiler,
which is imho not a good solution.
MSVC generates:
- your hand-written destructor as a function
- "scalal deleting destructor" thunk which calls your hand-written
destructor
- "vector deleting destructor" thunk which calls your hand-written
destructor
Nothing strange if GCC does the similar. Maintaining the correct
semantics of constuctors and destructors require a lot of machine code
auto-generated by the compiler.
Borland C++ went a way by generating the prolog/epilog code for
constuctors and destructors in a way other then for plain functions,
to avoid these "scalal deleting destructor" thunks. C++ standard
allows this - it allows implementation of constuctors and destructors
in a way other then functions, and thus prohibits taking their
addresses.
Max
>From Jeroen (taken from google since I never saw that reply on my
>usenet server :)
>They claim it's a 'feature', not a bug. Apparently there's different kinds
>of constructors, memory allocating ones and initializing ones.
>Same for destructors. I never quite understood why either, I even wrote a
>small script to filter some of them out of my kernel since they're mostly
>not used.
">In some cases the compiler might do better" it was said in response to all
>the bug reports coming in. So hopefully there will be improvement
>in future releases of GCC, because at the moment it's generating those
>stupid things for even the most trivial of cases
No matter what, geneting two time the exact same code in two different
function doesn't makes any sense. I solved that by inlining my constructors
which only contained a call to a private method called init() which did the
work. That way the code was only generated one time, in my case it was
important for the resulting file size to be as small as possible. Anyway,
it's just stupid, but it's certainly not the only weird thing that I found
in gcc :(