Disable global constructors

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
KemyLand
Member
Member
Posts: 213
Joined: Mon Jun 16, 2014 5:33 pm
Location: Costa Rica

Disable global constructors

Post 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?
Happy New Code!
Hello World in Brainfuck :D:

Code: Select all

++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
[/size]
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Disable global constructors

Post 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.
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
alexfru
Member
Member
Posts: 1112
Joined: Tue Mar 04, 2014 5:27 am

Re: Disable global constructors

Post by alexfru »

Write in C. :)
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: Disable global constructors

Post 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?
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Disable global constructors

Post 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.
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
sortie
Member
Member
Posts: 931
Joined: Wed Mar 21, 2012 3:01 pm
Libera.chat IRC: sortie

Re: Disable global constructors

Post 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.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Disable global constructors

Post by Combuster »

sortie wrote:Don't have global singleton classes, just havd regular functions.
Replace "class" with "namespace". Goodbye constructor.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
bluemoon
Member
Member
Posts: 1761
Joined: Wed Dec 01, 2010 3:41 am
Location: Hong Kong

Re: Disable global constructors

Post 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.
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Disable global constructors

Post 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.)
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: Disable global constructors

Post by Combuster »

But in that example we're not talking about singletons either.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
User avatar
sortie
Member
Member
Posts: 931
Joined: Wed Mar 21, 2012 3:01 pm
Libera.chat IRC: sortie

Re: Disable global constructors

Post by sortie »

Namespaces + function pointers ~= virtual classes.

The obvious solution to placement new is to get dynamic allocation online very early.
User avatar
KemyLand
Member
Member
Posts: 213
Joined: Mon Jun 16, 2014 5:33 pm
Location: Costa Rica

Re: Disable global constructors

Post 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...
Happy New Code!
Hello World in Brainfuck :D:

Code: Select all

++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
[/size]
User avatar
sortie
Member
Member
Posts: 931
Joined: Wed Mar 21, 2012 3:01 pm
Libera.chat IRC: sortie

Re: Disable global constructors

Post 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).
User avatar
KemyLand
Member
Member
Posts: 213
Joined: Mon Jun 16, 2014 5:33 pm
Location: Costa Rica

Re: Disable global constructors

Post 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...)
Happy New Code!
Hello World in Brainfuck :D:

Code: Select all

++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
[/size]
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Disable global constructors

Post 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.
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
Post Reply