memory: add flat translation interface

Defines the API for memory translation.

Signed-off-by: Ronald Caesar <github43132@proton.me>
This commit is contained in:
Ronald Caesar
2026-01-14 22:39:06 -04:00
parent 3d02fab4b7
commit 00c2866fb4
4 changed files with 163 additions and 14 deletions

View File

@@ -13,7 +13,8 @@ typedef enum
BAL_SUCCESS = 0,
BAL_ERROR_INVALID_ARGUMENT = -1,
BAL_ERROR_ALLOCATION_FAILED = -2,
BAL_ERROR_ENGINE_STATE_INVALID = -3,
BAL_ERROR_MEMORY_ALIGNMENT = -3,
BAL_ERROR_ENGINE_STATE_INVALID = -4,
// IR Errors.
//

View File

@@ -11,6 +11,8 @@
#define BALLISTIC_MEMORY_H
#include "bal_attributes.h"
#include "bal_types.h"
#include "bal_errors.h"
#include <stdint.h>
#include <stddef.h>
@@ -38,6 +40,30 @@ typedef void (*bal_free_function_t)(void *allocator,
void *pointer,
size_t size);
/*!
* @brief Translate a Guest Virtual Address to a Host Virtual Address.
*
* @details
* Ballistic calls this when it needs to fetch instructions. The implementer
* must return a pointer to the host memory corresponding to the requested
* guest address.
*
* @param[in] context The oapque pointer provided in @ref
* bal_memory_interface_t.
* @param[in] guest_address The guest address to translate.
* @param[out] max_readable_size The implementer MUST write the number of
* contiguous bytes valid to read from the
* returned pointer. This prevents Ballistic from
* reading off the end of a mapped page or buffer.
*
* @return A pointer to the host memory containing the data at @p guest_address.
* @return NULL if the address is unmapped or invalid.
*/
typedef const uint8_t *(*bal_translate_function_t)(
void *context,
bal_guest_address_t guest_address,
size_t *max_readable_size);
typedef struct
{
/*!
@@ -58,15 +84,54 @@ typedef struct
} bal_allocator_t;
typedef struct
{
void *context;
bal_translate_function_t translate;
} bal_memory_interface_t;
/*!
* @brief Populates an allocator struct with the default implementation.
*
* @param[out] allocator The strict to populate. Must not be NULL.
* @param[out] allocator The struct to populate. Must not be NULL.
*
* @warn Only supports Windows and POSIX systems.
*/
BAL_COLD void get_default_allocator(bal_allocator_t *out_allocator);
BAL_COLD void bal_get_default_allocator(bal_allocator_t *out_allocator);
/*!
* @brief Initializes a translation interface that uses a flat, contiguous
* memory buffer.
*
* @params[in] allocator The allocator used to allocate the internal interface
* structure.
* @params[out] interface The interface struct to populate.
* @params[in] buffer Pointer to the pre-allocated host memory containing
* the guest code.
* @params[in] size The size of the buffer in bytes.
*
* @return BAL_SUCCESS on success.
* @return BAL_ERROR_INVALID_ARGUMENT if arguments are NULL.
* @return BAL_ERROR_MEMORY_ALIGNMENT if buffer is not align to 4 bytes.
*
* @warning The caller retains ownership of buffer. It must remain valid for
* the lifetime of the interface.
*/
BAL_COLD bal_error_t
bal_memory_init_flat(bal_allocator_t *BAL_RESTRICT allocator,
bal_memory_interface_t *BAL_RESTRICT interface,
void *BAL_RESTRICT buffer,
size_t size);
/*!
* @brief Frees the interface's internal state.
*
* @details
* This does not free the buffer passed during initialization, as Ballistic
* does not own it.
*/
BAL_COLD void bal_memory_destroy_flat(bal_allocator_t *allocator,
bal_memory_interface_t *interface);
#endif /* BALLISTIC_MEMORY_H */
/*** end of file ***/

View File

@@ -8,7 +8,9 @@
#include <stdint.h>
typedef uint64_t bal_instruction_t;
typedef uint64_t bal_guest_address_t;
typedef uint16_t bal_instruction_count_t;
typedef uint16_t bal_ssa_id_t;
typedef uint8_t bal_bit_width_t;

View File

@@ -1,22 +1,78 @@
#include "bal_memory.h"
#include "bal_platform.h"
#include <stdlib.h>
#include <stddef.h>
void *default_allocate(void *, size_t, size_t);
void default_free(void *, void *, size_t);
static void *default_allocate(void *, size_t, size_t);
static void default_free(void *, void *, size_t);
BAL_HOT static const uint8_t *_bal_translate_flat(void *,
bal_guest_address_t,
size_t *);
typedef struct
{
uint8_t *host_base;
size_t size;
} flat_translation_interface_t;
void
get_default_allocator (bal_allocator_t *out_allocator)
bal_get_default_allocator (bal_allocator_t *out_allocator)
{
out_allocator->allocator = NULL;
out_allocator->allocate = default_allocate;
out_allocator->free = default_free;
}
bal_error_t
bal_memory_init_flat (bal_allocator_t *BAL_RESTRICT allocator,
bal_memory_interface_t *BAL_RESTRICT interface,
void *BAL_RESTRICT buffer,
size_t size)
{
if (NULL == allocator || NULL == interface || NULL == buffer || 0 == size)
{
return BAL_ERROR_INVALID_ARGUMENT;
}
if (((uintptr_t)buffer & 3U) != 0)
{
// Buffer is not 4 byte aligned.
//
return BAL_ERROR_MEMORY_ALIGNMENT;
}
size_t memory_alignment = 4U;
flat_translation_interface_t *flat_interface
= (flat_translation_interface_t *)allocator->allocate(
allocator, memory_alignment, sizeof(flat_translation_interface_t));
flat_interface->host_base = (uint8_t *)buffer;
flat_interface->size = size;
interface->context = flat_interface;
interface->translate = _bal_translate_flat;
return BAL_SUCCESS;
}
void
bal_memory_destroy_flat (bal_allocator_t *allocator,
bal_memory_interface_t *interface)
{
if (NULL == allocator || NULL == interface)
{
return;
}
if (NULL == interface->context)
{
return;
}
allocator->free(
allocator, interface->context, sizeof(flat_translation_interface_t));
}
#if BAL_PLATFORM_POSIX
void *
static void *
default_allocate (void *allocator, size_t alignment, size_t size)
{
if (0 == size)
@@ -25,11 +81,10 @@ default_allocate (void *allocator, size_t alignment, size_t size)
}
void *memory = aligned_alloc(alignment, size);
return memory;
}
void
static void
default_free (void *allocator, void *pointer, size_t size)
{
free(pointer);
@@ -41,7 +96,7 @@ default_free (void *allocator, void *pointer, size_t size)
#include <malloc.h>
void *
static void *
default_allocate (void *allocator, size_t alignment, size_t size)
{
if (0 == size)
@@ -50,11 +105,10 @@ default_allocate (void *allocator, size_t alignment, size_t size)
}
void *memory = _aligned_malloc(alignment, size);
return memory;
}
void
static void
default_free (void *allocator, void *pointer, size_t size)
{
_aligned_free(pointer);
@@ -62,4 +116,31 @@ default_free (void *allocator, void *pointer, size_t size)
#endif /* BAL_PLATFORM_WINDOWS */
static const uint8_t *
_bal_translate_flat (void *BAL_RESTRICT interface,
bal_guest_address_t guest_address,
size_t *BAL_RESTRICT max_readable_size)
{
if (BAL_UNLIKELY(NULL == interface || 0 == guest_address
|| NULL == max_readable_size))
{
return NULL;
}
flat_translation_interface_t *BAL_RESTRICT context
= (flat_translation_interface_t *)((bal_memory_interface_t *)interface)
->context;
// Is address out of bounds.
//
if (guest_address >= context->size)
{
return NULL;
}
*max_readable_size = context->size - guest_address;
const uint8_t *host_address = context->host_base + guest_address;
return host_address;
}
/*** end of file ***/