How to Debug Startup Issues in MulleObjC: A Complete Guide

To debug startup issues in MulleObjC, build with debug symbols and use mulle-sde debug stacktrace or GDB to trace failures in __register_mulle_objc_universe before main() executes.

When an executable links against the mulle-objc/mulleobjc-startup static library, the runtime initializes before your main() function ever runs. Any crash, missing symbol, or misconfiguration during this phase occurs inside the startup sequence defined in src/MulleObjC-startup.m, making standard debugging techniques ineffective. Understanding how to intercept and diagnose these early failures is essential for troubleshooting MulleObjC startup issues.

Understanding the MulleObjC Startup Sequence

The startup sequence is a carefully orchestrated chain of events that creates the Objective-C universe before program entry. Debugging startup issues in MulleObjC requires knowing exactly where this process can fail.

Entry Point and Symbol Registration

When the linker resolves symbols for your executable, it pulls in __register_mulle_objc_universe from src/MulleObjC-startup.m. This symbol is exported because the source defines MULLE_OBJC_DEFINE__register_mulle_objc_universe. The linker automatically calls this constructor function before main(), making it the absolute first point of entry for the MulleObjC runtime.

Universe Creation and Initialization

The __register_mulle_objc_universe function includes MulleObjC/mulle-objc-startup-private.inc and calls bang(), which copies the global default universe configuration via mulle_objc_global_get_default_universeconfiguration(). It then passes this configuration to MulleObjCBang, which creates the _mulle_objc_universe, installs the basic class hierarchy, registers atexit/atinit handlers, and returns control to the program entry point.

Common Causes of Startup Failures

Startup crashes in MulleObjC typically stem from three categories of problems:

  • Missing dependencies – The startup library requires mulle-atinit and mulle-atexit. If these are not linked, symbol resolution fails before universe creation.
  • Misconfigured universe parameters – Invalid configuration values passed through mulle_objc_global_get_default_universeconfiguration() can cause MulleObjCBang to abort.
  • Linker ordering issues – Static library link order affects constructor execution. If MulleObjC-startup appears too late in the link command, initialization may not occur.

Debugging Techniques for MulleObjC Startup Issues

Because failures occur before main(), you must use specialized tools to intercept the startup sequence.

Building with Debug Symbols

Always build the startup library and your program with debug symbols to get meaningful stack traces. The Debug configuration adds -g and disables optimization in cmake/share/CompilerFlagsObjC.cmake.

mulle-sde dependency add mulle-objc/MulleObjC-startup --debug
mulle-sde build --configuration Debug

Using mulle-sde debug stacktrace

The Mulle-SDE tool provides a convenient wrapper that runs your program and automatically prints a backtrace on crash, targeting the exact location in src/MulleObjC-startup.m where failure occurs.

mulle-sde debug stacktrace ./my-executable -- [program-args]

For detailed usage, see the built-in guide at .mulle/share/howto/debug.md.

Interactive Debugging with GDB

Attach GDB to step through the startup sequence. Set breakpoints on the registration function and the bang routine to inspect universe configuration before creation.

gdb ./my-executable
(gdb) break __register_mulle_objc_universe
(gdb) run [program-args]

# When breakpoint hits:

(gdb) step
(gdb) break MulleObjCBang
(gdb) continue
(gdb) bt  # Verify stack shows startup sequence

Enabling Verbose Logging

The runtime respects MULLE_OBJC_* environment variables for diagnostic output. Set MULLE_OBJC_LOGLEVEL to debug to see the startup sequence progress in src/MulleObjC-startup.m.

export MULLE_OBJC_LOGLEVEL=debug
./my-executable

# Expected output:

# MULLE_OBJC[debug] creating universe "default"

# MULLE_OBJC[debug] installing atinit/atexit handlers

Validating Dependencies

Verify that mulle-atinit and mulle-atexit are present in your link line. The library's cmake/share/Environment.cmake adds these automatically, but manual linker flags may omit them.


# Check linked libraries

mulle-sde show link-order | grep -E "(atinit|atexit)"

Inspecting the Universe Configuration

If the startup completes but behaves incorrectly, inspect the universe configuration from within a breakpoint or helper function. The bang() function in src/MulleObjC-startup.m copies the global configuration before passing it to MulleObjCBang.

#include <MulleObjC/MulleObjC.h>

void dump_universe_config(void)
{
    struct _mulle_objc_universe *universe = mulle_objc_global_get_universe();
    printf("Universe name: %s\n", universe->name);
    // Additional configuration inspection...
}

Call this from a breakpoint inside bang() to verify the configuration state before universe creation.

Summary

  • MulleObjC startup occurs in src/MulleObjC-startup.m via the __register_mulle_objc_universe constructor, which runs before main().
  • Debug builds with symbols are essential; use mulle-sde build --configuration Debug to enable them.
  • Stack traces from crashes can be obtained via mulle-sde debug stacktrace or by attaching GDB to __register_mulle_objc_universe.
  • Verbose logging via MULLE_OBJC_LOGLEVEL=debug reveals the startup sequence progress.
  • Dependencies mulle-atinit and mulle-atexit must be linked correctly, as configured in cmake/share/Environment.cmake.

Frequently Asked Questions

What is the first function executed in MulleObjC startup?

The first function executed is __register_mulle_objc_universe, defined in src/MulleObjC-startup.m. This function is marked as a constructor and automatically invoked by the dynamic linker before the program's main() function begins execution.

Why does my MulleObjC program crash before main()?

Crashes before main() typically indicate failures in the startup sequence within src/MulleObjC-startup.m. Common causes include missing dependencies (mulle-atinit or mulle-atexit), invalid universe configuration values, or linker ordering issues that prevent __register_mulle_objc_universe from executing properly.

How do I set a breakpoint in MulleObjC startup code?

Use GDB or LLDB to set a breakpoint on the __register_mulle_objc_universe symbol. In GDB, run break __register_mulle_objc_universe before executing run. When the breakpoint hits, you can step into MulleObjCBang to trace the universe creation process.

What dependencies are required for MulleObjC startup?

The startup library requires mulle-atinit and mulle-atexit to function correctly. These dependencies are automatically linked through the CMake configuration in cmake/share/Environment.cmake, but manual build systems must explicitly include them to avoid undefined symbol errors during startup.

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 →