engine: fix name mangling with c++ compilers

Added `extern "C"` to every header file to support using Ballistic
with C++ projects

Signed-off-by: Ronald Caesar <github43132@proton.me>
This commit is contained in:
Ronald Caesar
2026-04-16 16:56:45 -04:00
parent 43199c79f8
commit e3a0870b86
12 changed files with 689 additions and 574 deletions
+115 -103
View File
@@ -28,10 +28,15 @@
#include <stddef.h>
#include <stdint.h>
/// This struct manages a linear buffer of 32-bit integers where ARM64 machine code is written.
/// It tracks the current write position and handles boundary checking.
typedef struct
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/// This struct manages a linear buffer of 32-bit integers where ARM64 machine code is written.
/// It tracks the current write position and handles boundary checking.
typedef struct
{
/// A pointer to the start of the code buffer.
uint32_t *buffer;
@@ -46,125 +51,132 @@ typedef struct
/// The current error state of the assembler.
///
/// Once this is set to anything other than [`BAL_SUCCESS`], all subsequent emit calls will be
/// ignored until the assembler is reset.
/// Once this is set to anything other than [`BAL_SUCCESS`], all subsequent emit calls will
/// be ignored until the assembler is reset.
bal_error_t status;
} bal_assembler_t;
} bal_assembler_t;
/// Initializes the assembler with a specific memory buffer and the size of the buffer in
/// `uint32_t` elements.
///
/// # Returns
///
/// * [`BAL_SUCCESS`] on success.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if `assembler` or `buffer` is NULL.
/// * [`BAL_ERROR_MEMORY_ALIGNMENT`] if `buffer` is not 4-byte aligned.
bal_error_t bal_assembler_init(bal_assembler_t *assembler,
/// Initializes the assembler with a specific memory buffer and the size of the buffer in
/// `uint32_t` elements.
///
/// # Returns
///
/// * [`BAL_SUCCESS`] on success.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if `assembler` or `buffer` is NULL.
/// * [`BAL_ERROR_MEMORY_ALIGNMENT`] if `buffer` is not 4-byte aligned.
bal_error_t bal_assembler_init(bal_assembler_t *assembler,
void *buffer,
size_t size,
bal_logger_t logger);
/// Emit a `ADD` (Immediate) instruction.
///
/// Adds a register value `rn` and 12 bit immediate value `imm12` with the optional left shift `sh`
/// to apply to the immediate, and writes the result to the destination register `rd`.
///
/// # Safety
///
/// * `rn` will be truncated if it exceeds 5 bits.
/// * `imm12` will be truncated if it exceeds 12 bits.
/// * `sh` must have the value `0`, or `1`
/// * Function does not emit instructions if `assembler->status != BALL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_add_immediate(
bal_assembler_t *assembler, bal_register_index_t rd, uint8_t rn, uint16_t imm12, uint8_t shift);
/// Emit a `ADD` (Immediate) instruction.
///
/// Adds a register value `rn` and 12 bit immediate value `imm12` with the optional left shift
/// `sh` to apply to the immediate, and writes the result to the destination register `rd`.
///
/// # Safety
///
/// * `rn` will be truncated if it exceeds 5 bits.
/// * `imm12` will be truncated if it exceeds 12 bits.
/// * `sh` must have the value `0`, or `1`
/// * Function does not emit instructions if `assembler->status != BALL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_add_immediate(bal_assembler_t *assembler,
bal_register_index_t rd,
uint8_t rn,
uint16_t imm12,
uint8_t shift);
/// Emit a `MOVZ` (Move Wide with Zero) instruction.
///
/// Moves a 16-bit immediate into a register, shifting it left by 0, 16, 32, or 48 bits, and
/// the rest of the register to zero.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movz(bal_assembler_t *assembler,
/// Emit a `MOVZ` (Move Wide with Zero) instruction.
///
/// Moves a 16-bit immediate into a register, shifting it left by 0, 16, 32, or 48 bits, and
/// the rest of the register to zero.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movz(bal_assembler_t *assembler,
bal_register_index_t rd,
uint16_t imm,
uint8_t shift);
/// Emits a `MOVK` (Move Wide with Keep) instruction.
///
/// Moves a 16-bit immediate into a specific 16-bit field of a register, leaving the other bits
/// unchanged.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movk(bal_assembler_t *assembler,
/// Emits a `MOVK` (Move Wide with Keep) instruction.
///
/// Moves a 16-bit immediate into a specific 16-bit field of a register, leaving the other bits
/// unchanged.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movk(bal_assembler_t *assembler,
bal_register_index_t rd,
uint16_t imm,
uint8_t shift);
/// Emits a `MOVN` (Move Wide with Not) instruction.
///
/// Moves the bitwise inverse of a 16-bit immediate (shifted left) into a register, setting all
/// other bits to 1.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movn(bal_assembler_t *assembler,
/// Emits a `MOVN` (Move Wide with Not) instruction.
///
/// Moves the bitwise inverse of a 16-bit immediate (shifted left) into a register, setting all
/// other bits to 1.
///
/// # Safety
///
/// * `shift` must be 0, 16, 32, or 48.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_movn(bal_assembler_t *assembler,
bal_register_index_t rd,
uint16_t imm,
uint8_t shift);
/// Emits a `RET` instruction.
///
/// Returns from subroutine branches unconditionally to an address in a register with a hint that
/// this is a subroutine return.
///
/// # Safety
///
/// * `rn` must be between 0-31 inclusive.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_ret(bal_assembler_t *assembler, bal_register_index_t rn);
/// Emits a `RET` instruction.
///
/// Returns from subroutine branches unconditionally to an address in a register with a hint
/// that this is a subroutine return.
///
/// # Safety
///
/// * `rn` must be between 0-31 inclusive.
/// * Does not emit if `assembler->status != BAL_SUCCESS`.
///
/// # Errors
///
/// Modifies `assembler->status` to the following if an error occurs:
///
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
void bal_emit_ret(bal_assembler_t *assembler, bal_register_index_t rn);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BALLISTIC_ASSEMBLER_H */
+15 -2
View File
@@ -3,8 +3,17 @@
#include <stddef.h>
void bal_internal_assert_fail(
const char *file, int line, const char *func, const char *expr_str, const char *user_msg, ...);
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
void bal_internal_assert_fail(const char *file,
int line,
const char *func,
const char *expr_str,
const char *user_msg,
...);
#define BAL_ASSERT(expression) \
do \
@@ -33,4 +42,8 @@ void bal_internal_assert_fail(
"Unreachable code executed", \
##__VA_ARGS__);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif // BALLISTIC_COMMON_ASSERT_H
+9
View File
@@ -8,6 +8,11 @@
#include "bal_platform.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/// BAL_HOT()/BAL_COLD()
/// Marks a function as hot or cold. Hot makes the compiler optimize it more
/// aggressively. Cold marks the function as rarely executed.
@@ -77,6 +82,10 @@
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BALLISTIC_ATTRIBUTES_H */
/*** end of file ***/
+2 -2
View File
@@ -17,7 +17,7 @@
#ifdef __cplusplus
extern "C"
{
#endif
#endif /* __cplusplus */
#define BAL_OPERANDS_SIZE 5
#define BAL_OPERAND_BIT_WIDTH 5
@@ -91,5 +91,5 @@ extern "C"
#ifdef __cplusplus
}
#endif
#endif /* __cplusplus */
#endif // BAL_DECODER_H
+70 -60
View File
@@ -8,6 +8,11 @@
#include "bal_types.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/// A byte pattern written to memory during initialization, poisoning allocated
/// regions. This is mainly used for detecting reads from uninitialized memory.
#define POISON_UNINITIALIZED_MEMORY 0xFF
@@ -40,24 +45,24 @@
/// The bit position for the is constant flag in a bal_instruction_t.
#define BAL_IS_CONSTANT_BIT_POSITION (1U << 16U)
/// Represents the mapping of a Guest Register to an SSA variable.
/// This is only used during Single Static Assignment construction
/// to track variable definitions across basic blocks.
typedef struct
{
/// Represents the mapping of a Guest Register to an SSA variable.
/// This is only used during Single Static Assignment construction
/// to track variable definitions across basic blocks.
typedef struct
{
/// The index of the most recent SSA definition for this register.
uint32_t current_ssa_index;
/// The index of the SSA definition that existed at the start of the
/// current block.
uint32_t original_variable_index;
} bal_source_variable_t;
} bal_source_variable_t;
/// Holds the Intermediate Representation buffers, SSA state, and other
/// important metadata. The structure is divided into hot and cold data aligned
/// to 64 bytes. Both hot and cold data lives on their own cache lines.
typedef struct
{
/// Holds the Intermediate Representation buffers, SSA state, and other
/// important metadata. The structure is divided into hot and cold data aligned
/// to 64 bytes. Both hot and cold data lives on their own cache lines.
typedef struct
{
/* Hot Data */
/// Map of ARM registers to their current SSA definitions.
@@ -112,64 +117,69 @@ typedef struct
/// Handles logging for this engine.
bal_logger_t logger;
} bal_engine_t;
} bal_engine_t;
/// Initializes a Ballistic engine.
///
/// Populates `engine` with `logger` and empty buffers allocated with `allocator`. This is
/// a high-cost memory operation that reserves a lot of memory and should
/// be called sparingly.
///
/// Returns [`BAL_SUCCESS`] if the engine iz ready for use.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if the pointers are `NULL`.
///
/// Returns [`BAL_ERROR_ALLOCATION_FAILED`] if the allocator cannot fulfill the
/// request.
BAL_COLD bal_error_t bal_engine_init(const bal_allocator_t *allocator,
/// Initializes a Ballistic engine.
///
/// Populates `engine` with `logger` and empty buffers allocated with `allocator`. This is
/// a high-cost memory operation that reserves a lot of memory and should
/// be called sparingly.
///
/// Returns [`BAL_SUCCESS`] if the engine iz ready for use.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if the pointers are `NULL`.
///
/// Returns [`BAL_ERROR_ALLOCATION_FAILED`] if the allocator cannot fulfill the
/// request.
BAL_COLD bal_error_t bal_engine_init(const bal_allocator_t *allocator,
bal_engine_t *engine,
bal_logger_t logger);
/// Translates machine code starting at `guest_address_start` into the engine's
/// internal IR. `interface` provides memory access handling (like instruction
/// fetching).
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Safety
///
/// If `guest_address_staart` is `NULL`, we assume the entry point is 0x0.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if functino arguments are invalid or
/// or `engine->status != BAL_SUCCESS`.
///
/// Returns [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if the array `engine->constants` overflows.
BAL_HOT bal_error_t bal_engine_translate(bal_engine_t *BAL_RESTRICT engine,
/// Translates machine code starting at `guest_address_start` into the engine's
/// internal IR. `interface` provides memory access handling (like instruction
/// fetching).
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Safety
///
/// If `guest_address_staart` is `NULL`, we assume the entry point is 0x0.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if functino arguments are invalid or
/// or `engine->status != BAL_SUCCESS`.
///
/// Returns [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if the array `engine->constants` overflows.
BAL_HOT bal_error_t bal_engine_translate(bal_engine_t *BAL_RESTRICT engine,
const bal_memory_interface_t *BAL_RESTRICT interface,
bal_guest_address_t *guest_address_start,
size_t max_instructions);
/// Resets `engine` for the next compilation unit. This is a low-cost memory
/// operation designed to be called between translation units.
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if `engine` is `NULl`.
BAL_HOT bal_error_t bal_engine_reset(bal_engine_t *engine);
/// Resets `engine` for the next compilation unit. This is a low-cost memory
/// operation designed to be called between translation units.
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if `engine` is `NULl`.
BAL_HOT bal_error_t bal_engine_reset(bal_engine_t *engine);
/// Frees all `engine` heap-allocated resources using `allocator`.
///
/// # Warning
///
/// This function does not free the [`bal_engine_t`] struct itself, as the
/// caller may have allocated it on the stack.
BAL_COLD void bal_engine_destroy(const bal_allocator_t *allocator, bal_engine_t *engine);
#ifdef __cplusplus
}
#endif /* __cplusplus */
/// Frees all `engine` heap-allocated resources using `allocator`.
///
/// # Warning
///
/// This function does not free the [`bal_engine_t`] struct itself, as the
/// caller may have allocated it on the stack.
BAL_COLD void bal_engine_destroy(const bal_allocator_t *allocator, bal_engine_t *engine);
#endif /* BALLISTIC_ENGINE_H */
/*** end of file ***/
+13 -4
View File
@@ -8,8 +8,13 @@
#include "bal_attributes.h"
typedef enum
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
typedef enum
{
// General Errors.
//
BAL_SUCCESS = 0,
@@ -30,10 +35,14 @@ typedef enum
/// The decoded register type does not match the expected type.
BAL_ERROR_INCORRECT_REGISTER_TYPE = -101,
} bal_error_t;
} bal_error_t;
/// Converts the enum into a readable string for error handling.
BAL_COLD const char *bal_error_to_string(bal_error_t error);
/// Converts the enum into a readable string for error handling.
BAL_COLD const char *bal_error_to_string(bal_error_t error);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BAL_ERRORS_H */
+47 -37
View File
@@ -58,9 +58,14 @@
#include "bal_attributes.h"
#include <stdarg.h>
/// Defines the severity of a log message.
typedef enum
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/// Defines the severity of a log message.
typedef enum
{
/// Logging is disabled.
BAL_LOG_LEVEL_NONE = -1,
@@ -78,11 +83,11 @@ typedef enum
/// Extreme verbose output.
BAL_LOG_LEVEL_TRACE = 4,
} bal_log_level_t;
} bal_log_level_t;
/// Metadata associated with a specific log event.
typedef struct
{
/// Metadata associated with a specific log event.
typedef struct
{
/// Source file where the log occurred.
const char *filename;
@@ -94,22 +99,22 @@ typedef struct
/// The line number where the log occurred
int line;
} bal_log_data_t;
} bal_log_data_t;
/// A function pointer defining a custom logging backend.
///
/// Implementations of this function are responsible for formatting and persisting the log
/// message. The `user_data` pointer is passed through from the `bal_logger_t` context. The
/// `bal_data` struct contains metadata about the event, while `format`, and `args` provide the
/// standard printf-style message content.
typedef void (*bal_log_function_t)(void *user_data,
/// A function pointer defining a custom logging backend.
///
/// Implementations of this function are responsible for formatting and persisting the log
/// message. The `user_data` pointer is passed through from the `bal_logger_t` context. The
/// `bal_data` struct contains metadata about the event, while `format`, and `args` provide the
/// standard printf-style message content.
typedef void (*bal_log_function_t)(void *user_data,
bal_log_data_t *bal_data,
const char *format,
va_list args);
/// The main logging context.
typedef struct
{
/// The main logging context.
typedef struct
{
/// An opaque pointer passed to the log callback. This can be used to store file handles or
/// other context-specific data.
void *user_data;
@@ -120,7 +125,7 @@ typedef struct
/// The minimum severity level required for a message to be processed.
bal_log_level_t min_level;
} bal_logger_t;
} bal_logger_t;
// Remove all log code if log level not defined.
//
@@ -130,20 +135,21 @@ typedef struct
#endif
/// Dispatches a log message to the configured backend.
///
/// This is the entry point into the logging system. It invokes the callback defined in `logger`.
///
/// # Warning
///
/// Do not call this function directly. Use our logging macros like `BAL_LOG_INFO`.
///
/// # Safety
///
/// The `format` string must match the arguments provided in the variadic list, following standard
/// `printf`.
/// Dispatches a log message to the configured backend.
///
/// This is the entry point into the logging system. It invokes the callback defined in
/// `logger`.
///
/// # Warning
///
/// Do not call this function directly. Use our logging macros like `BAL_LOG_INFO`.
///
/// # Safety
///
/// The `format` string must match the arguments provided in the variadic list, following
/// standard `printf`.
BAL_COLD void bal_log_message(const bal_logger_t *logger,
BAL_COLD void bal_log_message(const bal_logger_t *logger,
bal_log_level_t log_level,
const char *filename,
const char *function,
@@ -151,12 +157,12 @@ BAL_COLD void bal_log_message(const bal_logger_t *logger,
const char *format,
...);
/// Populates `logger` with Ballistic's default logging implementation.
///
/// # Safety
///
/// `logger` must NOT be `NULL`.
BAL_COLD void bal_logger_init_default(bal_logger_t *logger);
/// Populates `logger` with Ballistic's default logging implementation.
///
/// # Safety
///
/// `logger` must NOT be `NULL`.
BAL_COLD void bal_logger_init_default(bal_logger_t *logger);
#define BAL_LOG_ERROR(logger, format, ...) \
bal_log_message((logger), \
@@ -212,6 +218,10 @@ BAL_COLD void bal_logger_init_default(bal_logger_t *logger);
} while (0)
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BALLISTIC_LOGGING_H */
/*** end of file ***/
+100 -88
View File
@@ -8,55 +8,62 @@
#include <stddef.h>
#include <stdint.h>
struct bal_allocator_state_s;
typedef struct bal_allocator_state_s *bal_allocator_handle_t;
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/// A function signature for allocating aligned memory.
///
/// Implementations must allocate a block of memory of at least `size` bytes
/// with an address that is a multiple of `alignment`. The `alignment`
/// parameter is guaranteed to be a power of two. The `allocator` parameter
/// provides access to the opaque state registered in [`bal_allocator_t`].
///
/// Returns a pointer to the allocated memory, or `NULL` if the request could
/// not be fulfilled.
typedef void *(*bal_allocate_function_t)(bal_allocator_handle_t allocator,
struct bal_allocator_state_s;
typedef struct bal_allocator_state_s *bal_allocator_handle_t;
/// A function signature for allocating aligned memory.
///
/// Implementations must allocate a block of memory of at least `size` bytes
/// with an address that is a multiple of `alignment`. The `alignment`
/// parameter is guaranteed to be a power of two. The `allocator` parameter
/// provides access to the opaque state registered in [`bal_allocator_t`].
///
/// Returns a pointer to the allocated memory, or `NULL` if the request could
/// not be fulfilled.
typedef void *(*bal_allocate_function_t)(bal_allocator_handle_t allocator,
size_t alignment,
size_t size);
/// A function signature for releasing memory.
///
/// Implementations must deallocate the memory at `pointer`, which was
/// previously allocated by the corresponding to allocate function. The `size`
/// parameter indicates the size of the allocation being freed. Access to the
/// heap state is provided via `allocator`.
typedef void (*bal_free_function_t)(bal_allocator_handle_t allocator, void *pointer, size_t size);
/// A function signature for releasing memory.
///
/// Implementations must deallocate the memory at `pointer`, which was
/// previously allocated by the corresponding to allocate function. The `size`
/// parameter indicates the size of the allocation being freed. Access to the
/// heap state is provided via `allocator`.
typedef void (*bal_free_function_t)(bal_allocator_handle_t allocator,
void *pointer,
size_t size);
/// Translates a Guest Virtual Address (GVA) to a Host Virtual Address (HVA).
///
/// Ballistic invokes this callback when it needs to fetch instructions or
/// access data.
///
/// Returns a pointer to the host memory containing the data at `guest_address`, or `NULL`
/// if the address is unmapped or invalid.
///
/// # Safety
///
/// The implementation must translate the provided `guest_address`
/// using the opaque `context` and return a pointer to the corresponding host
/// memory.
///
/// The implementation must write the number of contiguous, readable bytes
/// available at the returned pointer into `max_readable_size_bytes`. This prevents
/// Ballistic from reading beyond the end of a mapped page or buffer.
typedef const uint8_t *(*bal_translate_function_t)(void *context,
/// Translates a Guest Virtual Address (GVA) to a Host Virtual Address (HVA).
///
/// Ballistic invokes this callback when it needs to fetch instructions or
/// access data.
///
/// Returns a pointer to the host memory containing the data at `guest_address`, or `NULL`
/// if the address is unmapped or invalid.
///
/// # Safety
///
/// The implementation must translate the provided `guest_address`
/// using the opaque `context` and return a pointer to the corresponding host
/// memory.
///
/// The implementation must write the number of contiguous, readable bytes
/// available at the returned pointer into `max_readable_size_bytes`. This prevents
/// Ballistic from reading beyond the end of a mapped page or buffer.
typedef const uint8_t *(*bal_translate_function_t)(void *context,
bal_guest_address_t guest_address,
size_t *max_readable_size_bytes);
/// The host application is responsible for providing an allocator capable of
/// handling aligned memory requests.
typedef struct
{
/// The host application is responsible for providing an allocator capable of
/// handling aligned memory requests.
typedef struct
{
/// An opaque pointer defining the state or tracking information for the
/// heap.
bal_allocator_handle_t handle;
@@ -72,68 +79,73 @@ typedef struct
/// The callback to release memory.
bal_free_function_t free;
} bal_allocator_t;
} bal_allocator_t;
/// Defines the interface for translating guest addresses to host memory.
typedef struct
{
/// Defines the interface for translating guest addresses to host memory.
typedef struct
{
/// An opaque pointer to the context required for address translation
/// (e.g, a page walker or a buffer descriptor.).
void *context;
/// The callback invoked to perform address translation.
bal_translate_function_t translate;
} bal_memory_interface_t;
} bal_memory_interface_t;
/// Populates `out_allocator` with the default system implementation.
///
/// # Safety
///
/// `out_allocator` must not be `NULL`.
///
/// # Platform Support
///
/// This function only supports windows and POSIX-compliant systems.
BAL_COLD void bal_allocator_default_init(bal_allocator_t *out_allocator);
/// Populates `out_allocator` with the default system implementation.
///
/// # Safety
///
/// `out_allocator` must not be `NULL`.
///
/// # Platform Support
///
/// This function only supports windows and POSIX-compliant systems.
BAL_COLD void bal_allocator_default_init(bal_allocator_t *out_allocator);
/// Initializes a flat, contiguous translation interface.
///
/// This convenience function sets up a [`bal_memory_interface_t`] where guest
/// addresses map directly to offsets within the provided host `buffer`.
///
/// The internal interface is allocated with `allocator`. `interface` is
/// populated with the resulting context and translation callbacks. `buffer`
/// must be a pre-allocated block of host memory of at least `size` bytes.
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if any pointer are `NULL`.
///
/// Returns [`BAL_ERROR_MEMORY_ALIGNMENT`] if `buffer` is not aligned to a 16-byte
/// boundary.
///
/// Returns [`BAL_ERROR_ALLOCATION_FAILED`] if failed to allocate memory interface.
///
/// # Safety
///
/// The caller retains ownership of `buffer`. It must remain valid and
/// unmodified for the lifetime of the created interface.
BAL_COLD bal_error_t
bal_flat_translation_interface_init(bal_allocator_t *BAL_RESTRICT allocator,
/// Initializes a flat, contiguous translation interface.
///
/// This convenience function sets up a [`bal_memory_interface_t`] where guest
/// addresses map directly to offsets within the provided host `buffer`.
///
/// The internal interface is allocated with `allocator`. `interface` is
/// populated with the resulting context and translation callbacks. `buffer`
/// must be a pre-allocated block of host memory of at least `size` bytes.
///
/// Returns [`BAL_SUCCESS`] on success.
///
/// # Errors
///
/// Returns [`BAL_ERROR_INVALID_ARGUMENT`] if any pointer are `NULL`.
///
/// Returns [`BAL_ERROR_MEMORY_ALIGNMENT`] if `buffer` is not aligned to a 16-byte
/// boundary.
///
/// Returns [`BAL_ERROR_ALLOCATION_FAILED`] if failed to allocate memory interface.
///
/// # Safety
///
/// The caller retains ownership of `buffer`. It must remain valid and
/// unmodified for the lifetime of the created interface.
BAL_COLD bal_error_t
bal_flat_translation_interface_init(bal_allocator_t *BAL_RESTRICT allocator,
bal_memory_interface_t *BAL_RESTRICT interface,
void *BAL_RESTRICT buffer,
size_t size,
bal_logger_t logger);
/// Frees the internal state allocated within `interface` using the provided
/// `allocator` and resets `interface` to 0 with a memset.
///
/// This does **not** free the buffer passed to [`bal_flat_translation_interface_init`] as
/// Ballistic does not take ownership of the host memory region.
BAL_COLD bal_error_t bal_flat_translation_interface_destroy(bal_allocator_t *allocator,
/// Frees the internal state allocated within `interface` using the provided
/// `allocator` and resets `interface` to 0 with a memset.
///
/// This does **not** free the buffer passed to [`bal_flat_translation_interface_init`] as
/// Ballistic does not take ownership of the host memory region.
BAL_COLD bal_error_t bal_flat_translation_interface_destroy(bal_allocator_t *allocator,
bal_memory_interface_t *interface);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BALLISTIC_MEMORY_H */
/*** end of file ***/
+11
View File
@@ -1,6 +1,11 @@
#ifndef BALLISTIC_PLATFORM_H
#define BALLISTIC_PLATFORM_H
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
#if defined(_WIN32) || defined(_WIN64)
#define BAL_PLATFORM_WINDOWS 1
@@ -49,4 +54,10 @@
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BALLISTIC_PLATFORM_H */
/*** end of file ***/
+21 -12
View File
@@ -8,15 +8,20 @@
#include <stdint.h>
typedef uint64_t bal_guest_address_t;
typedef uint64_t bal_instruction_t;
typedef uint64_t bal_constant_t;
typedef uint16_t bal_instruction_count_t;
typedef uint16_t bal_constant_count_t;
typedef uint8_t bal_bit_width_t;
typedef enum
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
typedef uint64_t bal_guest_address_t;
typedef uint64_t bal_instruction_t;
typedef uint64_t bal_constant_t;
typedef uint16_t bal_instruction_count_t;
typedef uint16_t bal_constant_count_t;
typedef uint8_t bal_bit_width_t;
typedef enum
{
/// This opcode represents a volatile load of a guest register.
OPCODE_GET_REGISTER,
OPCODE_CONST,
@@ -41,10 +46,10 @@ typedef enum
OPCODE_CMP_COND,
OPCODE_TRAP,
OPCODE_ENUM_END = 0x7FF, // Force enum to 2 bytes.
} bal_opcode_t;
} bal_opcode_t;
typedef enum
{
typedef enum
{
/// The register index for X0.
BAL_REGISTER_X0 = 0,
@@ -140,7 +145,11 @@ typedef enum
/// The register index for the Zero Register (XZR)/Stack Pointer (SP).
BAL_REGISTER_X31 = 31,
} bal_register_index_t;
} bal_register_index_t;
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BALLISTIC_TYPES_H */
+19 -6
View File
@@ -3,19 +3,32 @@
* Generated with tools/generate_a64_table.py
*/
#ifndef BAL_DECODER_TABLE_GENERATED
#define BAL_DECODER_TABLE_GENERATED
#include "bal_decoder.h"
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
#define BAL_DECODER_ARM64_INSTRUCTIONS_SIZE 3608
typedef struct {
typedef struct
{
uint16_t index;
uint8_t count;
} decoder_bucket_t;
} decoder_bucket_t;
extern const bal_decoder_instruction_metadata_t g_bal_decoder_arm64_instructions[BAL_DECODER_ARM64_INSTRUCTIONS_SIZE];
extern const decoder_bucket_t g_decoder_lookup_table[2048];
extern const bal_decoder_instruction_metadata_t
g_bal_decoder_arm64_instructions[BAL_DECODER_ARM64_INSTRUCTIONS_SIZE];
extern const decoder_bucket_t g_decoder_lookup_table[2048];
extern const bal_decoder_instruction_metadata_t *const g_decoder_hash_candidates[];
extern const bal_decoder_instruction_metadata_t *const g_decoder_hash_candidates[];
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BAL_DECODER_TABLE_GENERATED */
/*** end of file ***/
+17 -10
View File
@@ -10,17 +10,17 @@ Generated ARM decoder table header file -> ../src/decoder_table_gen.h
Generated ARM decoder table source file -> ../src/decoder_table_gen.c
"""
import argparse
import glob
import io
import os
import re
import sys
import glob
import argparse
import xml.etree.ElementTree as ET
from xml.etree.ElementTree import ElementTree
from xml.etree.ElementTree import Element
from dataclasses import dataclass
from typing import List, Dict, Tuple, Optional
from xml.etree.ElementTree import Element
from xml.etree.ElementTree import ElementTree
DEFAULT_DECODER_GENERATED_HEADER_NAME = "bal_decoder_table_gen.h"
DEFAULT_DECODER_GENERATED_SOURCE_NAME = "bal_decoder_table_gen.c"
@@ -266,8 +266,6 @@ def parse_operands(
if operand_type == "BAL_OPERAND_TYPE_NONE":
continue
operand: Operand = Operand(operand_type, bit_position, bit_width)
if operand not in operands:
@@ -569,9 +567,14 @@ if __name__ == "__main__":
# -------------------------------------------------------------------------
with open(output_header_path, "w", encoding="utf-8") as f:
f.write(f"{GENERATED_FILE_WARNING}\n\n")
f.write("#ifndef BAL_DECODER_TABLE_GENERATED\n")
f.write("#define BAL_DECODER_TABLE_GENERATED\n\n")
f.write(f'#include "{DECODER_HEADER_NAME}"\n')
f.write("#include <stdint.h>\n")
f.write("#include <stddef.h>\n\n")
f.write("#ifdef __cplusplus\n")
f.write('extern "C"\n')
f.write("{\n")
f.write("#endif /* __cplusplus */\n\n")
f.write(
f"#define {DECODER_ARM64_INSTRUCTIONS_SIZE_NAME} {len(all_instructions)}\n\n"
)
@@ -583,11 +586,16 @@ if __name__ == "__main__":
f"extern const {DECODER_METADATA_STRUCT_NAME} {DECODER_ARM64_INSTRUCTIONS_ARRAY_NAME}[{DECODER_ARM64_INSTRUCTIONS_SIZE_NAME}];\n"
)
f.write(
f"extern const {DECODER_HASH_TABLE_BUCKET_STRUCT_NAME} g_decoder_lookup_table[{DECODER_HASH_TABLE_SIZE}];\n\n"
f"extern const {DECODER_HASH_TABLE_BUCKET_STRUCT_NAME} g_decoder_lookup_table[{DECODER_HASH_TABLE_SIZE}];\n"
)
f.write(
f"extern const {DECODER_METADATA_STRUCT_NAME} *const {DECODER_ARM64_CANDIDATES_ARRAY_NAME}[];\n\n"
)
f.write("#ifdef __cplusplus\n")
f.write("}\n")
f.write("#endif /* __cplusplus */\n")
f.write("#endif /* BAL_DECODER_TABLE_GENERATED */\n\n")
f.write("/*** end of file ***/\n\n")
print(f"Generated ARM decoder table header file -> {output_header_path}")
# -------------------------------------------------------------------------
@@ -605,7 +613,7 @@ if __name__ == "__main__":
# (start_index, count)
bucket_descriptors: List[Tuple[int, int]] = []
for i in range (DECODER_HASH_TABLE_SIZE):
for i in range(DECODER_HASH_TABLE_SIZE):
candidate: List[A64Instruction] = buckets[i]
count: int = len(candidate)
@@ -631,7 +639,6 @@ if __name__ == "__main__":
bucket_descriptors.append((start_index, count))
if len(flat_candidates) >= 65535:
printf("Total candidates {len(flat_candidates)} exceeds uint16_t limit.")
sys.exit(1)