Page 1 of 1

Disable global constructors

Posted: Thu Nov 13, 2014 7:38 pm
by KemyLand
Is there a way to disable global (and global static) constructors?

I want my C++ kernel to call some functions during initialization, that effectively construct some of the most vital objects in the system. I have noticed that leaving a single declaration of a class instance in the global (or file-local) namespace would implicitly call the empty constructor for that instance (that was obviously long ago). So I got a kernel coding rule: always declare an empty constructor (does nothing...) when declaring a class. That constructor would be empty in the defining file. As my kernel doesn't call global constructors (and won't [-X ), all those stuff remains in the file, but's never called. I want to remove that effect, before its too late [-o< . How can I force GCC to stop emitting that stuff?

Re: Disable global constructors

Posted: Thu Nov 13, 2014 11:52 pm
by xenos
My "solution" for this problem is to use placement new. Schematically, it works like this:

GlobalClass.h:

Code: Select all

class GlobalClass
{
public:
	// Constructor
	GlobalClass(void);

	void SomeFunction(void);
};

extern char gc_space[];
inline GlobalClass& gc_object(void) { return(reinterpret_cast<GlobalClass&>(gc_space)); }
GlobalClass.cpp:

Code: Select all

#include <GlobalClass.h>

GlobalClass::GlobalClass(void)
{
	// Initialize object.
}

void GlobalClass::SomeFunction(void)
{
	// ...
}

char gc_space[sizeof(GlobalClass)];
Main.cpp:

Code: Select all

#include <GlobalClass.h>

void main(void)
{
	new (gc_space) GlobalClass; // Explicitly construct object (calls constructor).
	gc_object().SomeFunction(); // Use object.
}
This way the constructor gets called explicitly when the object is constructed using placement new syntax. Instead of the default constructor without arguments also a more general constructor can be used. The main drawback I see for this method is that GCC issues a warning since it violates the "strict aliasing" rules, which forbid objects of different type to reside at the same memory location (in this case an object of type GlobalClass and a char array). As far as I know, this causes undefined behavior when one is accessing both of them. I'm still looking for a cleaner solution which resolves this warning.

Re: Disable global constructors

Posted: Fri Nov 14, 2014 2:23 am
by alexfru
Write in C. :)

Re: Disable global constructors

Posted: Fri Nov 14, 2014 2:30 am
by max
KemyLand wrote:As my kernel doesn't call global constructors (and won't [-X )
Whats the problem with calling global constructors? It's easy, and you can do it just when your kernel is ready, like after setting up your mm. Relying on yourself to do it right every time is a bad idea, because you *will* forget it somewhere and get undebuggable errors. What are your worries?

Re: Disable global constructors

Posted: Fri Nov 14, 2014 5:10 am
by xenos
Certain objects may depend on others, so it's important in which order their constructors are called. It's also possible that things need to be done inbetween constructor calls. This cannot be done easily if one has just an auto-generated list of constructors, as it would be the case in the "standard approach". See my kernel's startup function for an example.

Re: Disable global constructors

Posted: Fri Nov 14, 2014 5:49 am
by sortie
The obvious, simple solution to this problem is to call the global constructors and only initialize global data in them. It is a design mistake to have even remotely interesting constructors. Placement new is just a poor attempt at fixing this. Don't have global singleton classes, just havd regular functions.

Re: Disable global constructors

Posted: Fri Nov 14, 2014 6:26 am
by Combuster
sortie wrote:Don't have global singleton classes, just havd regular functions.
Replace "class" with "namespace". Goodbye constructor.

Re: Disable global constructors

Posted: Fri Nov 14, 2014 6:27 am
by bluemoon
Yes, I agree with just empty constructor plus an init function.

Code: Select all

class GlobalClass {
public:
   // Default Constructor

   int init();
   void SomeFunction(void);
};

GlobalClass gc;
void main(void){
   int rez = gc.init(); // Explicitly init object
   gc.SomeFunction(); // Use object.
}
If you care about the constructor sequence & state, you may also worry with the exceptions, and init() provide much flexibility.

Re: Disable global constructors

Posted: Fri Nov 14, 2014 8:00 am
by xenos
Combuster wrote:
sortie wrote:Don't have global singleton classes, just havd regular functions.
Replace "class" with "namespace". Goodbye constructor.
This doesn't work if your classes have virtual functions and derived classes and you wish to decide at runtime which of them to use. Just to give you an example, I have a Clock class, from which different classes (PITClock, ACPIClock, HPETClock) are derived. The kernel measures time using a global clock object which has base class Clock, but it is decided upon at runtime of which derived class the actual clock is instantiated. This does not work with namespaces (otherwise I would indeed use namespaces instead). (The clock is just an example - there are plenty other such cases, such as the physical memory manager etc.)

Re: Disable global constructors

Posted: Fri Nov 14, 2014 8:37 am
by Combuster
But in that example we're not talking about singletons either.

Re: Disable global constructors

Posted: Fri Nov 14, 2014 10:40 am
by sortie
Namespaces + function pointers ~= virtual classes.

The obvious solution to placement new is to get dynamic allocation online very early.

Re: Disable global constructors

Posted: Fri Nov 14, 2014 12:45 pm
by KemyLand
As mentioned *long* above, my problem is call-order related. I can't leave GCC do what it wants in the order it pleases. This is what would happen:

I have a KInit macro. It wraps a extern "C" function (using __VA_ARGS__ tricks :P) named as __init ## subsys ## __ (i.e. subsystem LIB's KInit is named __initLIB__ internally). As a subsystem is able to hold another subsystem, its easy to make a call stack from this macro, along with KInitCall(). KInit() also provides the subsystem with a single-initialization mechanism: if a KInitCall() occurs twice to a single subsystem, Panic Mode is set, and you know how this ends...

My extern "C" KernelInit() function is responsible for KInitCall()ing all the main subsystems, which can then call their own (sub)subsystems.

I don't want this "Global Constructor Table" to even exist. I've implemented placement new, but I don't like that approach too much [-X.

Although I can implement a KInit() version for the init() approach, the global constructor table is still there. I will try with this approach, but I still want to remove that extra table...

Re: Disable global constructors

Posted: Fri Nov 14, 2014 12:52 pm
by sortie
Then don't have any global constructors and the table is empty. I recommend you still have it, even if potentially empty, and call it, as the compiler might use it various language features. If it is empty, no harm done, if the compiler can't do something at compile time, it might emit a global constructor and do it at start up. Fighting the table is fruitless. If you really don't want it, ignore it, don't call _init, don't link in the sections in your linker script, and it all goes away.

Either way, there are no dependency issues here if you design your code sanely: The global constructors must be trivial and just initialize global memory. That way, there are no dependencies.

As for dependencies between subsystems, I recommend a no-initialization approach. If you can initialize it at compile/link/load time rather than run-time, then great, do that. That removes these initialization dependencies. For instance, my system call table is done at compile time, and so can the GDT, IDT and such be. If everything can be done at that time, there is very, very little initialization code remaining and it can just immediately execute the real code. (Of course, this isn't fully possible in a kernel, and might not in your design, but it's a simplicity goal I aim towards).

Re: Disable global constructors

Posted: Fri Nov 14, 2014 1:44 pm
by KemyLand
I think I've finally found the solution :idea:
sortie wrote:Then don't have any global constructors and the table is empty.
Sortie, you just gave me an excellent idea:
In <def.h> (Main type definitions):

Code: Select all

//...
#define Attribute(attr) __attribute__((__attr__))
//...
#define Inline Attribute(always_inline)
//...
Then all classes will declare

Code: Select all

Inline FooClass() {}
Then I can call global constructors, if any put in there by GCC, call KernelInit(), and problem solved 8)
Thanks to all of you!

By the way...
sortie wrote:As for dependencies between subsystems, I recommend a no-initialization approach. If you can initialize it at compile/link/load time rather than run-time, then great, do that. That removes these initialization dependencies. For instance, my system call table is done at compile time, and so can the GDT, IDT and such be. If everything can be done at that time, there is very, very little initialization code remaining and it can just immediately execute the real code. (Of course, this isn't fully possible in a kernel, and might not in your design, but it's a simplicity goal I aim towards).
Yes, in my design it isn't any practical :( . I follow that rule (initialize as little as you can at run-time), but there are still some run-time dependencies (i.e. unpredictable dynamic memory allocation) that I can't remove (by now...)

Re: Disable global constructors

Posted: Fri Nov 14, 2014 5:14 pm
by xenos
sortie wrote:Namespaces + function pointers ~= virtual classes.
If I wanted to use function pointers and thus throw away a lot of the nice type safety of C++, I wouldn't use C++ at all. Using virtual classes instead of function pointers is an intentional design decision.
The obvious solution to placement new is to get dynamic allocation online very early.
...unless the dynamic allocator itself depends on global objects, in which case this "solution" does not work for these objects.