# Static vs Dynamic Linking with MulleObjC-startup: Why Only Static Works

> Discover why MulleObjC-startup mandates static linking for guaranteed Objective-C runtime registration at startup, explicitly forbidding dynamic linking.

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

---

**MulleObjC-startup is intentionally built exclusively as a static library to guarantee Objective-C runtime registration at program startup, and the build system explicitly forbids dynamic linking with a fatal error.**

The `mulle-objc/mulleobjc-startup` repository provides the essential startup machinery for the Mulle Objective-C runtime. Understanding the implications of static versus dynamic linking with MulleObjC-startup is critical because the library enforces a static-only architecture that directly impacts deployment, runtime behavior, and build system configuration.

## Why MulleObjC-startup Enforces Static Linking

### Build System Enforcement

The project’s root [`CMakeLists.txt`](https://github.com/mulle-objc/mulleobjc-startup/blob/main/CMakeLists.txt) contains an explicit guard that prevents any attempt to build shared libraries. If `BUILD_SHARED_LIBS` is enabled, CMake aborts immediately:

```cmake
if( BUILD_SHARED_LIBS)
   message( FATAL_ERROR "Startup library must be built static")
endif()

```

This check appears at lines 26–28 of [`CMakeLists.txt`](https://github.com/mulle-objc/mulleobjc-startup/blob/main/CMakeLists.txt) and makes dynamic linking physically impossible without modifying the source build configuration.

### Runtime Registration Guarantees

Static linking ensures that the `__register_mulle_objc_universe` symbol is compiled directly into the final executable. This symbol, defined in `src/MulleObjC-startup.m`, performs critical runtime initialization before `main()` executes. Dynamic linking would require lazy symbol resolution at runtime, which could delay or fail to trigger the registration routine, leaving the Objective-C universe uninitialized when user code begins executing.

## Static vs Dynamic Linking Comparison

When evaluating static versus dynamic linking with MulleObjC-startup, consider the following technical implications:

| Aspect | Static Linking (Supported) | Dynamic Linking (Unsupported) |
|--------|---------------------------|------------------------------|
| **Symbol Availability** | `__register_mulle_objc_universe` is embedded in the executable, ensuring immediate availability at process start. | Would require export from a shared object and runtime loading, risking uninitialized runtime state. |
| **Runtime Dependencies** | Self-contained executable with no external startup library required at runtime. | Would require shipping `libMulleObjC-startup.so` or `.dylib` and managing `LD_LIBRARY_PATH` or RPATH. |
| **Binary Size** | Larger per-executable footprint because startup code is copied into each binary. | Smaller individual binaries, but requires separate library file on disk. |
| **Link-Time Safety** | All symbols resolved at link time; missing symbols trigger immediate build failures. | Missing symbols could surface only at runtime, complicating debugging. |
| **Coverage Optimization** | Supports `OptimizedLinkObjC.cmake` for generating *all-load* static libraries optimized for coverage collection. | No equivalent support; coverage tools would require separate shared-library loader mechanisms. |
| **Cross-Platform Consistency** | Works on static-only environments like `musl` or `cosmopolitan` libc. | Would require platform-specific handling for symbol exports and loader behavior. |

## Practical Implementation Examples

### Basic CMake Configuration

To correctly include MulleObjC-startup in your project, explicitly declare it as static:

```cmake
add_library(MulleObjC-startup STATIC src/MulleObjC-startup.m)
target_include_directories(MulleObjC-startup PUBLIC include)
target_compile_definitions(MulleObjC-startup PUBLIC
    MULLE_OBJC_DEFINE__register_mulle_objc_universe)

```

Attempting to use `SHARED` instead of `STATIC` triggers the fatal error defined in the library’s [`CMakeLists.txt`](https://github.com/mulle-objc/mulleobjc-startup/blob/main/CMakeLists.txt).

### Linking Executables

When building your final executable, link against the static startup library:

```cmake
add_executable(myprog src/main.c)
target_link_libraries(myprog PRIVATE MulleObjC-startup)

# Include other static Objective-C libraries

target_link_libraries(myprog PRIVATE MulleObjC Foundation)

```

The resulting `myprog` binary contains the `__register_mulle_objc_universe` symbol and requires no external startup library at runtime.

### Coverage-Optimized Static Linking

For coverage collection builds, enable the optimized linking path:

```cmake
set(OBJC_COVERAGE_OPTIMIZED_LIBS ON)

# OptimizedLinkObjC.cmake automatically:

# 1. Creates an "all-load" static library (*_ObjC${CMAKE_STATIC_LIBRARY_SUFFIX})

# 2. Creates an optimizable static lib (*_c${CMAKE_STATIC_LIBRARY_SUFFIX})

# 3. Links both into the final executable

```

This process, implemented in `cmake/share/OptimizedLinkObjC.cmake` at lines 58–66, ensures all Objective-C objects are included for accurate coverage reporting while maintaining static linking benefits.

### Attempting Dynamic Linking (Failure Case)

The following configuration will fail:

```cmake

# This triggers a fatal error

add_library(MulleObjC-startup SHARED src/MulleObjC-startup.m)

```

CMake output:

```

FATAL_ERROR: Startup library must be built static

```

This enforcement ensures developers cannot accidentally create deployments that lack guaranteed runtime initialization.

## Key Source Files and Their Roles

- **[`CMakeLists.txt`](https://github.com/mulle-objc/mulleobjc-startup/blob/main/CMakeLists.txt)** – Enforces the static-only build constraint through the `BUILD_SHARED_LIBS` fatal error check at lines 26–28.
- **`src/MulleObjC-startup.m`** – Implements the startup routine and defines the critical `__register_mulle_objc_universe` symbol that initializes the Objective-C runtime.
- **`cmake/share/OptimizedLinkObjC.cmake`** – Provides coverage-optimized static linking that generates *all-load* static libraries for comprehensive code coverage collection.
- **`cmake/share/Framework.cmake`** – Documents the `-ObjC` linker flag usage (lines 85–87) for forcing the linker to load all static Objective-C objects, essential when combining multiple static libraries.

## Summary

- MulleObjC-startup is **static-only by design**, with CMake enforcing this via a fatal error if `BUILD_SHARED_LIBS` is enabled.
- Static linking embeds `__register_mulle_objc_universe` directly into executables, guaranteeing Objective-C runtime initialization before `main()` executes.
- This approach eliminates runtime dependencies, ensures link-time safety, and supports specialized coverage-optimized builds through `OptimizedLinkObjC.cmake`.
- Dynamic linking would compromise startup reliability and is explicitly unsupported by the build system architecture.

## Frequently Asked Questions

### Can I force MulleObjC-startup to build as a shared library?

No. The build system explicitly forbids this configuration. If you set `BUILD_SHARED_LIBS` or attempt to declare the library as `SHARED` in CMake, the configuration step fails with the fatal error "Startup library must be built static" as defined in [`CMakeLists.txt`](https://github.com/mulle-objc/mulleobjc-startup/blob/main/CMakeLists.txt) lines 26–28.

### Why does static linking matter for Objective-C runtime registration?

Static linking ensures that the `__register_mulle_objc_universe` symbol, implemented in `src/MulleObjC-startup.m`, is resolved and linked directly into the executable at build time. This guarantees the Mulle Objective-C universe is initialized immediately at process startup, before any user code executes. Dynamic linking would defer this resolution to runtime, risking uninitialized runtime state when Objective-C objects are first accessed.

### How does static linking affect binary size and deployment?

Static linking produces larger individual binaries because the startup code is copied into each executable, but it creates self-contained deployments with no external runtime dependencies. You do not need to ship `libMulleObjC-startup.so` or manage `LD_LIBRARY_PATH` and RPATH variables. This is particularly advantageous for embedded systems, minimal containers, or static-only environments like `musl` libc.

### What is the coverage-optimized static linking feature?

The `cmake/share/OptimizedLinkObjC.cmake` script provides a specialized static linking mode for code coverage collection. When `OBJC_COVERAGE_OPTIMIZED_LIBS` is enabled, the build system generates two static libraries: an *all-load* library containing every Objective-C object (ensuring coverage tools see all code paths) and an optimizable library for standard symbols. This advanced static linking strategy ensures comprehensive coverage data while maintaining the benefits of static linking.