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-atinitandmulle-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 causeMulleObjCBangto abort. - Linker ordering issues – Static library link order affects constructor execution. If
MulleObjC-startupappears 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.mvia the__register_mulle_objc_universeconstructor, which runs beforemain(). - Debug builds with symbols are essential; use
mulle-sde build --configuration Debugto enable them. - Stack traces from crashes can be obtained via
mulle-sde debug stacktraceor by attaching GDB to__register_mulle_objc_universe. - Verbose logging via
MULLE_OBJC_LOGLEVEL=debugreveals the startup sequence progress. - Dependencies
mulle-atinitandmulle-atexitmust be linked correctly, as configured incmake/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →