Static Objects in Dynamically Loaded C++ Modules

When you're dynamically loading a C++ .so into a main program which was written in C, the constructor functions for static objects are never called. Why do we care? For me, the reason to care is that I want to write extension modules in C++ for the Python language. The Python interpreter is written in C, and recompiling it with C++ is a real pain (if you can get it to compile that way at all!), so I need to get dynamic loading of my C++ modules to work correctly with the unmodified interpreter.

What's Going Wrong

When you compile a C++ file to a .o, the C++ compiler creates special functions to call the constructors of any static objects. Since the Python interpreter is written & compiled in C, and therefore doesn't know about constructors, it never calls these magic functions when a C++ module is dynamically loaded. So, we need to make sure that these functions get called.

How to Fix It

Basically, you compile all your files to .o's, inspect the .o files (and any libraries you're using) to find the magic functions, then construct a function that calls all the magic ones. I've written a little Python script to do this. It's really pretty braindead; you give it a list of .o's and libraries, it writes (to stdout) a file that contains one function, called static_construct. Redirect this to a file, and compile it. Put static_construct's prototype in your module, and write your module so that it calls static_construct() right away. (In Python modules, that means the first thing initxxxmodule does is call static_construct().) You only have to make these changes to your module once, not every time you compile. Link everything together into the module. And now, when the Python interpreter loads your module, the constructors get called. Much fun.

On an Irix machine, the magic functions all start with __sti__. (There are also destructor-calling functions for these objects, starting with __std__, but probably you don't really care about destructing at the end of the program. If you do you can figure it out from the way I'm doing constructors.)

So now the relevant part of my makefile looks like this:	$(MYOBJ)
	static_construct $(MYOBJ) /usr/lib/ $(CORIOLISDIR)/lib/sgi_53/libcsupport.a > sc.c
	CC $(WARNINGS) -c $(CFLAGS) $(IFLAGS) sc.c -o sc.o
	ld -shared sc.o $(MYOBJ)  -o -L$(CORIOLISDIR)/lib/sgi_53 -lcsupport
Many, many thanks to David Baraff for figuring out how to do this.
Ari Rapkin
Last modified: Tue Oct 29 14:06:22 EST 1996