How MulleObjC-startup Provides Automatic Startup Code for MulleObjC

MulleObjC-startup exports a constructor symbol __register_mulle_objc_universe that the linker preserves and the platform runtime invokes before main, automatically initializing the MulleObjC universe without requiring explicit user code.

The mulle-objc/mulleobjc-startup repository solves the bootstrap problem of the MulleObjC runtime by supplying the necessary startup code through a minimal static library. By exposing a specially-named symbol that the dynamic loader recognizes as a constructor, the library ensures the Objective-C environment is fully operational before the first line of main executes.

The Core Mechanism: Exported Symbols and Linker Integration

MulleObjC-startup operates by injecting a single exported function into the final binary. This function acts as a hook that the platform runtime calls during program initialization.

Defining the Exported Symbol in src/MulleObjC-startup.m

The implementation centers on the source file src/MulleObjC-startup.m, which defines the macro MULLE_OBJC_DEFINE__register_mulle_objc_universe. This macro causes the private include <MulleObjC/mulle-objc-startup-private.inc> to emit the symbol __register_mulle_objc_universe with dllexport visibility. The symbol serves as the entry point for all runtime initialization activities.

Enforcing Linker Visibility with CMake

Static libraries typically do not export symbols unless explicitly requested. To ensure __register_mulle_objc_universe survives the linking process and remains visible to the dynamic loader, the CMake helper cmake/share/ExecutableObjC.cmake adds the linker flag -exported_symbol,___register_mulle_objc_universe. This flag guarantees the symbol is present in the final binary even though the library itself contains no other public interfaces.

Runtime Initialization Sequence

Once the symbol is exported, the platform runtime handles the rest of the initialization chain automatically.

The Constructor Hook and bang() Function

The private header included by src/MulleObjC-startup.m registers __register_mulle_objc_universe as a constructor—a function the platform runtime executes automatically during program load, prior to main. This constructor immediately forwards execution to the static helper function bang(), defined in the same source file.

Universe Creation via MulleObjCBang()

Inside bang(), the current global universe configuration is copied using mulle_objc_global_get_default_universeconfiguration(), then the helper MulleObjCBang() performs the heavy lifting. This function creates the universe object, installs the default allocator, and registers all built-in MulleObjC classes, method tables, and exception handlers.

static void bang( struct _mulle_objc_universe *universe,
                  struct mulle_allocator *allocator,
                  void *userinfo )
{
    struct _mulle_objc_universeconfiguration config;
    memcpy( &config,
            mulle_objc_global_get_default_universeconfiguration(),
            sizeof( config ));
    MulleObjCBang( universe, allocator, &config );
}

After MulleObjCBang() returns, the full MulleObjC runtime is operational and ready to receive messages.

Integrating MulleObjC-startup Into Your Build

No explicit initialization calls are required in application code. Simply linking against the static library injects the constructor into the final binary, ensuring automatic initialization.

/* No explicit call needed – just link the static library */
#include <MulleObjC/MulleObjC.h>

int main(void)
{
    // At this point the MulleObjC universe is already set up.
    id obj = [NSObject new];
    NSLog(@"Created %@", obj);
    [obj release];
    return 0;
}

Link the library using the provided CMake integration or manually:

clang -o myprog main.c -lMulleObjC-startup -lMulleObjC

Summary

  • Exported Symbol: The macro MULLE_OBJC_DEFINE__register_mulle_objc_universe in src/MulleObjC-startup.m creates the entry point __register_mulle_objc_universe.
  • Linker Enforcement: The flag -exported_symbol,___register_mulle_objc_universe in cmake/share/ExecutableObjC.cmake prevents symbol stripping.
  • Automatic Execution: The runtime calls the exported constructor before main, invoking bang() and subsequently MulleObjCBang().
  • Zero Configuration: Developers link the library and immediately have a fully initialized MulleObjC environment available at program start.

Frequently Asked Questions

What is the exact symbol that enables automatic MulleObjC initialization?

The symbol is __register_mulle_objc_universe (with two leading underscores). This function is emitted by the MULLE_OBJC_DEFINE__register_mulle_objc_universe macro and acts as the constructor that triggers the runtime bootstrap sequence.

Why does the CMake configuration use a special linker flag?

The flag -exported_symbol,___register_mulle_objc_universe (three leading underscores in this context) is required because static library symbols are typically internal to the linked binary. Without this explicit export directive, the linker would strip the symbol, preventing the platform runtime from locating and executing the constructor during program load.

At what point during program execution does the MulleObjC universe become available?

The MulleObjC universe is fully initialized before main() begins execution. The platform runtime processes constructor functions during the loading phase, meaning bang() and MulleObjCBang() complete their work—setting up the universe, allocator, and class tables—prior to any application code running.

Can I customize the universe configuration during startup?

The bang() function copies the default configuration via mulle_objc_global_get_default_universeconfiguration() before passing it to MulleObjCBang(). While the default startup sequence uses global defaults, advanced users can modify the universe configuration by intercepting the initialization flow or providing a custom configuration structure before the constructor executes, though this requires deeper integration with the runtime internals.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →