infix
A JIT-Powered FFI Library for C
Loading...
Searching...
No Matches
infix_internals.h File Reference

Internal data structures, function prototypes, and constants. More...

#include "common/infix_config.h"
#include "common/platform.h"
#include <infix/infix.h>
Include dependency graph for infix_internals.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  infix_executable_t
 Internal representation of an executable memory block for JIT code. More...
 
struct  infix_protected_t
 Internal representation of a memory block that will be made read-only. More...
 
struct  infix_forward_t
 Internal definition of a forward trampoline handle. More...
 
struct  infix_reverse_t
 Internal definition of a reverse trampoline (callback/closure) handle. More...
 
struct  infix_arena_t
 Internal definition of a memory arena. More...
 
struct  _infix_registry_entry_t
 A single entry in the registry's hash table. More...
 
struct  infix_registry_t
 Internal definition of a named type registry. More...
 
struct  infix_registry_iterator_t
 Internal definition of a registry iterator. More...
 
struct  code_buffer
 A dynamic buffer for staged machine code generation. More...
 
struct  infix_library_t
 Internal definition of a dynamic library handle. More...
 
struct  infix_arg_location
 Detailed location information for a single function argument. More...
 
struct  infix_call_frame_layout
 A complete layout blueprint for a forward call frame. More...
 
struct  infix_reverse_call_frame_layout
 A complete layout blueprint for a reverse call frame. More...
 
struct  infix_forward_abi_spec
 Defines the ABI-specific implementation interface for forward trampolines. More...
 
struct  infix_reverse_abi_spec
 Defines the ABI-specific implementation interface for reverse trampolines. More...
 

Macros

#define INFIX_MAX_STACK_ALLOC   (1024 * 1024 * 4)
 A safety limit (4MB) for the total stack space a trampoline can allocate. This prevents stack exhaustion from malformed or malicious type layouts.
 
#define INFIX_MAX_ARG_SIZE   (1024 * 64)
 A safety limit (64KB) for the size of a single argument.
 
#define EMIT_BYTES(buf, ...)
 Appends a sequence of bytes (e.g., an instruction opcode) to a code buffer.
 

Typedefs

typedef void(* infix_internal_dispatch_callback_fn) (infix_reverse_t *, void *, void **)
 A function pointer to the universal C dispatcher for reverse calls.
 
typedef struct _infix_registry_entry_t _infix_registry_entry_t
 

Enumerations

enum  infix_arg_location_type {
  ARG_LOCATION_GPR , ARG_LOCATION_XMM , ARG_LOCATION_GPR_PAIR , ARG_LOCATION_SSE_SSE_PAIR ,
  ARG_LOCATION_INTEGER_SSE_PAIR , ARG_LOCATION_SSE_INTEGER_PAIR , ARG_LOCATION_STACK
}
 Describes the physical location where a function argument is passed according to the ABI. More...
 

Functions

void _infix_set_error (infix_error_category_t category, infix_error_code_t code, size_t position)
 Sets the thread-local error state with detailed information.
 
void _infix_set_system_error (infix_error_category_t category, infix_error_code_t code, long system_code, const char *msg)
 Sets the thread-local error state for a system-level error.
 
void _infix_clear_error (void)
 Clears the thread-local error state.
 
void _infix_type_recalculate_layout (infix_type *type)
 Recalculates the layout of a fully resolved type graph.
 
c23_nodiscard infix_status _infix_resolve_type_graph_inplace (infix_type **type_ptr, infix_registry_t *registry)
 Resolves all named type references in a type graph in-place.
 
c23_nodiscard infix_status _infix_parse_type_internal (infix_type **, infix_arena_t **, const char *)
 The internal core of the signature parser.
 
c23_nodiscard infix_status _infix_type_print_body_only (char *, size_t, const infix_type *, infix_print_dialect_t)
 An internal-only function to serialize a type's body without its registered name.
 
infix_type_copy_type_graph_to_arena (infix_arena_t *, const infix_type *)
 Performs a deep copy of a type graph into a destination arena.
 
size_t _infix_estimate_graph_size (infix_arena_t *temp_arena, const infix_type *type)
 Estimates the total memory required to deep-copy a complete type graph.
 
const infix_forward_abi_specget_current_forward_abi_spec (void)
 Gets the ABI v-table for forward calls for the current platform.
 
const infix_reverse_abi_specget_current_reverse_abi_spec (void)
 Gets the ABI v-table for reverse calls for the current platform.
 
void code_buffer_init (code_buffer *buf, infix_arena_t *arena)
 Initializes a code buffer for JIT code generation.
 
void code_buffer_append (code_buffer *buf, const void *data, size_t len)
 Appends raw bytes to a code buffer, reallocating within its arena if necessary.
 
void emit_byte (code_buffer *buf, uint8_t byte)
 A convenience wrapper to append a single byte to a code buffer.
 
void emit_int32 (code_buffer *buf, int32_t value)
 A convenience wrapper to append a 32-bit integer (little-endian) to a code buffer.
 
void emit_int64 (code_buffer *buf, int64_t value)
 A convenience wrapper to append a 64-bit integer (little-endian) to a code buffer.
 
c23_nodiscard infix_status _infix_forward_create_internal (infix_forward_t **, infix_type *, infix_type **, size_t, size_t, void *)
 The core implementation for creating all forward trampolines.
 
c23_nodiscard infix_executable_t infix_executable_alloc (size_t size)
 Allocates a block of executable memory using the platform's W^X strategy.
 
void infix_executable_free (infix_executable_t exec)
 Frees a block of executable memory and applies guard pages to prevent use-after-free.
 
c23_nodiscard bool infix_executable_make_executable (infix_executable_t exec)
 Makes a block of JIT memory executable, completing the W^X process.
 
c23_nodiscard infix_protected_t infix_protected_alloc (size_t size)
 Allocates a block of standard memory for later protection.
 
void infix_protected_free (infix_protected_t prot)
 Frees a block of protected memory.
 
c23_nodiscard bool infix_protected_make_readonly (infix_protected_t prot)
 Makes a block of memory read-only for security hardening.
 
void infix_internal_dispatch_callback_fn_impl (infix_reverse_t *context, void *return_value_ptr, void **args_array)
 The universal C entry point for all reverse call trampolines.
 
static size_t _infix_align_up (size_t value, size_t alignment)
 Aligns a value up to the next multiple of a power-of-two alignment.
 
static bool is_float (const infix_type *type)
 A fast inline check to determine if an infix_type is a float.
 
static bool is_double (const infix_type *type)
 A fast inline check to determine if an infix_type is a double.
 
static bool is_long_double (const infix_type *type)
 A fast inline check to determine if an infix_type is a long double.
 

Detailed Description

Internal data structures, function prototypes, and constants.

Copyright (c) 2025 Sanko Robinson

This source code is dual-licensed under the Artistic License 2.0 or the MIT License. You may choose to use this code under the terms of either license.

SPDX-License-Identifier: (Artistic-2.0 OR MIT)

The documentation blocks within this file are licensed under the Creative Commons Attribution 4.0 International License (CC BY 4.0).

SPDX-License-Identifier: CC-BY-4.0

This is the primary internal header for the infix library. It defines the complete layout of all opaque public structs (like infix_forward_t) and declares internal-only functions (_infix_*) that are shared between modules.

Its most important role is to define the core ABI abstraction layer through v-tables (infix_forward_abi_spec, infix_reverse_abi_spec). These structures form the contract between the platform-agnostic JIT engine (trampoline.c) and the platform-specific ABI implementations (arch/...), making them key to the library's portability and architectural design.

This header also brings together all other internal type definitions, creating a single source of truth for the library's internal data model.

Macro Definition Documentation

◆ EMIT_BYTES

#define EMIT_BYTES (   buf,
  ... 
)
Value:
do { \
const uint8_t bytes[] = {__VA_ARGS__}; \
code_buffer_append((buf), bytes, sizeof(bytes)); \
} while (0)

Appends a sequence of bytes (e.g., an instruction opcode) to a code buffer.

◆ INFIX_MAX_ARG_SIZE

#define INFIX_MAX_ARG_SIZE   (1024 * 64)

A safety limit (64KB) for the size of a single argument.

◆ INFIX_MAX_STACK_ALLOC

#define INFIX_MAX_STACK_ALLOC   (1024 * 1024 * 4)

A safety limit (4MB) for the total stack space a trampoline can allocate. This prevents stack exhaustion from malformed or malicious type layouts.

Typedef Documentation

◆ _infix_registry_entry_t

◆ infix_internal_dispatch_callback_fn

typedef void(* infix_internal_dispatch_callback_fn) (infix_reverse_t *, void *, void **)

A function pointer to the universal C dispatcher for reverse calls.

This is the C function that the JIT-compiled reverse trampoline stub calls after marshalling all arguments into a standard C format.

Enumeration Type Documentation

◆ infix_arg_location_type

Describes the physical location where a function argument is passed according to the ABI.

This enumeration abstracts away the differences in how various ABIs use registers and the stack to pass data. It is the primary output of the ABI classification process.

Enumerator
ARG_LOCATION_GPR 

Argument is passed in a general-purpose integer register (e.g., RCX, RDI, X0).

ARG_LOCATION_XMM 

(x64) Argument is passed in an SSE/XMM register (e.g., XMM0).

ARG_LOCATION_GPR_PAIR 

(SysV x64) A struct passed in two GPRs (e.g., RDI, RSI).

ARG_LOCATION_SSE_SSE_PAIR 

(SysV x64) A struct passed in two SSE registers (e.g., XMM0, XMM1).

ARG_LOCATION_INTEGER_SSE_PAIR 

(SysV x64) A struct split between a GPR and an SSE register.

ARG_LOCATION_SSE_INTEGER_PAIR 

(SysV x64) A struct split between an SSE and a GPR register.

ARG_LOCATION_STACK 

Argument is passed on the stack.

Function Documentation

◆ _copy_type_graph_to_arena()

infix_type * _copy_type_graph_to_arena ( infix_arena_t dest_arena,
const infix_type src_type 
)

Performs a deep copy of a type graph into a destination arena.

Located in src/core/types.c. This is the "Copy" stage of the data pipeline, crucial for creating self-contained trampoline objects and ensuring memory safety. It uses memoization to correctly handle cycles and shared type objects.

Parameters
[in]dest_arenaThe destination arena for the new type graph.
[in]src_typeThe source type graph to copy.
Returns
A pointer to the newly created copy in dest_arena, or nullptr on failure.

◆ _infix_align_up()

static size_t _infix_align_up ( size_t  value,
size_t  alignment 
)
inlinestatic

Aligns a value up to the next multiple of a power-of-two alignment.

Parameters
valueThe value to align.
alignmentThe alignment boundary (must be a power of two).
Returns
The aligned value.

◆ _infix_clear_error()

void _infix_clear_error ( void  )

Clears the thread-local error state.

Located in src/core/error.c. This is called at the beginning of every public API function to ensure that a prior error from an unrelated call is not accidentally returned.

◆ _infix_estimate_graph_size()

size_t _infix_estimate_graph_size ( infix_arena_t temp_arena,
const infix_type type 
)

Estimates the total memory required to deep-copy a complete type graph.

Located in src/core/types.c. This function recursively walks the entire type graph, including all nested aggregates and function arguments, to calculate the exact size needed for an arena that will hold a deep copy.

Parameters
[in]temp_arenaA temporary arena used for the estimator's own bookkeeping (e.g., cycle detection).
[in]typeThe root of the type graph to estimate.
Returns
The estimated size in bytes required for a deep copy.

◆ _infix_forward_create_internal()

c23_nodiscard infix_status _infix_forward_create_internal ( infix_forward_t **  ,
infix_type ,
infix_type **  ,
size_t  ,
size_t  ,
void *   
)

The core implementation for creating all forward trampolines.

Located in src/jit/trampoline.c, this function orchestrates the entire JIT pipeline for forward calls, from ABI classification to code generation and final handle creation.

Returns
INFIX_SUCCESS on success.

◆ _infix_parse_type_internal()

c23_nodiscard infix_status _infix_parse_type_internal ( infix_type **  out_type,
infix_arena_t **  out_arena,
const char *  signature 
)

The internal core of the signature parser.

Located in src/core/signature.c. This is the "Parse" stage of the data pipeline. It takes a signature string and produces a raw, unresolved infix_type graph in a new, temporary arena. It does not perform any copying, resolution, or layout calculation.

Parameters
[out]out_typeOn success, receives the parsed type graph.
[out]out_arenaOn success, receives the temporary arena holding the graph.
[in]signatureThe signature string to parse.
Returns
INFIX_SUCCESS on success.

◆ _infix_resolve_type_graph_inplace()

c23_nodiscard infix_status _infix_resolve_type_graph_inplace ( infix_type **  type_ptr,
infix_registry_t registry 
)

Resolves all named type references in a type graph in-place.

Located in src/core/type_registry.c. This is the "Resolve" stage of the data pipeline. It traverses a type graph and replaces all INFIX_TYPE_NAMED_REFERENCE nodes (@Name) with direct pointers to the canonical infix_type objects from the registry.

Parameters
[in,out]type_ptrA pointer to the root of the type graph to resolve. The pointer may be changed.
[in]registryThe registry to use for lookups.
Returns
INFIX_SUCCESS on success, or an error if a name cannot be resolved.

◆ _infix_set_error()

void _infix_set_error ( infix_error_category_t  category,
infix_error_code_t  code,
size_t  position 
)

Sets the thread-local error state with detailed information.

Located in src/core/error.c, this function is the primary mechanism for reporting errors from within the library. It populates the thread-local g_infix_last_error struct. For parser errors, it generates a rich diagnostic message with a code snippet.

Parameters
categoryThe general category of the error.
codeThe specific error code.
positionFor parser errors, the byte offset into the signature string where the error occurred.

◆ _infix_set_system_error()

void _infix_set_system_error ( infix_error_category_t  category,
infix_error_code_t  code,
long  system_code,
const char *  msg 
)

Sets the thread-local error state for a system-level error.

Located in src/core/error.c, this is used for errors originating from the operating system, such as dlopen or mmap failures.

Parameters
categoryThe general category of the error.
codeThe infix error code that corresponds to the failure.
system_codeThe OS-specific error code (e.g., from errno or GetLastError).
msgAn optional custom message from the OS (e.g., from dlerror).

◆ _infix_type_print_body_only()

c23_nodiscard infix_status _infix_type_print_body_only ( char *  buffer,
size_t  buffer_size,
const infix_type type,
infix_print_dialect_t  dialect 
)

An internal-only function to serialize a type's body without its registered name.

Located in src/core/signature.c. Unlike infix_type_print, which will print @Name for a named struct, this function will always print the full {...} body. This is essential for infix_registry_print to function correctly.

Parameters
[out]bufferThe output buffer.
[in]buffer_sizeThe size of the buffer.
[in]typeThe type to print.
[in]dialectThe output format dialect.
Returns
INFIX_SUCCESS on success.

◆ _infix_type_recalculate_layout()

void _infix_type_recalculate_layout ( infix_type type)

Recalculates the layout of a fully resolved type graph.

Located in src/core/types.c. This is the "Layout" stage of the data pipeline. It recursively walks a type graph and computes the final size, alignment, and member offset fields for all aggregate types. It must only be called on a fully resolved graph.

Parameters
[in,out]typeThe root of the type graph to recalculate. The graph is modified in-place.

◆ code_buffer_append()

void code_buffer_append ( code_buffer buf,
const void *  data,
size_t  len 
)

Appends raw bytes to a code buffer, reallocating within its arena if necessary.

See src/jit/trampoline.c. This is the fundamental operation for building the machine code. If the buffer runs out of space, it is grown exponentially.

Parameters
[in,out]bufThe code buffer to append to.
[in]dataA pointer to the bytes to append.
[in]lenThe number of bytes to append.

◆ code_buffer_init()

void code_buffer_init ( code_buffer buf,
infix_arena_t arena 
)

Initializes a code buffer for JIT code generation.

See src/jit/trampoline.c. Associates the buffer with a temporary arena and sets its initial capacity.

Parameters
[out]bufA pointer to the code_buffer to initialize.
[in]arenaThe temporary arena to use for the buffer's memory.

◆ emit_byte()

void emit_byte ( code_buffer buf,
uint8_t  byte 
)

A convenience wrapper to append a single byte to a code buffer.

Parameters
[in,out]bufThe code buffer.
[in]byteThe byte to append.

◆ emit_int32()

void emit_int32 ( code_buffer buf,
int32_t  value 
)

A convenience wrapper to append a 32-bit integer (little-endian) to a code buffer.

Parameters
[in,out]bufThe code buffer.
[in]valueThe 32-bit integer to append.

◆ emit_int64()

void emit_int64 ( code_buffer buf,
int64_t  value 
)

A convenience wrapper to append a 64-bit integer (little-endian) to a code buffer.

Parameters
[in,out]bufThe code buffer.
[in]valueThe 64-bit integer to append.

◆ get_current_forward_abi_spec()

const infix_forward_abi_spec * get_current_forward_abi_spec ( void  )

Gets the ABI v-table for forward calls for the current platform.

See src/jit/trampoline.c. This function is the entry point to the ABI abstraction layer, returning the correct set of function pointers based on the compile-time ABI detection.

Returns
A pointer to the active infix_forward_abi_spec.

◆ get_current_reverse_abi_spec()

const infix_reverse_abi_spec * get_current_reverse_abi_spec ( void  )

Gets the ABI v-table for reverse calls for the current platform.

See src/jit/trampoline.c. This function mirrors get_current_forward_abi_spec for reverse call trampolines.

Returns
A pointer to the active infix_reverse_abi_spec.

◆ infix_executable_alloc()

c23_nodiscard infix_executable_t infix_executable_alloc ( size_t  size)

Allocates a block of executable memory using the platform's W^X strategy.

Located in src/jit/executor.c. This is a platform-specific function that abstracts VirtualAlloc, mmap with MAP_JIT, or shm_open with dual-mapping.

Parameters
sizeThe number of bytes to allocate.
Returns
An infix_executable_t handle containing pointers to the allocated memory.

◆ infix_executable_free()

void infix_executable_free ( infix_executable_t  exec)

Frees a block of executable memory and applies guard pages to prevent use-after-free.

Located in src/jit/executor.c. Before freeing, it attempts to change the memory's protection to be inaccessible, causing an immediate crash on a UAF.

Parameters
execThe handle to the memory block to free.

◆ infix_executable_make_executable()

c23_nodiscard bool infix_executable_make_executable ( infix_executable_t  exec)

Makes a block of JIT memory executable, completing the W^X process.

Located in src/jit/executor.c. For single-map platforms, this calls VirtualProtect or mprotect. For dual-map platforms, this is a no-op. It also handles instruction cache flushing on relevant architectures like AArch64.

Parameters
execThe handle to the memory block to make executable.
Returns
true on success, false on failure.

◆ infix_internal_dispatch_callback_fn_impl()

void infix_internal_dispatch_callback_fn_impl ( infix_reverse_t context,
void *  return_value_ptr,
void **  args_array 
)

The universal C entry point for all reverse call trampolines.

Located in src/jit/executor.c, this function is called by the JIT-compiled stub. It receives the marshalled arguments and dispatches the call to either the type-safe callback (via a cached forward trampoline) or the generic closure handler.

Parameters
[in]contextThe infix_reverse_t context for this call.
[out]return_value_ptrA pointer to the stack buffer for the return value.
[in]args_arrayA pointer to the void** array of argument pointers.

◆ infix_protected_alloc()

c23_nodiscard infix_protected_t infix_protected_alloc ( size_t  size)

Allocates a block of standard memory for later protection.

Located in src/jit/executor.c. This is used to allocate the memory for an infix_reverse_t context before it is made read-only.

Parameters
sizeThe number of bytes to allocate.
Returns
An infix_protected_t handle.

◆ infix_protected_free()

void infix_protected_free ( infix_protected_t  prot)

Frees a block of protected memory.

Located in src/jit/executor.c.

Parameters
protThe memory block to free.

◆ infix_protected_make_readonly()

c23_nodiscard bool infix_protected_make_readonly ( infix_protected_t  prot)

Makes a block of memory read-only for security hardening.

Located in src/jit/executor.c. This is called on the infix_reverse_t context after it has been fully initialized.

Parameters
protThe memory block to make read-only.
Returns
true on success, false on failure.

◆ is_double()

static bool is_double ( const infix_type type)
inlinestatic

A fast inline check to determine if an infix_type is a double.

Parameters
typeThe type to check.
Returns
true if the type is a double primitive.

◆ is_float()

static bool is_float ( const infix_type type)
inlinestatic

A fast inline check to determine if an infix_type is a float.

Parameters
typeThe type to check.
Returns
true if the type is a float primitive.

◆ is_long_double()

static bool is_long_double ( const infix_type type)
inlinestatic

A fast inline check to determine if an infix_type is a long double.

Parameters
typeThe type to check.
Returns
true if the type is a long double primitive.