# Role of mulle-atinit and mulle-atexit in the MulleObjC Lifecycle

> Discover the role of mulle-atinit and mulle-atexit in MulleObjC startup. These functions provide deterministic initialization and shutdown hooks for the Objective-C runtime universe.

- Repository: [mulle-objc/mulleobjc-startup](https://github.com/mulle-objc/mulleobjc-startup)
- Tags: internals
- Published: 2026-03-07

---

**mulle-atinit and mulle-atexit provide deterministic initialization and shutdown hooks that automatically create the Objective-C runtime universe before `main()` runs and destroy it after program termination.**

The mulle-objc/mulleobjc-startup repository is a thin static library that orchestrates the Objective-C runtime lifecycle without requiring explicit initialization calls in user code. By leveraging the role of mulle-atinit and mulle-atexit, the library ensures the runtime is ready before any user code executes and properly cleaned up after the program exits.

## What Are mulle-atinit and mulle-atexit?

These are helper libraries from the mulle-core project that provide deterministic execution points outside the standard C runtime initialization sequence.

### mulle-atinit: Pre-main Initialization

**mulle-atinit** defines the exported symbol `__mulle_atinit`, which the linker arranges to call **before** the `main()` function begins execution. According to the mulleobjc-startup source code, this symbol triggers the creation of the global Objective-C universe by calling `__register_mulle_objc_universe()`.

### mulle-atexit: Post-main Cleanup

**mulle-atexit** provides the counterpart mechanism through the exported symbol `__mulle_atexit`. The linker ensures this function executes **after** `main()` returns or after `exit()` is called, invoking `__unregister_mulle_objc_universe()` to tear down the runtime and release static resources.

## Implementation in src/MulleObjC-startup.m

The actual callback implementations live in `src/MulleObjC-startup.m`, the repository's sole source file. The code uses compiler section attributes to place the functions where the mulle-atinit and mulle-atexit libraries can discover them:

```objc
// src/MulleObjC-startup.m
void __register_mulle_objc_universe(void);
void __unregister_mulle_objc_universe(void);

void __attribute__((section("__DATA,__mulle_atinit")))
__mulle_atinit(void)
{
    __register_mulle_objc_universe();
}

void __attribute__((section("__DATA,__mulle_atexit")))
__mulle_atexit(void)
{
    __unregister_mulle_objc_universe();
}

```

The `section("__DATA,__mulle_atinit")` and `section("__DATA,__mulle_atexit")` attributes instruct the **mulle-clang** toolchain to place these symbols in special data sections. During linking, the helper libraries scan these sections to build global initialization and exit lists.

## Linker Configuration for Symbol Export

For the hooks to function, the symbols must survive link-time dead-code elimination. The CMake build system explicitly exports these symbols in two key files:

In `cmake/share/PROJECT_MAKE-config.cmake.in`:

```cmake
add_link_options("SHELL:LINKER:-exported_symbol,__mulle_atinit")

```

In `cmake/share/ExecutableObjC.cmake`, a similar directive ensures `__mulle_atinit` remains visible for Objective-C executables. Without these exports, the linker might strip the symbols, preventing the automatic initialization mechanism from functioning.

## The MulleObjC Lifecycle Sequence

The complete lifecycle follows a deterministic four-phase sequence:

1. **Link time** – The build system exports `__mulle_atinit` and `__mulle_atexit`, placing them in the special sections recognized by the helper libraries.
2. **Program start** – The mulle-atinit mechanism invokes `__mulle_atinit`, which calls `__register_mulle_objc_universe()` to create the Objective-C universe.
3. **User code execution** – All Objective-C classes, selectors, and objects operate on the fully initialized runtime without explicit setup.
4. **Program termination** – The mulle-atexit mechanism invokes `__mulle_atexit`, calling `__unregister_mulle_objc_universe()` to release resources and destroy the universe.

## Practical Code Examples

Under normal circumstances, user code requires no explicit initialization. The universe exists before `main()` begins:

```c
#include <MulleObjC-startup/MulleObjC-startup.h>

int main(void)
{
    // Runtime is already initialized
    @autoreleasepool {
        NSLog(@"Hello from MulleObjC!");
    }
    // Cleanup happens automatically
    return 0;
}

```

If you need to force early registration in specialized scenarios (rarely necessary), you can invoke the registration function directly:

```c
extern void __register_mulle_objc_universe(void);

__attribute__((constructor))
static void force_universe_init(void)
{
    __register_mulle_objc_universe();
}

```

However, the mulle-atinit and mulle-atexit libraries handle this automatically under standard configurations.

## Summary

- **mulle-atinit** provides deterministic pre-main initialization via the `__mulle_atinit` symbol, ensuring the Objective-C universe exists before user code runs.
- **mulle-atexit** provides deterministic post-main cleanup via the `__mulle_atexit` symbol, destroying the universe after program termination.
- The implementation in `src/MulleObjC-startup.m` uses section attributes to place callbacks where the helper libraries can discover them.
- CMake linker options in `PROJECT_MAKE-config.cmake.in` and `ExecutableObjC.cmake` explicitly export these symbols to prevent dead-code elimination.
- Together, these mechanisms create a reliable, deterministic lifecycle that works across all platforms supported by the Mulle toolchain.

## Frequently Asked Questions

### What happens if __mulle_atinit is not exported by the linker?

If the symbol is not exported using the `-exported_symbol` linker flag, the linker may treat it as dead code and remove it. Consequently, `__register_mulle_objc_universe()` never executes, the Objective-C runtime remains uninitialized, and any Objective-C code in the program will fail or crash when attempting to access the universe.

### Can I use MulleObjC without mulle-atinit and mulle-atexit?

While technically possible by manually calling `__register_mulle_objc_universe()` before any Objective-C code and `__unregister_mulle_objc_universe()` at program end, this defeats the purpose of the mulleobjc-startup library. You would lose the deterministic, linker-driven initialization guarantees and must carefully manage initialization ordering yourself.

### When exactly does __mulle_atexit run relative to C++ destructors?

The `__mulle_atexit` function runs after `main()` returns but before the C++ runtime destroys static objects. This ordering ensures the Objective-C universe remains valid for any Objective-C code that might be invoked by C++ destructors, while still allowing proper cleanup before the process terminates.

### How does mulle-atinit differ from __attribute__((constructor))?

Unlike `__attribute__((constructor))`, which relies on the dynamic loader's constructor mechanism and offers limited ordering guarantees, **mulle-atinit** provides deterministic, ordered initialization through explicit linker section scanning. This allows precise control over initialization priority and ensures the Objective-C universe is ready before any user constructors execute.