Expand description
C ABI allocator compatibility — xmlMemSetup, xmlMemGet, xmlMalloc, xmlFree, etc. (§58).
This module implements the complete memory hook system exposed by libxml2:
- Global allocator function pointers (malloc, realloc, free, strdup)
xmlMemSetup()/xmlMemGet()— set/get allocator hooksxmlGcMemSetup()/xmlGcMemGet()— GC-aware allocator hooks (wrappers)xmlMalloc()/xmlMallocAtomic()/xmlRealloc()/xmlFree()/xmlMemStrdup()xmlMemUsed()/xmlMemBlocks()— allocation trackingxmlMemDisplay()/xmlMemShow()— debugging output
§Phase 1 status
Complete — all allocator APIs are implemented; state lives in the five
exported function-pointer variables exactly as upstream (globals.c),
giving xmlMemSetup and direct xmlMalloc = custom assignment one
shared override mechanism (R-000176). Since 11.1-Z.3 (R-000178) the
default bodies are plain libc malloc/realloc/free/strdup
wrappers — no Rust std::alloc layout fabrication (that was UB) and no
accounting, byte-identical with upstream’s globals.c defaults
(xmlMalloc = malloc etc.).
§Safety
Allocator hooks are unsafe because they operate on raw pointers and are called
from C code. Every public function documents its safety contract.
§Upstream contract
The parity target is libxml2 2.15.3 (SRC-LIBXML2-2.15.0-XMLMEMORY-C:
oracle/historical/src/libxml2-2.15.0/xmlmemory.c) plus the allocator globals
of globals.c. The 5 allocator entry points (xmlMalloc, xmlMallocAtomic,
xmlRealloc, xmlFree, xmlMemStrdup) are exported as DATA function-pointer
globals matching the upstream XMLPUBVAR declarations (R-000162). Upstream
initializes them to the C runtime functions (xmlFree = free, xmlMalloc = malloc, xmlMallocAtomic = malloc, xmlRealloc = realloc, xmlMemStrdup = xmlPosixStrdup); the candidate initializes them to the *Default bodies,
which are libc wrappers with identical observable behavior.
§Conceptual behavior
This module implements the complete libxml2 memory-hook system: swappable
allocator hooks via xmlMemSetup/xmlMemGet (and the GC aliases), plus the
deprecated debug-named surface (xmlMemMalloc/xmlMemFree/xmlMemRealloc/
xmlMemoryStrdup and the *Loc variants) which upstream keeps as a
separately-tagged debug allocator. There are therefore two allocation
planes, exactly as in upstream 2.15.0:
- the five exported variables (the hook system): default = libc, and
xmlMemSetup/direct assignment re-route them. Untracked — upstream’sdebugMemSize/debugMemBlockscounters are only maintained by the debug allocator, so with the default installedxmlMemUsed()== 0,xmlMemBlocks()== 0 andxmlMemSize()== 0 (verified against the oracle); - the debug-named surface (deprecated, exported for legacy consumers):
always libc-backed and tracked by the per-block registry, mirroring
upstream
xmlMemMallocet al.xmlMemSizereturns the recorded size for these blocks andxmlMemUsed/xmlMemBlockscount them.
The display entry points (xmlMemDisplay, xmlMemDisplayLast, xmlMemShow,
xmlMemoryDump) are no-ops matching upstream 2.15.0, which removed that
feature.
§Ownership & safety invariants
Every pointer returned by an xml* allocator must be freed with xmlFree
(OWNERSHIP_ATLAS section 1). The block registry records
ptr -> (size, file, line) for the debug-named surface only, so
xmlMemSize is exact for debug-surface blocks and 0 for default-allocator
blocks (upstream’s MEMHDR tag lookup behaves identically: a plain malloc
block carries no tag). xmlMemSetup custom allocators bypass the registry
entirely, matching upstream’s debug-allocator-only contract.
§Historical quirks & epochs
R-000178 (11.1-Z.3): the pre-Z.3 default allocator routed through Rust’s
global allocator with fabricated Layouts — default_free deallocated
every pointer with a 1-byte layout and default_realloc passed the
requested new size as the old allocation layout; both are invalid-layout
UB under the Rust allocator contract. Replaced with libc
malloc/realloc/free (C allocation semantics; no layout exists), and
the default no longer maintains the accounting registry so xmlMemUsed/
xmlMemBlocks/xmlMemSize match the oracle’s 0s; the registry now backs
only the debug-named surface. R-000131 (11.1-J) sealed: xmlMemSize
returns the recorded size for debug-surface blocks, the *Loc variants
accept-and-ignore file/line exactly like upstream 2.15.0’s ATTRIBUTE_UNUSED
parameters, and the display functions are upstream-faithful no-ops.
R-000133 (11.1-H): the legacy names
(xmlMemMalloc/xmlMemFree/xmlMemRealloc/xmlMemoryStrdup) were
declared-but-unexported and had to be implemented for the honest-header
rule.
§Deliberate oddities
xmlMemSetup/direct variable assignment bypass the accounting registry
(deliberate: upstream’s block table exists only in the debug allocator).
The five exported variables (xmlMalloc, xmlMallocAtomic, xmlRealloc,
xmlFree, xmlMemStrdup) are the single source of truth (R-000176,
11.1-Z.2): xmlMemSetup assigns them and every internal allocation reads
them through the *Impl indirection, exactly like upstream internal
xmlMalloc(...) calls. The debug-named functions deliberately do NOT
route through the variables (upstream’s debug allocator is independent of
the hooks); they are always libc-backed + registry-tracked.
§Proving courts
ABI-DATA, ALLOCATOR, ALLOCATOR-DEFAULT, GLOBAL-STATE and THREADING court
families; the allocator probes (tools/abi/*_probe.py +
courts/suites/data-abi/*) compile the same C probe against the oracle DSO
and the candidate and require byte-identical output; ALLOCATOR-DEFAULT-001
proves the default-allocator contract (many sizes, zero-size, grow/shrink
realloc, realloc-to-zero, realloc/malloc failure, strdup, direct
exported-variable calls, long churn, xmlMemSize/xmlMemUsed/xmlMemBlocks
exactness — all byte-identical with the oracle, R-000178); the DSO-LOADER
court resolves every exported symbol from the built DSO.
§Tempting simplifications that would break parity
A tempting simplification is to keep the pre-Z.3 default allocator — Rust
std::alloc with fabricated layouts is invalid-layout UB (R-000178) and
returning nonzero xmlMemUsed/xmlMemBlocks under the default diverges
from the oracle’s 0s. Another tempting shortcut is exporting the allocator
entry points as plain functions — upstream exports them as data function
pointers, so the allocator-override mechanism (xmlMalloc = custom) could
not link (R-000162 lesson).
Statics§
- xmlFree
xmlFreeFunc xmlFree— the free hook.- xmlMalloc
xmlMallocFunc xmlMalloc— the malloc hook (default:xmlMallocDefault).- xmlMalloc
Atomic xmlMallocFunc xmlMallocAtomic— the atomic-malloc hook.- xmlMem
Strdup xmlStrdupFunc xmlMemStrdup— the strdup hook.- xmlRealloc
xmlReallocFunc xmlRealloc— the realloc hook.
Functions§
- __
xmlFree ⚠ - Upstream
xmlFreeFunc *__xmlFree(void). - __
xmlMalloc ⚠ - Upstream
xmlMallocFunc *__xmlMalloc(void). - __
xmlMalloc ⚠Atomic - Upstream
xmlMallocFunc *__xmlMallocAtomic(void). - __
xmlMem ⚠Strdup - Upstream
xmlStrdupFunc *__xmlMemStrdup(void). - __
xmlRealloc ⚠ - Upstream
xmlReallocFunc *__xmlRealloc(void). - xmlCleanup
Memory - Clean up the memory layer.
- xmlFree
Impl ⚠ - Free through the exported
xmlFreevariable. - xmlGc
MemGet ⚠ - Get GC-aware memory allocator functions (upstream xmlmemory.h).
- xmlGc
MemSetup ⚠ - Set GC-aware memory allocator functions (upstream xmlmemory.h).
- xmlInit
Memory - Initialize the memory layer with debugging support.
- xmlMalloc
Atomic ⚠Impl - Allocate through the exported
xmlMallocAtomicvariable. - xmlMalloc
Atomic ⚠Loc - Allocate memory, recording the allocation site (upstream xmlmemory.h).
- xmlMalloc
Atomic ⚠Zero - Allocate zero-initialized memory (atomic variant).
- xmlMalloc
Impl ⚠ - Allocate memory through the exported
xmlMallocvariable. - xmlMalloc
Loc ⚠ - Allocate memory, recording the allocation site (upstream xmlmemory.h).
- xmlMalloc
Zero ⚠ - Allocate zero-initialized memory.
- xmlMem
Blocks - Return the current number of allocated blocks (approximate).
- xmlMem
Display ⚠ - Display memory allocation information to a file.
- xmlMem
Display ⚠Last - Display a limited amount of memory debug information (upstream xmlmemory.h).
- xmlMem
Free ⚠ - Free memory through the debug allocator (upstream xmlmemory.h).
- xmlMem
Get ⚠ - Get the current memory allocator functions (upstream xmlmemory.h).
- xmlMem
Malloc ⚠ - Allocate memory through the debug allocator (upstream xmlmemory.h).
- xmlMem
Realloc ⚠ - Reallocate memory through the debug allocator (upstream xmlmemory.h).
- xmlMem
Setup ⚠ - Set custom memory allocator functions (upstream xmlmemory.h).
- xmlMem
Show ⚠ - Show memory allocation information.
- xmlMem
Size ⚠ - Return the size of an allocated block (upstream xmlmemory.h).
- xmlMem
Strdup ⚠Impl - Duplicate a C string through the exported
xmlMemStrdupvariable. - xmlMem
Strdup ⚠Loc - Duplicate a string, recording the allocation site (upstream xmlmemory.h).
- xmlMem
Used - Return the total amount of memory currently allocated (approximate).
- xmlMemory
Dump ⚠ - Dump memory allocation statistics (upstream xmlmemory.h).
- xmlMemory
Strdup ⚠ - Duplicate a string through the debug allocator (upstream xmlmemory.h).
- xmlRealloc
Impl ⚠ - Reallocate through the exported
xmlReallocvariable. - xmlRealloc
Loc ⚠ - Reallocate memory, recording the allocation site (upstream xmlmemory.h).
- xmlRealloc
Zero ⚠ - Reallocate and zero-initialize the new portion.