From e3a0870b86683188abec8947416039f28ae2f2a8 Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Thu, 16 Apr 2026 16:56:45 -0400 Subject: [PATCH] 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 --- include/bal_assembler.h | 262 +++++++++++++++++++----------------- include/bal_assert.h | 17 ++- include/bal_attributes.h | 9 ++ include/bal_decoder.h | 4 +- include/bal_engine.h | 234 +++++++++++++++++--------------- include/bal_errors.h | 49 ++++--- include/bal_logging.h | 158 ++++++++++++---------- include/bal_memory.h | 240 +++++++++++++++++---------------- include/bal_platform.h | 11 ++ include/bal_types.h | 209 ++++++++++++++-------------- src/bal_decoder_table_gen.h | 29 ++-- tools/generate_a64_table.py | 41 +++--- 12 files changed, 689 insertions(+), 574 deletions(-) diff --git a/include/bal_assembler.h b/include/bal_assembler.h index 876a6a30..c1de76d2 100644 --- a/include/bal_assembler.h +++ b/include/bal_assembler.h @@ -28,143 +28,155 @@ #include #include -/// 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 */ diff --git a/include/bal_assert.h b/include/bal_assert.h index 1ffb1c76..aac282cb 100644 --- a/include/bal_assert.h +++ b/include/bal_assert.h @@ -3,8 +3,17 @@ #include -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 diff --git a/include/bal_attributes.h b/include/bal_attributes.h index b0b276d1..14ac1d7e 100644 --- a/include/bal_attributes.h +++ b/include/bal_attributes.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 ***/ diff --git a/include/bal_decoder.h b/include/bal_decoder.h index 36a051e2..70c9cbc0 100644 --- a/include/bal_decoder.h +++ b/include/bal_decoder.h @@ -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 diff --git a/include/bal_engine.h b/include/bal_engine.h index 3eff16e3..6b14c919 100644 --- a/include/bal_engine.h +++ b/include/bal_engine.h @@ -8,6 +8,11 @@ #include "bal_types.h" #include +#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 ***/ diff --git a/include/bal_errors.h b/include/bal_errors.h index 62d3a892..0dec6aea 100644 --- a/include/bal_errors.h +++ b/include/bal_errors.h @@ -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 */ diff --git a/include/bal_logging.h b/include/bal_logging.h index 6e7e6815..a972ddf7 100644 --- a/include/bal_logging.h +++ b/include/bal_logging.h @@ -58,69 +58,74 @@ #include "bal_attributes.h" #include -/// 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 ***/ diff --git a/include/bal_memory.h b/include/bal_memory.h index abd34314..7a1ed9f2 100644 --- a/include/bal_memory.h +++ b/include/bal_memory.h @@ -8,132 +8,144 @@ #include #include -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 ***/ diff --git a/include/bal_platform.h b/include/bal_platform.h index a3f77e58..8045d22f 100644 --- a/include/bal_platform.h +++ b/include/bal_platform.h @@ -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 ***/ diff --git a/include/bal_types.h b/include/bal_types.h index 9ece9ebd..4bc6bf4a 100644 --- a/include/bal_types.h +++ b/include/bal_types.h @@ -8,139 +8,148 @@ #include -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 */ diff --git a/src/bal_decoder_table_gen.h b/src/bal_decoder_table_gen.h index c0b87e13..907c7573 100644 --- a/src/bal_decoder_table_gen.h +++ b/src/bal_decoder_table_gen.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 -#include +#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 ***/ diff --git a/tools/generate_a64_table.py b/tools/generate_a64_table.py index 8ee20f32..27fa5be4 100644 --- a/tools/generate_a64_table.py +++ b/tools/generate_a64_table.py @@ -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 \n") - f.write("#include \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)