Cross Platform Implications

Cross Platform Implications

in

This page ties together the investigation results and explains the design conclusions for the malloc project. This is a part of dynamic linking investigation series.

The core takeaway is simple but strict:

A process must use exactly one allocator implementation.

Everything else follows from this constraint.

What We Observed

Through return-address tracing, debugger inspection, and dynamic linking analysis, we established that:

  • On Linux / ELF, shared libraries resolve malloc to a strong symbol defined in the executable.
  • On macOS / Mach-O, shared libraries keep using their own allocator unless explicitly interposed.

As a result:

  • Linux routed allocator calls from libc, libstdc++, sanitizers, and the test framework into our allocator.
  • macOS did not.

Why Multiple Allocators Are Not Acceptable

Allowing multiple allocator implementations to coexist in a single process is undefined behavior.

Each allocator:

  • maintains its own metadata layout
  • assumes ownership of heap regions
  • applies its own invariants when allocating and freeing

When two allocators operate on overlapping memory regions:

  • metadata corruption becomes inevitable
  • free lists diverge
  • double frees and invalid frees become possible
  • failures are non-deterministic and often delayed

From a correctness standpoint, this situation cannot be reasoned about safely.

Why the Linux Model Is the Reference

The allocator project deliberately targets the Linux / ELF behavior as the reference model because:

  • symbol interposition is implicit and well-defined
  • one strong allocator symbol governs the entire process
  • behavior is observable, debuggable, and deterministic
  • this matches how many real-world allocator replacements operate

This makes Linux a natural baseline for allocator experimentation.

Making macOS Behave Like Linux

To achieve equivalent behavior on macOS, the project adopts explicit dyld interposition.

By injecting an interposing dylib:

  • all allocator calls are forced through a single implementation
  • system libraries no longer bypass the project allocator
  • allocator invariants hold globally

This aligns macOS runtime behavior with the Linux model without relying on deprecated mechanisms such as flat namespaces.

Implications for Testing

This decision has direct consequences for tests:

  • allocator counters observe real allocator activity from the entire process
  • tests must use relative assertions rather than absolute counts
  • runtime startup and logging allocations must be accounted for

The investigation tooling (callsite tracing, counters, containers) exists precisely to make these effects visible and controllable.

Design Constraints Going Forward

As a result of this analysis, the project enforces the following rules:

  • exactly one allocator per process
  • no mixing of system and custom allocators
  • platform-specific mechanisms are acceptable when they preserve global invariants
  • determinism and debuggability take precedence over portability shortcuts

These constraints guide allocator design, testing strategy, and platform support decisions.

Summary

The differences between ELF and Mach-O are not merely academic — they directly affect allocator correctness.

By understanding and controlling dynamic linking behavior:

  • allocator behavior becomes predictable
  • tests become meaningful
  • platform differences become explicit rather than surprising

With these rules in place, the malloc project can reason about memory management in a principled, cross-platform way.

Finally the next page, Current Implementation & Syscall Abstraction, explains what decisions are made and is implemented for the test suite of the malloc project.