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_runtimeversionto 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 viamulle_objc_global_register_universebefore processing any classes. -
loadclasslist– Points to an array ofstruct _mulle_objc_loadclassentries. 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 toloadclasslistbut describes categories that extend existing classes, including their method and property additions. -
loadsuperlist– Holds inheritance linkage information required for fastsupermessage dispatch, populated viamulle_objc_loadsuperlist_enqueue_nofail. -
loadstringlist– References pre-created constantNSStringobjects needed during class initialization, loaded without holding locks viamulle_objc_loadstringlist_enqueue_nofail. -
loadhashedstringlist– Optional debug structure storing hashed strings for quick method name lookup. Controlled by theunsortedbit 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:
-
Universe selection – Determines the target universe (default or specified by
loaduniverse) and registers it globally. -
Version validation – Compares the three version fields against the current runtime, aborting with a descriptive error on mismatch.
-
Callback veto check – Optionally invokes
should_load_loadinfoto allow embedding applications to skip specific load info objects. -
Load constant strings – Processes
loadstringlistwithout acquiring locks, installingNSStringconstants needed by subsequent steps. -
Load hashed strings – If present, optionally sorts them (unless the
unsortedbit is set) and registers them for fast lookup. -
Load super-class information – Populates the fast-super dispatch table via
mulle_objc_loadsuperlist_enqueue_nofail. -
Acquire wait-queue lock – Calls
_mulle_objc_universe_lock_waitqueuesto serialize modifications to class and category tables. -
Register classes –
mulle_objc_loadclasslist_enqueue_nofailallocates class and metaclass structures, installs ivars, method lists, properties, and protocols. -
Register categories –
mulle_objc_loadcategorylist_enqueue_nofailmerges category methods and properties into target classes. -
Execute +load methods – Builds a temporary
struct _mulle_objc_callqueue, walks it viamulle_objc_callqueue_walk, and invokes each class or category+loadmethod to maintain Objective-C load-time semantics. -
Release lock – Calls
_mulle_objc_universe_unlock_waitqueuesto 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_loadinfostructure insrc/mulle-objc-load.hserves 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 viamulle_atinituntil 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.
+loadmethods 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.cfor the loading logic andsrc/mulle-objc-universe.h/cfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →