AxlMem – Memory Allocation

Memory allocation with dmalloc-inspired debug features. Size-tracking headers enable axl_realloc without passing the old size. Debug builds add fence-post guards, alloc/free fill patterns, file/line tracking, and leak reporting.

Header: <axl/axl-mem.h>

Overview

AXL provides its own allocator on top of UEFI’s pool memory. All allocated memory is tracked with a small header that stores the block size, enabling axl_realloc and debug features without requiring the caller to pass the old size.

Do not mix axl_malloc with UEFI’s FreePool, or AllocatePool with axl_free – they use different headers.

Debug vs. Release

In debug builds (-DAXL_MEM_DEBUG, the default for make):

  • Fill patterns: newly allocated memory is filled with 0xDA; freed memory is filled with 0xDD. Use-after-free often manifests as reads of 0xDD.

  • Fence-post guards: 8 bytes of 0xFD are placed before and after each allocation. axl_mem_check(ptr) verifies these guards.

  • File/line tracking: each allocation records __FILE__ and __LINE__ for leak reports.

  • Leak reporting: axl_mem_dump_leaks() prints all outstanding allocations with their sizes and source locations.

In release builds (make BUILD=RELEASE), these features are disabled and the allocator has minimal overhead.

RAII Auto-Cleanup

AXL provides GLib-style auto-cleanup macros using __attribute__((cleanup)):

// Automatically freed when 's' goes out of scope
AXL_AUTO_FREE char *s = axl_strdup("hello");

// Automatically freed when 'h' goes out of scope
AXL_AUTOPTR(AxlHashTable) h = axl_hash_table_new();

if (error_condition) {
    return -1;  // both 's' and 'h' are freed automatically
}

Important: Always initialize at declaration. Never use with goto that jumps over the declaration. See the GLib g_autoptr documentation for detailed rules.

Quick Reference

#include <axl.h>

// Basic allocation
char *buf = axl_malloc(256);
void *copy = axl_memdup(original, size);
char *str = axl_strdup("hello");

// Typed allocation (zero-initialized)
MyStruct *s = axl_new(MyStruct);
int *arr = axl_new_array(int, 100);

// Free (NULL-safe)
axl_free(buf);

// Debug: check for leaks before exit
axl_mem_dump_leaks();

API Reference

Defines

AXL_OK

operation succeeded

AXL_ERR

operation failed

AXL_AUTO_FREE
AXL_HAVE_AUTOPTR

Define an auto-cleanup function for a named type.

Place after the type’s _free() declaration in its header:

AXL_DEFINE_AUTOPTR_CLEANUP(AxlString, axl_string_free)

Then use AXL_AUTOPTR(AxlString) in variable declarations.

AXL_DEFINE_AUTOPTR_CLEANUP(Type, free_func)
AXL_AUTOPTR(Type)

Declare a pointer that is automatically freed at scope exit.

Requires AXL_DEFINE_AUTOPTR_CLEANUP for the type.

Declares a pointer — do not add an extra *:

AXL_AUTOPTR(AxlString) s = axl_string_new("hello");
axl_string_append(s, " world");
// s is freed automatically at end of scope

IMPORTANT: Always initialize at declaration (use NULL if no initial value). Never use with goto that jumps over the declaration.

AXL_ARRAY_SIZE(a)

Get the number of elements in a static array.

Only works on actual arrays, not pointers. Produces a compile-time constant suitable for use in static initializers.

AXL_SIGNATURE_32(a, b, c, d)

Construct a 32-bit signature from four ASCII characters.

Encodes characters in little-endian order. Commonly used for structure validation signatures in UEFI drivers.

AXL_CONTAINER_OF(ptr, type, member)

Derive a pointer to the enclosing structure from a member pointer.

Given a pointer to a struct member, returns a pointer to the containing structure. Equivalent to Linux kernel’s container_of and EDK2’s CR/BASE_CR macros.

Parameters:
  • ptr – pointer to the member

  • type – type of the containing structure

  • member – name of the member within the structure

Functions

void *axl_malloc(size_t size)

Allocate size bytes of uninitialized memory.

Parameters:
  • size – number of bytes to allocate

Returns:

pointer to allocated memory, or NULL on failure. Free with axl_free().

void *axl_calloc(size_t count, size_t size)

Allocate zero-initialized memory for count elements of size bytes each.

Parameters:
  • count – number of elements

  • size – size of each element in bytes

Returns:

pointer to allocated memory, or NULL on failure. Free with axl_free().

void *axl_realloc(void *ptr, size_t size)

Resize a previously allocated block to size bytes.

Contents are preserved up to the smaller of old and new sizes. If ptr is NULL, behaves like axl_malloc().

Parameters:
  • ptr – pointer from axl_malloc/axl_calloc/axl_realloc, or NULL

  • size – new size in bytes

Returns:

pointer to reallocated memory, or NULL on failure (original block is unchanged).

void axl_free(void *ptr)

Free memory allocated by axl_malloc, axl_calloc, axl_realloc, axl_strdup, or axl_memdup. NULL-safe.

Parameters:
  • ptr – pointer to free, or NULL

char *axl_strdup(const char *s)

Duplicate a NUL-terminated string.

Parameters:
  • s – string to duplicate, or NULL

Returns:

newly allocated copy, or NULL on failure. Free with axl_free().

void *axl_memdup(const void *src, size_t size)

Duplicate size bytes of memory from src.

Parameters:
  • src – source buffer

  • size – number of bytes to copy

Returns:

newly allocated copy, or NULL on failure. Free with axl_free().

void *axl_new(size_t Type)

Allocate and zero-initialize a single instance of a type.

Usage: MyStruct *p = axl_new(MyStruct);

Parameters:
  • Type – the type to allocate (passed as a type name)

Returns:

typed pointer, or NULL on failure. Free with axl_free().

void *axl_new_array(size_t Type, size_t Count)

Allocate and zero-initialize an array of elements.

Usage: int *arr = axl_new_array(int, 100);

Parameters:
  • Type – the type to allocate (passed as a type name)

  • Count – number of elements

Returns:

typed pointer, or NULL on failure. Free with axl_free().

void axl_free_impl(void *ptr)

Auto-free a heap pointer when it goes out of scope.

Works with axl_malloc, axl_strdup, axl_memdup, and any pointer freed by axl_free().

AXL_AUTO_FREE char *s = axl_strdup("hello");
if (error) return -1;  // s is freed automatically

IMPORTANT: Always initialize at declaration. Never use with goto that jumps over the declaration. Cleanup runs at scope exit regardless of whether the variable was assigned.

static inline void _axl_auto_free_func(void *p)
int axl_alloc_pages(size_t count, uint64_t *phys_addr)

Allocate contiguous page-aligned memory.

Allocates count pages (each 4096 bytes) of contiguous physical memory. Suitable for DMA buffers, RAM disks, and other uses requiring physical address alignment.

Parameters:
  • count – number of 4KB pages to allocate

  • phys_addr – [out] receives physical address

Returns:

0 on success, -1 on error.

void axl_free_pages(uint64_t phys_addr, size_t count)

Free page-aligned memory allocated by axl_alloc_pages.

Parameters:
  • phys_addr – physical address from axl_alloc_pages

  • count – number of pages (must match alloc)

void axl_mem_get_stats(AxlMemStats *stats)

Get current allocation statistics.

Parameters:
  • stats – [out] receives statistics

void axl_mem_dump_leaks(void)

Print all outstanding allocations to the log (debug builds).

Each leaked block is reported with its size, file, and line number. No-op in release builds.

bool axl_mem_check(const void *ptr)

Validate a heap pointer’s fence-post guards (debug builds).

Parameters:
  • ptr – pointer to validate

Returns:

true if valid, false if corrupted or not an axl_malloc’d pointer. Always returns true in release builds.

struct AxlMemStats

Public Members

size_t count
size_t bytes
size_t total_count
size_t total_bytes