Computing > Programming

C - How to doing things before main() and after exit()

<< < (3/4) > >>

Keith956:
Sounds to me like you want an app that is extendable using plugins. Have a look at:

http://www.cplusplus.com/articles/48TbqMoL/

The basic idea is you create DLL's (or shared libraries in the *nix world) that have well-defined interfaces. Then your app looks for them in some predetermined location, loads them and makes them available as extensions to your app.


hamster_nz:
I might not have made it clear that this already works, and works really well.

I am just exploring what other similar things can be done, and where such things are documented.

IanB:

--- Quote from: hamster_nz on May 14, 2021, 06:26:37 am ---I might not have made it clear that this already works, and works really well.

I am just exploring what other similar things can be done, and where such things are documented.

--- End quote ---

These things are not generally part of the language standard, but rather are specific to particular vendors and implementations. So they will be documented by the vendor of the tools that you are using.

You should understand that what you are doing is absolutely not "C", and therefore is not portable. If you move to a different platform everything you are doing will likely stop working.

Any time you see something with two underscores in it, such as "__attribute__", that is an indication that you are dealing with a local extension, and have taken a detour off the main road.

Nominal Animal:

--- Quote from: hamster_nz on May 14, 2021, 01:04:02 am ---
--- Code: ---__attribute__((constructor))
--- End code ---

--- End quote ---
These function attributes are GNU extensions provided by GCC, and implemented by the linker in Linux and some BSD variants (but not Mac OS); they are not part of C, nor are they supported in all operating systems.

In Linux, functions marked constructor have their addresses listed in .init_array section, and functions marked destructor in .fini_array section.  The startup/linker code executes the constructors just before calling main() and destructors when returning from main() and when calling exit() (but not when calling _exit().  This means that such constructor/destructor functions can be static, without an exported symbol at all.

(The section variable attribute can be used to collect elements (of the same type, properly aligned and sized) from different compilation units into a single array.  This is used in e.g. the Linux kernel to collect module license information, and can be used to make it trivial for a system utility to collect its command-line options and other structures automagically based on what parts were enabled at compile time.)

When an executable is loaded in Linux, the linker can even call a function to resolve a symbol.  This is done via the ifunc function attribute, and is based on the ELF file standard optional features.  (This means it is not technically a Linux extension, but an ELF extension.  The constructor/destructor attributes are similarly based on optional features in the ELF standard).  This is extremely useful if you want your binaries to include optimized functions for different hardware architectures, as this incurs no extra overhead at all compared to a normal, non-inline function.

When an executable or library is loaded in Linux, these get called recursively, but I'm not exactly sure of the ordering; normally (without priority overrides, i.e. a priority argument to the constructor function attribute), I do believe "everything just works" because the depended-on library constructors get called before the dependee's.  Thus, for the executable itself, everything that is available at the beginning of main() should be available in the constructors (assuming no priority has been set for the constructor); for dynamic libraries, everything the dynamic library is linked against, should be available when its default-priority constructors are called.

This also means that if your application does not need to unload plugins at run time, it is basically trivial to implement plugins in Linux due to the support of these ELF features.  Basically, your application provides a registration function that plugins can use to register the new features the plugin implements, and the plugin just needs to have a (static) constructor function that registers the features when the library is loaded.  Trick is, if you write the code properly (so that it is safe to call these registration functions before main(), as long as the standard C library has been initialized), you don't even need to add dlopen() code to the application (although I definitely would; I'd probably scan an user-specific plugin directory for symlinks to the currently active plugins), as the user can preload the desired plugins by setting the LD_PRELOAD environment variable to point to the plugin file paths (separated either by spaces or by colons : ).

Although there are more "tricks" described in the above pages related to GCC and ELF binaries and libraries, these are the ones I routinely use.

Nominal Animal:
(Before anyone asks, similar features are available in the Linux kernel because its binary is also in ELF format.  Obviously, it uses its own internal dynamic linker, but that one implements these features as well.  That linker is actually pretty crafty, as it examines various headers in kernel module ELF headers, and marks the kernel tainted etc. depending on those (including the license).)

Navigation

[0] Message Index

[#] Next page

[*] Previous page

There was an error while thanking
Thanking...
Go to full version