mirror of
https://github.com/pound-emu/ballistic.git
synced 2026-06-17 04:16:48 -07:00
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:
+137
-125
@@ -28,143 +28,155 @@
|
||||
#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"
|
||||
{
|
||||
/// A pointer to the start of the code buffer.
|
||||
uint32_t *buffer;
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/// The maximum number of instructions that can fit in the buffer.
|
||||
size_t capacity;
|
||||
/// 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;
|
||||
|
||||
/// The current write index within the buffer.
|
||||
size_t offset;
|
||||
/// The maximum number of instructions that can fit in the buffer.
|
||||
size_t capacity;
|
||||
|
||||
/// The logging context used to report details and errors.
|
||||
bal_logger_t logger;
|
||||
/// The current write index within the buffer.
|
||||
size_t offset;
|
||||
|
||||
/// The current error state of the assembler.
|
||||
/// The logging context used to report details and errors.
|
||||
bal_logger_t logger;
|
||||
|
||||
/// 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.
|
||||
bal_error_t status;
|
||||
} bal_assembler_t;
|
||||
|
||||
/// Initializes the assembler with a specific memory buffer and the size of the buffer in
|
||||
/// `uint32_t` elements.
|
||||
///
|
||||
/// 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;
|
||||
/// # 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);
|
||||
|
||||
/// 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,
|
||||
bal_register_index_t rd,
|
||||
uint16_t imm,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
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
@@ -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
|
||||
|
||||
@@ -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 ***/
|
||||
|
||||
@@ -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
|
||||
|
||||
+122
-112
@@ -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,136 +45,141 @@
|
||||
/// 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
|
||||
{
|
||||
/// The index of the most recent SSA definition for this register.
|
||||
uint32_t current_ssa_index;
|
||||
/// 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;
|
||||
/// The index of the SSA definition that existed at the start of the
|
||||
/// current block.
|
||||
uint32_t original_variable_index;
|
||||
} 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
|
||||
{
|
||||
/* Hot Data */
|
||||
/// 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.
|
||||
bal_source_variable_t *source_variables;
|
||||
/// Map of ARM registers to their current SSA definitions.
|
||||
bal_source_variable_t *source_variables;
|
||||
|
||||
/// The linear buffer of generated IR instructions for the current
|
||||
/// compilation unit.
|
||||
bal_instruction_t *instructions;
|
||||
/// The linear buffer of generated IR instructions for the current
|
||||
/// compilation unit.
|
||||
bal_instruction_t *instructions;
|
||||
|
||||
/// Metadata tracking the bit-width (32 or 64 bit) for each variable.
|
||||
bal_bit_width_t *ssa_bit_widths;
|
||||
/// Metadata tracking the bit-width (32 or 64 bit) for each variable.
|
||||
bal_bit_width_t *ssa_bit_widths;
|
||||
|
||||
/// Linear buffer of constants generated in the current compilation unit.
|
||||
bal_constant_t *constants;
|
||||
/// Linear buffer of constants generated in the current compilation unit.
|
||||
bal_constant_t *constants;
|
||||
|
||||
/// The size of the `source_variables` array.
|
||||
size_t source_variables_size;
|
||||
/// The size of the `source_variables` array.
|
||||
size_t source_variables_size;
|
||||
|
||||
/// The size of the `instructions` array.
|
||||
size_t instructions_size;
|
||||
/// The size of the `instructions` array.
|
||||
size_t instructions_size;
|
||||
|
||||
/// The size of the `constants` array.
|
||||
size_t constants_size;
|
||||
/// The size of the `constants` array.
|
||||
size_t constants_size;
|
||||
|
||||
/// The current number of instructions emitted.
|
||||
/// The current number of instructions emitted.
|
||||
///
|
||||
/// This tracks the current position in `instructions` and `ssa_bit_widths`
|
||||
/// arrays.
|
||||
bal_instruction_count_t instruction_count;
|
||||
|
||||
/// The number of constants emitted.
|
||||
///
|
||||
/// This tracks the current position in the `constants` array.
|
||||
bal_constant_count_t constant_count;
|
||||
|
||||
/// The current error state of the Engine.
|
||||
///
|
||||
/// If an operation fails, this field is set to a specific error code.
|
||||
/// See [`bal_opcode_t`]. Once set to an error state, subsequent operation
|
||||
/// on this engine will silently fail until [`bal_engine_reset`] is called.
|
||||
bal_error_t status;
|
||||
|
||||
/* Cold Data */
|
||||
|
||||
/// The base pointer returned during the underlying heap allocation. This
|
||||
/// is required to correctly free the engine's internal arrays.
|
||||
void *arena_base;
|
||||
|
||||
/// The total size of the allocated arena.
|
||||
size_t arena_size;
|
||||
|
||||
/// Handles logging for this engine.
|
||||
bal_logger_t logger;
|
||||
|
||||
} bal_engine_t;
|
||||
|
||||
/// Initializes a Ballistic engine.
|
||||
///
|
||||
/// This tracks the current position in `instructions` and `ssa_bit_widths`
|
||||
/// arrays.
|
||||
bal_instruction_count_t instruction_count;
|
||||
|
||||
/// The number of constants emitted.
|
||||
/// 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.
|
||||
///
|
||||
/// This tracks the current position in the `constants` array.
|
||||
bal_constant_count_t constant_count;
|
||||
|
||||
/// The current error state of the Engine.
|
||||
/// Returns [`BAL_SUCCESS`] if the engine iz ready for use.
|
||||
///
|
||||
/// If an operation fails, this field is set to a specific error code.
|
||||
/// See [`bal_opcode_t`]. Once set to an error state, subsequent operation
|
||||
/// on this engine will silently fail until [`bal_engine_reset`] is called.
|
||||
bal_error_t status;
|
||||
/// # 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);
|
||||
|
||||
/* Cold Data */
|
||||
/// 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);
|
||||
|
||||
/// The base pointer returned during the underlying heap allocation. This
|
||||
/// is required to correctly free the engine's internal arrays.
|
||||
void *arena_base;
|
||||
/// 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);
|
||||
|
||||
/// The total size of the allocated arena.
|
||||
size_t arena_size;
|
||||
/// 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);
|
||||
|
||||
/// Handles logging for this engine.
|
||||
bal_logger_t logger;
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
} 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,
|
||||
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,
|
||||
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);
|
||||
|
||||
/// 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 ***/
|
||||
|
||||
+29
-20
@@ -8,32 +8,41 @@
|
||||
|
||||
#include "bal_attributes.h"
|
||||
|
||||
typedef enum
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
// General Errors.
|
||||
//
|
||||
BAL_SUCCESS = 0,
|
||||
BAL_ERROR_INVALID_ARGUMENT = -1,
|
||||
BAL_ERROR_ALLOCATION_FAILED = -2,
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/// Guest code tried to execute an unaligned instruction.
|
||||
BAL_ERROR_PC_ALIGNMENT = -3,
|
||||
BAL_ERROR_MEMORY_ALIGNMENT = -4,
|
||||
typedef enum
|
||||
{
|
||||
// General Errors.
|
||||
//
|
||||
BAL_SUCCESS = 0,
|
||||
BAL_ERROR_INVALID_ARGUMENT = -1,
|
||||
BAL_ERROR_ALLOCATION_FAILED = -2,
|
||||
|
||||
/// Ballistic tried to access memory it was not allowed to access.
|
||||
BAL_ERROR_MEMORY_FAULT = -5,
|
||||
BAL_ERROR_UNKNOWN_INSTRUCTION = -6,
|
||||
/// Guest code tried to execute an unaligned instruction.
|
||||
BAL_ERROR_PC_ALIGNMENT = -3,
|
||||
BAL_ERROR_MEMORY_ALIGNMENT = -4,
|
||||
|
||||
// IR Errors.
|
||||
//
|
||||
BAL_ERROR_INSTRUCTION_OVERFLOW = -100,
|
||||
/// Ballistic tried to access memory it was not allowed to access.
|
||||
BAL_ERROR_MEMORY_FAULT = -5,
|
||||
BAL_ERROR_UNKNOWN_INSTRUCTION = -6,
|
||||
|
||||
/// The decoded register type does not match the expected type.
|
||||
BAL_ERROR_INCORRECT_REGISTER_TYPE = -101,
|
||||
} bal_error_t;
|
||||
// IR Errors.
|
||||
//
|
||||
BAL_ERROR_INSTRUCTION_OVERFLOW = -100,
|
||||
|
||||
/// Converts the enum into a readable string for error handling.
|
||||
BAL_COLD const char *bal_error_to_string(bal_error_t error);
|
||||
/// The decoded register type does not match the expected type.
|
||||
BAL_ERROR_INCORRECT_REGISTER_TYPE = -101,
|
||||
} 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* BAL_ERRORS_H */
|
||||
|
||||
|
||||
+84
-74
@@ -58,69 +58,74 @@
|
||||
#include "bal_attributes.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
/// Defines the severity of a log message.
|
||||
typedef enum
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
/// Logging is disabled.
|
||||
BAL_LOG_LEVEL_NONE = -1,
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/// Critical errors that likely result in immediate termination or undefined behaviour.
|
||||
BAL_LOG_LEVEL_ERROR = 0,
|
||||
/// Defines the severity of a log message.
|
||||
typedef enum
|
||||
{
|
||||
/// Logging is disabled.
|
||||
BAL_LOG_LEVEL_NONE = -1,
|
||||
|
||||
/// Non-critical issues that may result in degraded performance or functionality loss.
|
||||
BAL_LOG_LEVEL_WARN = 1,
|
||||
/// Critical errors that likely result in immediate termination or undefined behaviour.
|
||||
BAL_LOG_LEVEL_ERROR = 0,
|
||||
|
||||
/// General operational events.
|
||||
BAL_LOG_LEVEL_INFO = 2,
|
||||
/// Non-critical issues that may result in degraded performance or functionality loss.
|
||||
BAL_LOG_LEVEL_WARN = 1,
|
||||
|
||||
/// Information useful for debugging logic errors.
|
||||
BAL_LOG_LEVEL_DEBUG = 3,
|
||||
/// General operational events.
|
||||
BAL_LOG_LEVEL_INFO = 2,
|
||||
|
||||
/// Extreme verbose output.
|
||||
BAL_LOG_LEVEL_TRACE = 4,
|
||||
} bal_log_level_t;
|
||||
/// Information useful for debugging logic errors.
|
||||
BAL_LOG_LEVEL_DEBUG = 3,
|
||||
|
||||
/// Metadata associated with a specific log event.
|
||||
typedef struct
|
||||
{
|
||||
/// Source file where the log occurred.
|
||||
const char *filename;
|
||||
/// Extreme verbose output.
|
||||
BAL_LOG_LEVEL_TRACE = 4,
|
||||
} bal_log_level_t;
|
||||
|
||||
/// The function name where the log occurred.
|
||||
const char *function;
|
||||
/// Metadata associated with a specific log event.
|
||||
typedef struct
|
||||
{
|
||||
/// Source file where the log occurred.
|
||||
const char *filename;
|
||||
|
||||
/// The log level.
|
||||
bal_log_level_t level;
|
||||
/// The function name where the log occurred.
|
||||
const char *function;
|
||||
|
||||
/// The line number where the log occurred
|
||||
int line;
|
||||
} bal_log_data_t;
|
||||
/// The log level.
|
||||
bal_log_level_t level;
|
||||
|
||||
/// 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 line number where the log occurred
|
||||
int line;
|
||||
} bal_log_data_t;
|
||||
|
||||
/// 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;
|
||||
/// 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 callback invoked when a message needs to be logged. If this is `NULL`, logging is
|
||||
/// disabled for this context.
|
||||
bal_log_function_t log;
|
||||
/// 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;
|
||||
|
||||
/// The minimum severity level required for a message to be processed.
|
||||
bal_log_level_t min_level;
|
||||
} bal_logger_t;
|
||||
/// The callback invoked when a message needs to be logged. If this is `NULL`, logging is
|
||||
/// disabled for this context.
|
||||
bal_log_function_t log;
|
||||
|
||||
/// The minimum severity level required for a message to be processed.
|
||||
bal_log_level_t min_level;
|
||||
} bal_logger_t;
|
||||
|
||||
// Remove all log code if log level not defined.
|
||||
//
|
||||
@@ -130,33 +135,34 @@ 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_log_level_t log_level,
|
||||
const char *filename,
|
||||
const char *function,
|
||||
int line,
|
||||
const char *format,
|
||||
...);
|
||||
BAL_COLD void bal_log_message(const bal_logger_t *logger,
|
||||
bal_log_level_t log_level,
|
||||
const char *filename,
|
||||
const char *function,
|
||||
int line,
|
||||
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 ***/
|
||||
|
||||
+126
-114
@@ -8,132 +8,144 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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);
|
||||
|
||||
/// 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
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
/// An opaque pointer defining the state or tracking information for the
|
||||
/// heap.
|
||||
bal_allocator_handle_t handle;
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/// The callback invoked to allocate aligned memory.
|
||||
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);
|
||||
|
||||
/// 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 return memory aligned to at least 64 bytes if
|
||||
/// requested.
|
||||
bal_allocate_function_t allocate;
|
||||
/// 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 callback to release memory.
|
||||
bal_free_function_t free;
|
||||
/// 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;
|
||||
|
||||
} bal_allocator_t;
|
||||
/// The callback invoked to allocate aligned memory.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The implementation must return memory aligned to at least 64 bytes if
|
||||
/// requested.
|
||||
bal_allocate_function_t allocate;
|
||||
|
||||
/// 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 to release memory.
|
||||
bal_free_function_t free;
|
||||
|
||||
/// The callback invoked to perform address translation.
|
||||
bal_translate_function_t translate;
|
||||
} bal_memory_interface_t;
|
||||
} bal_allocator_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);
|
||||
/// 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;
|
||||
|
||||
/// 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);
|
||||
/// The callback invoked to perform address translation.
|
||||
bal_translate_function_t translate;
|
||||
} 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);
|
||||
|
||||
/// 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,
|
||||
bal_memory_interface_t *interface);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/// 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);
|
||||
#endif /* BALLISTIC_MEMORY_H */
|
||||
|
||||
/*** end of 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 ***/
|
||||
|
||||
+109
-100
@@ -8,139 +8,148 @@
|
||||
|
||||
#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"
|
||||
{
|
||||
/// This opcode represents a volatile load of a guest register.
|
||||
OPCODE_GET_REGISTER,
|
||||
OPCODE_CONST,
|
||||
OPCODE_MOV,
|
||||
OPCODE_ADD,
|
||||
OPCODE_SUB,
|
||||
OPCODE_MUL,
|
||||
OPCODE_DIV,
|
||||
OPCODE_AND,
|
||||
OPCODE_XOR,
|
||||
OPCODE_OR_NOT,
|
||||
OPCODE_SHIFT,
|
||||
OPCODE_LOAD,
|
||||
OPCODE_STORE,
|
||||
OPCODE_JUMP,
|
||||
OPCODE_CALL,
|
||||
OPCODE_RETURN,
|
||||
OPCODE_BRANCH_ZERO,
|
||||
OPCODE_BRANCH_NOT_ZERO,
|
||||
OPCODE_TEST_BIT_ZERO,
|
||||
OPCODE_CMP,
|
||||
OPCODE_CMP_COND,
|
||||
OPCODE_TRAP,
|
||||
OPCODE_ENUM_END = 0x7FF, // Force enum to 2 bytes.
|
||||
} bal_opcode_t;
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
/// The register index for X0.
|
||||
BAL_REGISTER_X0 = 0,
|
||||
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;
|
||||
|
||||
/// The register index for X1.
|
||||
BAL_REGISTER_X1 = 1,
|
||||
typedef enum
|
||||
{
|
||||
/// This opcode represents a volatile load of a guest register.
|
||||
OPCODE_GET_REGISTER,
|
||||
OPCODE_CONST,
|
||||
OPCODE_MOV,
|
||||
OPCODE_ADD,
|
||||
OPCODE_SUB,
|
||||
OPCODE_MUL,
|
||||
OPCODE_DIV,
|
||||
OPCODE_AND,
|
||||
OPCODE_XOR,
|
||||
OPCODE_OR_NOT,
|
||||
OPCODE_SHIFT,
|
||||
OPCODE_LOAD,
|
||||
OPCODE_STORE,
|
||||
OPCODE_JUMP,
|
||||
OPCODE_CALL,
|
||||
OPCODE_RETURN,
|
||||
OPCODE_BRANCH_ZERO,
|
||||
OPCODE_BRANCH_NOT_ZERO,
|
||||
OPCODE_TEST_BIT_ZERO,
|
||||
OPCODE_CMP,
|
||||
OPCODE_CMP_COND,
|
||||
OPCODE_TRAP,
|
||||
OPCODE_ENUM_END = 0x7FF, // Force enum to 2 bytes.
|
||||
} bal_opcode_t;
|
||||
|
||||
/// The register index for X2.
|
||||
BAL_REGISTER_X2 = 2,
|
||||
typedef enum
|
||||
{
|
||||
/// The register index for X0.
|
||||
BAL_REGISTER_X0 = 0,
|
||||
|
||||
/// The register index for X3.
|
||||
BAL_REGISTER_X3 = 3,
|
||||
/// The register index for X1.
|
||||
BAL_REGISTER_X1 = 1,
|
||||
|
||||
/// The register index for X4.
|
||||
BAL_REGISTER_X4 = 4,
|
||||
/// The register index for X2.
|
||||
BAL_REGISTER_X2 = 2,
|
||||
|
||||
/// The register index for X5.
|
||||
BAL_REGISTER_X5 = 5,
|
||||
/// The register index for X3.
|
||||
BAL_REGISTER_X3 = 3,
|
||||
|
||||
/// The register index for X6.
|
||||
BAL_REGISTER_X6 = 6,
|
||||
/// The register index for X4.
|
||||
BAL_REGISTER_X4 = 4,
|
||||
|
||||
/// The register index for X7.
|
||||
BAL_REGISTER_X7 = 7,
|
||||
/// The register index for X5.
|
||||
BAL_REGISTER_X5 = 5,
|
||||
|
||||
/// The register index for X8.
|
||||
BAL_REGISTER_X8 = 8,
|
||||
/// The register index for X6.
|
||||
BAL_REGISTER_X6 = 6,
|
||||
|
||||
/// The register index for X9.
|
||||
BAL_REGISTER_X9 = 9,
|
||||
/// The register index for X7.
|
||||
BAL_REGISTER_X7 = 7,
|
||||
|
||||
/// The register index for 10.
|
||||
BAL_REGISTER_X10 = 10,
|
||||
/// The register index for X8.
|
||||
BAL_REGISTER_X8 = 8,
|
||||
|
||||
/// The register index for 11.
|
||||
BAL_REGISTER_X11 = 11,
|
||||
/// The register index for X9.
|
||||
BAL_REGISTER_X9 = 9,
|
||||
|
||||
/// The register index for 12.
|
||||
BAL_REGISTER_X12 = 12,
|
||||
/// The register index for 10.
|
||||
BAL_REGISTER_X10 = 10,
|
||||
|
||||
/// The register index for 13.
|
||||
BAL_REGISTER_X13 = 13,
|
||||
/// The register index for 11.
|
||||
BAL_REGISTER_X11 = 11,
|
||||
|
||||
/// The register index for 14.
|
||||
BAL_REGISTER_X14 = 14,
|
||||
/// The register index for 12.
|
||||
BAL_REGISTER_X12 = 12,
|
||||
|
||||
/// The register index for 15.
|
||||
BAL_REGISTER_X15 = 15,
|
||||
/// The register index for 13.
|
||||
BAL_REGISTER_X13 = 13,
|
||||
|
||||
/// The register index for 16.
|
||||
BAL_REGISTER_X16 = 16,
|
||||
/// The register index for 14.
|
||||
BAL_REGISTER_X14 = 14,
|
||||
|
||||
/// The register index for 17.
|
||||
BAL_REGISTER_X17 = 17,
|
||||
/// The register index for 15.
|
||||
BAL_REGISTER_X15 = 15,
|
||||
|
||||
/// The register index for 18.
|
||||
BAL_REGISTER_X18 = 18,
|
||||
/// The register index for 16.
|
||||
BAL_REGISTER_X16 = 16,
|
||||
|
||||
/// The register index for 19.
|
||||
BAL_REGISTER_X19 = 19,
|
||||
/// The register index for 17.
|
||||
BAL_REGISTER_X17 = 17,
|
||||
|
||||
/// The register index for 20.
|
||||
BAL_REGISTER_X20 = 20,
|
||||
/// The register index for 18.
|
||||
BAL_REGISTER_X18 = 18,
|
||||
|
||||
/// The register index for 21.
|
||||
BAL_REGISTER_X21 = 21,
|
||||
/// The register index for 19.
|
||||
BAL_REGISTER_X19 = 19,
|
||||
|
||||
/// The register index for 22.
|
||||
BAL_REGISTER_X22 = 22,
|
||||
/// The register index for 20.
|
||||
BAL_REGISTER_X20 = 20,
|
||||
|
||||
/// The register index for 23.
|
||||
BAL_REGISTER_X23 = 23,
|
||||
/// The register index for 21.
|
||||
BAL_REGISTER_X21 = 21,
|
||||
|
||||
/// The register index for 24.
|
||||
BAL_REGISTER_X24 = 24,
|
||||
/// The register index for 22.
|
||||
BAL_REGISTER_X22 = 22,
|
||||
|
||||
/// The register index for 25.
|
||||
BAL_REGISTER_X25 = 25,
|
||||
/// The register index for 23.
|
||||
BAL_REGISTER_X23 = 23,
|
||||
|
||||
/// The register index for 26.
|
||||
BAL_REGISTER_X26 = 26,
|
||||
/// The register index for 24.
|
||||
BAL_REGISTER_X24 = 24,
|
||||
|
||||
/// The register index for 27.
|
||||
BAL_REGISTER_X27 = 27,
|
||||
/// The register index for 25.
|
||||
BAL_REGISTER_X25 = 25,
|
||||
|
||||
/// The register index for 28.
|
||||
BAL_REGISTER_X28 = 28,
|
||||
/// The register index for 26.
|
||||
BAL_REGISTER_X26 = 26,
|
||||
|
||||
/// The register index for 29 (Frame Pointer).
|
||||
BAL_REGISTER_X29 = 29,
|
||||
/// The register index for 27.
|
||||
BAL_REGISTER_X27 = 27,
|
||||
|
||||
/// The register index for 30 (Link Register).
|
||||
BAL_REGISTER_X30 = 30,
|
||||
/// The register index for 28.
|
||||
BAL_REGISTER_X28 = 28,
|
||||
|
||||
/// The register index for the Zero Register (XZR)/Stack Pointer (SP).
|
||||
BAL_REGISTER_X31 = 31,
|
||||
} bal_register_index_t;
|
||||
/// The register index for 29 (Frame Pointer).
|
||||
BAL_REGISTER_X29 = 29,
|
||||
|
||||
/// The register index for 30 (Link Register).
|
||||
BAL_REGISTER_X30 = 30,
|
||||
|
||||
/// The register index for the Zero Register (XZR)/Stack Pointer (SP).
|
||||
BAL_REGISTER_X31 = 31,
|
||||
} bal_register_index_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* BALLISTIC_TYPES_H */
|
||||
|
||||
|
||||
@@ -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 {
|
||||
uint16_t index;
|
||||
uint8_t count;
|
||||
} decoder_bucket_t;
|
||||
typedef struct
|
||||
{
|
||||
uint16_t index;
|
||||
uint8_t count;
|
||||
} 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 ***/
|
||||
|
||||
+24
-17
@@ -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"
|
||||
@@ -182,7 +182,7 @@ def derive_operand_type(text: str, hover: str) -> str:
|
||||
h = hover.lower()
|
||||
# Immediate checks
|
||||
if any(
|
||||
k in h for k in ["immediate", "amount", "offset", "index", "label", "shift", "option"]
|
||||
k in h for k in ["immediate", "amount", "offset", "index", "label", "shift", "option"]
|
||||
) or t.startswith("#"):
|
||||
return "BAL_OPERAND_TYPE_IMMEDIATE"
|
||||
|
||||
@@ -227,9 +227,9 @@ def derive_operand_type(text: str, hover: str) -> str:
|
||||
|
||||
|
||||
def parse_operands(
|
||||
asmtemplate: ET.Element,
|
||||
field_map: Dict[str, Tuple[int, int]],
|
||||
explanation_map: Dict[str, str],
|
||||
asmtemplate: ET.Element,
|
||||
field_map: Dict[str, Tuple[int, int]],
|
||||
explanation_map: Dict[str, str],
|
||||
) -> List[Operand]:
|
||||
"""
|
||||
Parses `asmtemplate` to find operands and map them to bit fields.
|
||||
@@ -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:
|
||||
@@ -454,7 +452,7 @@ def derive_opcode(mnemonic: str) -> str:
|
||||
|
||||
|
||||
def generate_hash_table(
|
||||
instructions: List[A64Instruction],
|
||||
instructions: List[A64Instruction],
|
||||
) -> Dict[int, List[A64Instruction]]:
|
||||
buckets: Dict[int, List[A64Instruction]] = {
|
||||
i: [] for i in range(DECODER_HASH_TABLE_SIZE)
|
||||
@@ -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"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)
|
||||
|
||||
@@ -618,7 +626,7 @@ if __name__ == "__main__":
|
||||
|
||||
if candidate_tuple in sequence_map:
|
||||
# Re-use existing sequence[candidate_tuple]
|
||||
start_index: int = sequence_map[candidate_tuple]
|
||||
start_index: int = sequence_map[candidate_tuple]
|
||||
else:
|
||||
# Create new sequence
|
||||
start_index: int = len(flat_candidates)
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user