Libraries that use glib sometimes need to use glib's link flags --- what's the best way to achieve this?

Hello everyone,

I’m one of the libvips maintainers, and I have a question and suggestion regarding gobject and web servers.

gobject links with nodelete on linux hosts to prevent unloading and reinitializing. If gobject unloads and reloads, types can become confused, since type unloading is not really supported (I hope I have this right).

This means that any downstream library which uses gobject must also link with nodelete, or types will double init on reload.

I can’t see a way for a library that builds on gobject to know if it has been linked with nodelete. Does gobject have a way to pass this information on, or should downstream libraries implement their own logic?

Perhaps gobject could add something to gobject-2.0.pc? Maybe a variable with these extra link flags?

This PR adds nodelete to php-vips and has some discussion of the issue:

This comment tries to explain why crashes happen on load and unload:

Exactly correct. Type unloading is not supported (and is not going to be supported in the foreseeable future). One reason for that is that there’s no way to guarantee that GTypes would be registered in the same order if the library was re-loaded, which would mean type references (GTypes are a numeric ID) from before unloading would suddenly resolve to different types from before and everything would break.

I’m not sure about that. There’s frustratingly little documentation on what nodelete actually means, but I would have thought the loader would know to keep GObject loaded even if the library which originally caused it to be loaded into the process is unloaded. This is at least partially backed up by the Sun loader documentation.

However, that thinking is obviously at odds with what you’re seeing, so there must be something else going on.

Are you statically linking to GLib, or linking in some other non-standard way?

Is the gobject_init() function definitely getting called twice?

Hi @pwithnall, thanks for responding.

Yes, we’re dynamically linking everything in (I think?) a completely vanilla way.

I think the issue is that libvips is creating a set of gtypes for various classes it uses. If libvips is unloaded but gobject is not, libvips will attempt to create the gtypes again on reload.

So the sequence is:

  1. apache starts up
  2. apache starts mod_php
  3. mod_php starts all PHP extensions
  4. The vips.so PHP extension uses dlopen() to load libvips.so
  5. libvips.so creates a set of gtypes for classes, triggering the load and init of libgobject.so
  6. apache now forks four times to make the worker processes
  7. Some time later, a user restarts apache with graceful
  8. Apache shuts down mod_php
  9. libgobject.so is linked with nodelete, so it stays in memory, but other libs are cleared out
  10. As the new libvips inits, it attempts to create the classes it needs, but they half-exist already (the gobject half of the class) and massive chaos ensues

The solution is to make sure that libraries which use gobject classes are not unloaded and reloaded, either by giving the RTLD_NODELETE flag to dlopen(), or by linking the library with the nodelete flag.

This means that every library that uses gobject / gtype needs to have logic to detect platforms which need nodelete (I’ve no idea what *BSD or macOS do about this, or if a similar issue exists on win) and enable it.

This seems like duplication of effort and a chance for subtle errors to creep in. Could a solution be to somehow export knowledge about the status of nodelete from gobject?

We found and fixed this a while ago, but we’ve just been bitten again after adding support for loadable file format modules to libvips, argh.

Perhaps another fix would be for gmodule to set RTLD_NODELETE when it dlopen()s a module? Modules that gmodule loads are very likely to be gobject users, so won’t be unloadable.

This is starting to go well beyond my knowledge of GType internals, but have you looked at g_type_register_dynamic()?

That’s not really accurate. You can create types that can be dynamically unloaded—see GTypeModule and g_type_register_dynamic(). There are plenty of GTK and GNOME-based applications that use those for plugins.

The main issue is that: any library calling g_type_register_static() cannot be unloaded, for the reasons outlined by @pwithnall.

That would be impossible, because:

  1. it would prevent any plugin system that registers dynamic types from working
  2. it would break non-GObject modules

GModule is not really used just for GObject. There’s a reason why libgmodule lives below libgobject in the stack.

Thanks @ebassi. I hadn’t come across g_type_register_dynamic(), but I’m not sure that’s an option for us.

How about adding a nodelete variable to gmodule-2.0.pc? It would save downstream libraries from duplicating the platform tests. Maybe something as simple as:

library_link_flags=-Wl,-z,nodelete

Out of interest, why not?

No. As I said above, GModule doesn’t really care about what GObject does.

Plus, -Wl,-z,nodelete is not really portable across toolchains.

Conclusion (I think): downstream libraries should copy-paste the linker flag logic from glib’s meson.build. I’ll close.