Cross Platform Implications
Cross platform implications of the investigation results for dynamic linking.
This post documents the current state of the malloc project, which intentionally differs from the fully interposed, cross-platform allocator described in the dynamic linking deep dives. This is a part of dynamic linking investigation series.
At present, the project prioritizes determinism, testability, and approachability by abstracting system calls behind a controllable interface rather than globally interposing on the platform allocator.
The investigation pages describe what is ultimately required to own allocation across an entire process (ELF interposition on Linux, dyld interposition on macOS).
However, that model:
To keep the allocator playable, hackable, and testable, the current mainline implementation deliberately avoids full interposition.
Instead of calling system interfaces directly, the allocator routes all OS interactions through project-defined wrappers.
Typical examples include:
mm_sbrk() instead of sbrkmm_mmap() / mm_munmap() instead of mmap / munmapmm_mremap()- instead of mremapThese wrappers serve two purposes:
When compiled in test mode:
This allows tests to:
Crucially, this avoids needing global allocator interposition just to test allocator logic.
In malloc.c, we define CALLOC/FREE/MALLOC/REALLOC macros. All calls inside this file after preprocessing become either:
mm_ appended versions of those symbols to separate from and not overwrite original symbols while testing,calloc/free/malloc/realloc when compiling into a library.In this way, during testing, any other executable other than our tests call the original allocator procedures, while tests call our implementations using mock syscalls to isolate the behavior under testing.
#ifdef TESTING
#define CALLOC mm_calloc
#define FREE mm_free
#define MALLOC mm_malloc
#define REALLOC mm_realloc
#else
#define CALLOC calloc
#define FREE free
#define MALLOC malloc
#define REALLOC realloc
#endif
The dynamic linking and interposition work answers the question:
How do we ensure that all allocations in a real process flow through our allocator?
The syscall abstraction answers a different question:
How do we develop and test allocator logic in isolation?
These approaches are complementary, not competing:
To make this distinction explicit:
The interposition branch exists so readers can:
Meanwhile, the mainline remains stable, debuggable, and suitable for iteration.
The project intentionally avoids premature global interposition.
Allocator correctness, invariants, and testability come first. Once those are solid, interposition becomes an integration concern rather than a development hazard.
Both paths are documented so the reader can choose between learning, experimenting, or integrating the allocator depending on their goals.