Understanding the Mulle-ObjC Load Info Structure: How Code Is Loaded into the Runtime

The mulle_objc_loadinfo structure is a compiler-populated metadata container defined in src/mulle-objc-load.h that describes classes, categories, strings, and version compatibility, which the runtime processes through mulle_objc_loadinfo_enqueue_nofail in src/mulle-objc-load.c to safely register code into a universe.

The mulle-objc/mulle-objc-runtime repository implements a unique loading mechanism where compiled object files carry self-describing metadata rather than relying on external registration functions. This load info structure serves as the single source of truth for everything the runtime must add to a universe when a .o file is linked.

What Is the Load Info Structure?

The struct _mulle_objc_loadinfo acts as a manifest generated by the compiler. It lives in src/mulle-objc-load.h (lines 83–95) and aggregates pointers to tables describing classes, categories, inheritance links, and constant strings.

Core Structure Definition

struct _mulle_objc_loadinfo
{
    struct mulle_objc_loadversion   version;
    struct _mulle_objc_loaduniverse *loaduniverse;
    struct _mulle_objc_loadclasslist    *loadclasslist;
    struct _mulle_objc_loadcategorylist *loadcategorylist;
    struct _mulle_objc_superlist        *loadsuperlist;
    struct _mulle_objc_loadstringlist   *loadstringlist;
    struct _mulle_objc_loadhashedstringlist *loadhashedstringlist;
    char *origin;
};

Field-by-Field Breakdown

  • version – Contains three version numbers (load, runtime, foundation) plus a bitmask. The runtime checks these via _mulle_objc_universe_assert_runtimeversion to guarantee binary compatibility between the compiled code and the executing universe.

  • loaduniverse – If non-NULL, explicitly requests a specific universe ID. The runtime registers this universe globally via mulle_objc_global_register_universe before processing any classes.

  • loadclasslist – Points to an array of struct _mulle_objc_loadclass entries. Each entry carries a class identifier, name, superclass reference, ivar layout, method lists, property list, protocol list, and the originating object file name.

  • loadcategorylist – Analogous to loadclasslist but describes categories that extend existing classes, including their method and property additions.

  • loadsuperlist – Holds inheritance linkage information required for fast super message dispatch, populated via mulle_objc_loadsuperlist_enqueue_nofail.

  • loadstringlist – References pre-created constant NSString objects needed during class initialization, loaded without holding locks via mulle_objc_loadstringlist_enqueue_nofail.

  • loadhashedstringlist – Optional debug structure storing hashed strings for quick method name lookup. Controlled by the unsorted bit in the version field.

  • origin – Human-readable source file path indicating where the load info originated, useful for debugging and trace output.

How Code Is Loaded into the Runtime

The loading process centers on the mulle_objc_loadinfo_enqueue_nofail function declared in src/mulle-objc-load.h and implemented in src/mulle-objc-load.c. The compiler emits calls to this function for each object file, deferring execution via mulle_atinit to run after static initialization but before user code executes.

The Entry Point and Deferred Execution

When an object file is linked, the compiler generates a stub that registers the load info for later processing:

mulle_atinit(_mulle_objc_loadinfo_enqueue_nofail, info, 0, comment);

This defers execution until the runtime environment is ready, ensuring static initializers complete first. The public wrapper mulle_objc_loadinfo_enqueue_nofail forwards to the internal implementation _mulle_objc_loadinfo_enqueue_nofail after printing optional trace messages (lines 57–74 in src/mulle-objc-load.c).

The Loading Pipeline Step-by-Step

The implementation in src/mulle-objc-load.c (lines 49–141) executes the following sequence:

  1. Universe selection – Determines the target universe (default or specified by loaduniverse) and registers it globally.

  2. Version validation – Compares the three version fields against the current runtime, aborting with a descriptive error on mismatch.

  3. Callback veto check – Optionally invokes should_load_loadinfo to allow embedding applications to skip specific load info objects.

  4. Load constant strings – Processes loadstringlist without acquiring locks, installing NSString constants needed by subsequent steps.

  5. Load hashed strings – If present, optionally sorts them (unless the unsorted bit is set) and registers them for fast lookup.

  6. Load super-class information – Populates the fast-super dispatch table via mulle_objc_loadsuperlist_enqueue_nofail.

  7. Acquire wait-queue lock – Calls _mulle_objc_universe_lock_waitqueues to serialize modifications to class and category tables.

  8. Register classes – mulle_objc_loadclasslist_enqueue_nofail allocates class and metaclass structures, installs ivars, method lists, properties, and protocols.

  9. Register categories – mulle_objc_loadcategorylist_enqueue_nofail merges category methods and properties into target classes.

  10. Execute +load methods – Builds a temporary struct _mulle_objc_callqueue, walks it via mulle_objc_callqueue_walk, and invokes each class or category +load method to maintain Objective-C load-time semantics.

  11. Release lock – Calls _mulle_objc_universe_unlock_waitqueues to finalize the load and allow normal execution.

Universe Selection and Version Validation

Before mutating any runtime structures, the code validates compatibility. The loaduniverse field allows compiled code to target specific universe instances, while the version field prevents loading code compiled against incompatible runtime or foundation versions. This ensures that binary metadata matches the executing environment's ABI.

Registering Classes and Categories

The loadclasslist and loadcategorylist fields drive the core metadata registration. Each class entry triggers allocation of struct _mulle_objc_class pairs (class and metaclass), installation of instance variable layouts, and registration of method dispatch tables. Categories are attached to existing classes, with their methods inserted into the class's method lists according to Objective-C precedence rules.

Executing +load Methods

After all metadata is registered, the runtime constructs a temporary call queue to handle +load invocations. This queue ensures that class loads execute before category loads, and that dependencies respect the loading order. The mulle_objc_callqueue infrastructure (defined in src/mulle-objc-callqueue.h) manages this ordered execution before the final unlock releases the universe for normal message passing.

Summary

  • The mulle_objc_loadinfo structure in src/mulle-objc-load.h serves as a compiler-generated manifest describing all runtime metadata in a compiled object file.
  • Loading occurs through mulle_objc_loadinfo_enqueue_nofail, which defers execution via mulle_atinit until after static initialization.
  • The pipeline validates version compatibility, selects or creates the target universe, loads constant strings and super-class tables, then locks wait-queues to safely register classes and categories.
  • +load methods execute from a temporary call queue after all metadata registration completes but before the wait-queue lock is released.
  • Key implementation files include src/mulle-objc-load.c for the loading logic and src/mulle-objc-universe.h/c for universe management and version checking.

Frequently Asked Questions

What happens if the version numbers in the load info structure do not match the runtime?

The runtime calls _mulle_objc_universe_assert_runtimeversion to compare the load, runtime, and foundation version fields. If any value is incompatible, the function aborts execution with a descriptive error message, preventing crashes from ABI mismatches between compiled code and the running universe.

Can an application prevent specific load info structures from being processed?

Yes. The runtime checks for an optional should_load_loadinfo callback before proceeding with loading. If this callback returns false, the runtime skips the entire load info object, allowing embedding applications to veto loading of specific compiled object files or libraries.

Why are constant strings loaded before acquiring the wait-queue lock?

The loadstringlist is processed via mulle_objc_loadstringlist_enqueue_nofail before locking because constant NSString objects are immutable global data that do not mutate shared runtime structures. Loading them early ensures they are available for class and category registration while avoiding unnecessary lock contention during the string installation phase.

How does the runtime handle the +load method execution order?

The runtime builds a temporary struct _mulle_objc_callqueue after registering all classes and categories. It walks this queue via mulle_objc_callqueue_walk (defined in src/mulle-objc-callqueue.c), invoking each +load method in the correct order: class loads execute before category loads, respecting dependencies. This occurs while the wait-queue lock is still held, ensuring no other threads interact with partially initialized classes.

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 →