summaryrefslogtreecommitdiffstats
path: root/js/src/zydis/Zydis/Formatter.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/zydis/Zydis/Formatter.c646
1 files changed, 646 insertions, 0 deletions
diff --git a/js/src/zydis/Zydis/Formatter.c b/js/src/zydis/Zydis/Formatter.c
new file mode 100644
index 0000000000..3fafd1ed17
--- /dev/null
+++ b/js/src/zydis/Zydis/Formatter.c
@@ -0,0 +1,646 @@
+/***************************************************************************************************
+
+ Zyan Disassembler Library (Zydis)
+
+ Original Author : Florian Bernd, Joel Hoener
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+#include "zydis/Zycore/LibC.h"
+#include "zydis/Zydis/Formatter.h"
+#include "zydis/Zydis/Internal/FormatterATT.h"
+#include "zydis/Zydis/Internal/FormatterIntel.h"
+#include "zydis/Zydis/Internal/String.h"
+
+/* ============================================================================================== */
+/* Constants */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Formatter presets */
+/* ---------------------------------------------------------------------------------------------- */
+
+static const ZydisFormatter* const FORMATTER_PRESETS[ZYDIS_FORMATTER_STYLE_MAX_VALUE + 1] =
+{
+ &FORMATTER_ATT,
+ &FORMATTER_INTEL,
+ &FORMATTER_INTEL_MASM
+};
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Internal functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Helper functions */
+/* ---------------------------------------------------------------------------------------------- */
+
+void ZydisFormatterBufferInit(ZydisFormatterBuffer* buffer, char* user_buffer,
+ ZyanUSize length)
+{
+ ZYAN_ASSERT(buffer);
+ ZYAN_ASSERT(user_buffer);
+ ZYAN_ASSERT(length);
+
+ buffer->is_token_list = ZYAN_FALSE;
+ buffer->string.flags = ZYAN_STRING_HAS_FIXED_CAPACITY;
+ buffer->string.vector.allocator = ZYAN_NULL;
+ buffer->string.vector.element_size = sizeof(char);
+ buffer->string.vector.size = 1;
+ buffer->string.vector.capacity = length;
+ buffer->string.vector.data = user_buffer;
+ *user_buffer = '\0';
+}
+
+void ZydisFormatterBufferInitTokenized(ZydisFormatterBuffer* buffer,
+ ZydisFormatterToken** first_token, void* user_buffer, ZyanUSize length)
+{
+ ZYAN_ASSERT(buffer);
+ ZYAN_ASSERT(first_token);
+ ZYAN_ASSERT(user_buffer);
+ ZYAN_ASSERT(length);
+
+ *first_token = user_buffer;
+ (*first_token)->type = ZYDIS_TOKEN_INVALID;
+ (*first_token)->next = 0;
+
+ user_buffer = (ZyanU8*)user_buffer + sizeof(ZydisFormatterToken);
+ length -= sizeof(ZydisFormatterToken);
+
+ buffer->is_token_list = ZYAN_TRUE;
+ buffer->capacity = length;
+ buffer->string.flags = ZYAN_STRING_HAS_FIXED_CAPACITY;
+ buffer->string.vector.allocator = ZYAN_NULL;
+ buffer->string.vector.element_size = sizeof(char);
+ buffer->string.vector.size = 1;
+ buffer->string.vector.capacity = length;
+ buffer->string.vector.data = user_buffer;
+ *(char*)user_buffer = '\0';
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Initialization */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZydisFormatterInit(ZydisFormatter* formatter, ZydisFormatterStyle style)
+{
+ if (!formatter || (style > ZYDIS_FORMATTER_STYLE_MAX_VALUE))
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZYAN_MEMCPY(formatter, FORMATTER_PRESETS[style], sizeof(*formatter));
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Setter */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZydisFormatterSetProperty(ZydisFormatter* formatter, ZydisFormatterProperty property,
+ ZyanUPointer value)
+{
+ if (!formatter)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZydisNumericBase base = (ZydisNumericBase)(-1);
+ ZyanU8 index = 0xFF;
+
+ switch (property)
+ {
+ case ZYDIS_FORMATTER_PROP_FORCE_SIZE:
+ {
+ formatter->force_memory_size = (value) ? ZYAN_TRUE : ZYAN_FALSE;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_FORCE_SEGMENT:
+ {
+ formatter->force_memory_segment = (value) ? ZYAN_TRUE : ZYAN_FALSE;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_FORCE_RELATIVE_BRANCHES:
+ {
+ formatter->force_relative_branches = (value) ? ZYAN_TRUE : ZYAN_FALSE;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_FORCE_RELATIVE_RIPREL:
+ {
+ formatter->force_relative_riprel = (value) ? ZYAN_TRUE : ZYAN_FALSE;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_PRINT_BRANCH_SIZE:
+ {
+ formatter->print_branch_size = (value) ? ZYAN_TRUE : ZYAN_FALSE;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_DETAILED_PREFIXES:
+ {
+ formatter->detailed_prefixes = (value) ? ZYAN_TRUE : ZYAN_FALSE;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_ADDR_BASE:
+ {
+ if ((ZydisNumericBase)value > ZYDIS_NUMERIC_BASE_MAX_VALUE)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ formatter->addr_base = (ZydisNumericBase)value;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_ADDR_SIGNEDNESS:
+ {
+ if ((ZydisSignedness)value > ZYDIS_SIGNEDNESS_MAX_VALUE)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ formatter->addr_signedness = (ZydisSignedness)value;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_ADDR_PADDING_ABSOLUTE:
+ {
+ formatter->addr_padding_absolute = (ZydisPadding)value;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_ADDR_PADDING_RELATIVE:
+ {
+ formatter->addr_padding_relative = (ZydisPadding)value;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_DISP_BASE:
+ {
+ if ((ZydisNumericBase)value > ZYDIS_NUMERIC_BASE_MAX_VALUE)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ formatter->disp_base = (ZydisNumericBase)value;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_DISP_SIGNEDNESS:
+ {
+ if ((ZydisSignedness)value > ZYDIS_SIGNEDNESS_MAX_VALUE)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ formatter->disp_signedness = (ZydisSignedness)value;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_DISP_PADDING:
+ {
+ if ((ZydisPadding)value == ZYDIS_PADDING_AUTO)
+ {
+ if (formatter->style > ZYDIS_FORMATTER_STYLE_MAX_VALUE)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ formatter->disp_padding = FORMATTER_PRESETS[formatter->style]->disp_padding;
+ }
+ formatter->disp_padding = (ZydisPadding)value;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_IMM_BASE:
+ {
+ if ((ZydisNumericBase)value > ZYDIS_NUMERIC_BASE_MAX_VALUE)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ formatter->imm_base = (ZydisNumericBase)value;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_IMM_SIGNEDNESS:
+ {
+ if ((ZydisSignedness)value > ZYDIS_SIGNEDNESS_MAX_VALUE)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ formatter->imm_signedness = (ZydisSignedness)value;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_IMM_PADDING:
+ {
+ if ((ZydisPadding)value == ZYDIS_PADDING_AUTO)
+ {
+ if (formatter->style > ZYDIS_FORMATTER_STYLE_MAX_VALUE)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ formatter->imm_padding = FORMATTER_PRESETS[formatter->style]->imm_padding;
+ }
+ formatter->imm_padding = (ZydisPadding)value;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_UPPERCASE_PREFIXES:
+ {
+ formatter->case_prefixes = (value) ? ZYDIS_LETTER_CASE_UPPER : ZYDIS_LETTER_CASE_DEFAULT;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_UPPERCASE_MNEMONIC:
+ {
+ formatter->case_mnemonic = (value) ? ZYDIS_LETTER_CASE_UPPER : ZYDIS_LETTER_CASE_DEFAULT;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_UPPERCASE_REGISTERS:
+ {
+ formatter->case_registers = (value) ? ZYDIS_LETTER_CASE_UPPER : ZYDIS_LETTER_CASE_DEFAULT;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_UPPERCASE_TYPECASTS:
+ {
+ formatter->case_typecasts = (value) ? ZYDIS_LETTER_CASE_UPPER : ZYDIS_LETTER_CASE_DEFAULT;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_UPPERCASE_DECORATORS:
+ {
+ formatter->case_decorators = (value) ? ZYDIS_LETTER_CASE_UPPER : ZYDIS_LETTER_CASE_DEFAULT;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_DEC_PREFIX:
+ {
+ base = ZYDIS_NUMERIC_BASE_DEC;
+ index = 0;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_DEC_SUFFIX:
+ {
+ base = ZYDIS_NUMERIC_BASE_DEC;
+ index = 1;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_HEX_UPPERCASE:
+ {
+ formatter->hex_uppercase = (value) ? ZYAN_TRUE : ZYAN_FALSE;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_HEX_PREFIX:
+ {
+ base = ZYDIS_NUMERIC_BASE_HEX;
+ index = 0;
+ break;
+ }
+ case ZYDIS_FORMATTER_PROP_HEX_SUFFIX:
+ {
+ base = ZYDIS_NUMERIC_BASE_HEX;
+ index = 1;
+ break;
+ }
+ default:
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ // Set prefix or suffix
+ if (base != (ZydisNumericBase)(-1))
+ {
+ if (value)
+ {
+ const ZyanUSize len = ZYAN_STRLEN((char*)value);
+ if (len > 10)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ ZYAN_MEMCPY(formatter->number_format[base][index].buffer, (void*)value, len);
+ formatter->number_format[base][index].buffer[len] = '\0';
+ formatter->number_format[base][index].string_data.string.vector.data =
+ formatter->number_format[base][index].buffer;
+ formatter->number_format[base][index].string_data.string.vector.size = len + 1;
+ formatter->number_format[base][index].string =
+ &formatter->number_format[base][index].string_data;
+ } else
+ {
+ formatter->number_format[base][index].string = ZYAN_NULL;
+ }
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZydisFormatterSetHook(ZydisFormatter* formatter, ZydisFormatterFunction type,
+ const void** callback)
+{
+ if (!formatter || !callback || (type > ZYDIS_FORMATTER_FUNC_MAX_VALUE))
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ const void* const temp = *callback;
+
+ // The following code relies on the order of the enum values and the function fields inside
+ // the `ZydisFormatter` struct
+
+#ifdef ZYAN_DEBUG
+ const ZyanUPointer* test = (ZyanUPointer*)(&formatter->func_pre_instruction + type);
+ switch (type)
+ {
+ case ZYDIS_FORMATTER_FUNC_PRE_INSTRUCTION:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_pre_instruction ); break;
+ case ZYDIS_FORMATTER_FUNC_POST_INSTRUCTION:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_post_instruction ); break;
+ case ZYDIS_FORMATTER_FUNC_FORMAT_INSTRUCTION:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_format_instruction); break;
+ case ZYDIS_FORMATTER_FUNC_PRE_OPERAND:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_pre_operand ); break;
+ case ZYDIS_FORMATTER_FUNC_POST_OPERAND:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_post_operand ); break;
+ case ZYDIS_FORMATTER_FUNC_FORMAT_OPERAND_REG:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_format_operand_reg); break;
+ case ZYDIS_FORMATTER_FUNC_FORMAT_OPERAND_MEM:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_format_operand_mem); break;
+ case ZYDIS_FORMATTER_FUNC_FORMAT_OPERAND_PTR:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_format_operand_ptr); break;
+ case ZYDIS_FORMATTER_FUNC_FORMAT_OPERAND_IMM:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_format_operand_imm); break;
+ case ZYDIS_FORMATTER_FUNC_PRINT_MNEMONIC:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_mnemonic ); break;
+ case ZYDIS_FORMATTER_FUNC_PRINT_REGISTER:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_register ); break;
+ case ZYDIS_FORMATTER_FUNC_PRINT_ADDRESS_ABS:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_address_abs ); break;
+ case ZYDIS_FORMATTER_FUNC_PRINT_ADDRESS_REL:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_address_rel ); break;
+ case ZYDIS_FORMATTER_FUNC_PRINT_DISP:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_disp ); break;
+ case ZYDIS_FORMATTER_FUNC_PRINT_IMM:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_imm ); break;
+ case ZYDIS_FORMATTER_FUNC_PRINT_TYPECAST:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_typecast ); break;
+ case ZYDIS_FORMATTER_FUNC_PRINT_SEGMENT:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_segment ); break;
+ case ZYDIS_FORMATTER_FUNC_PRINT_PREFIXES:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_prefixes ); break;
+ case ZYDIS_FORMATTER_FUNC_PRINT_DECORATOR:
+ ZYAN_ASSERT(test == (ZyanUPointer*)&formatter->func_print_decorator ); break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+#endif
+
+ *callback = *(const void**)(&formatter->func_pre_instruction + type);
+ if (!temp)
+ {
+ return ZYAN_STATUS_SUCCESS;
+ }
+ ZYAN_MEMCPY(&formatter->func_pre_instruction + type, &temp, sizeof(ZyanUPointer));
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Formatting */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZydisFormatterFormatInstruction(const ZydisFormatter* formatter,
+ const ZydisDecodedInstruction* instruction, char* buffer, ZyanUSize length,
+ ZyanU64 runtime_address)
+{
+ return ZydisFormatterFormatInstructionEx(formatter, instruction, buffer, length,
+ runtime_address, ZYAN_NULL);
+}
+
+ZyanStatus ZydisFormatterFormatInstructionEx(const ZydisFormatter* formatter,
+ const ZydisDecodedInstruction* instruction, char* buffer, ZyanUSize length,
+ ZyanU64 runtime_address, void* user_data)
+{
+ if (!formatter || !instruction || !buffer || (length == 0))
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZydisFormatterBuffer formatter_buffer;
+ ZydisFormatterBufferInit(&formatter_buffer, buffer, length);
+
+ ZydisFormatterContext context;
+ context.instruction = instruction;
+ context.runtime_address = runtime_address;
+ context.operand = ZYAN_NULL;
+ context.user_data = user_data;
+
+ if (formatter->func_pre_instruction)
+ {
+ ZYAN_CHECK(formatter->func_pre_instruction(formatter, &formatter_buffer, &context));
+ }
+
+ ZYAN_CHECK(formatter->func_format_instruction(formatter, &formatter_buffer, &context));
+
+ if (formatter->func_post_instruction)
+ {
+ ZYAN_CHECK(formatter->func_post_instruction(formatter, &formatter_buffer, &context));
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZydisFormatterFormatOperand(const ZydisFormatter* formatter,
+ const ZydisDecodedInstruction* instruction, ZyanU8 index, char* buffer, ZyanUSize length,
+ ZyanU64 runtime_address)
+{
+ return ZydisFormatterFormatOperandEx(formatter, instruction, index, buffer, length,
+ runtime_address, ZYAN_NULL);
+}
+
+ZyanStatus ZydisFormatterFormatOperandEx(const ZydisFormatter* formatter,
+ const ZydisDecodedInstruction* instruction, ZyanU8 index, char* buffer, ZyanUSize length,
+ ZyanU64 runtime_address, void* user_data)
+{
+ if (!formatter || !instruction || index >= instruction->operand_count || !buffer ||
+ (length == 0))
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZydisFormatterBuffer formatter_buffer;
+ ZydisFormatterBufferInit(&formatter_buffer, buffer, length);
+
+ ZydisFormatterContext context;
+ context.instruction = instruction;
+ context.runtime_address = runtime_address;
+ context.operand = &instruction->operands[index];
+ context.user_data = user_data;
+
+ // We ignore `ZYDIS_STATUS_SKIP_TOKEN` for all operand-functions as it does not make any sense
+ // to skip the only operand printed by this function
+
+ if (formatter->func_pre_operand)
+ {
+ ZYAN_CHECK(formatter->func_pre_operand(formatter, &formatter_buffer, &context));
+ }
+
+ switch (context.operand->type)
+ {
+ case ZYDIS_OPERAND_TYPE_REGISTER:
+ ZYAN_CHECK(formatter->func_format_operand_reg(formatter, &formatter_buffer, &context));
+ break;
+ case ZYDIS_OPERAND_TYPE_MEMORY:
+ ZYAN_CHECK(formatter->func_format_operand_mem(formatter, &formatter_buffer, &context));
+ break;
+ case ZYDIS_OPERAND_TYPE_IMMEDIATE:
+ ZYAN_CHECK(formatter->func_format_operand_imm(formatter, &formatter_buffer, &context));
+ break;
+ case ZYDIS_OPERAND_TYPE_POINTER:
+ ZYAN_CHECK(formatter->func_format_operand_ptr(formatter, &formatter_buffer, &context));
+ break;
+ default:
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (formatter->func_post_operand)
+ {
+ ZYAN_CHECK(formatter->func_post_operand(formatter, &formatter_buffer, &context));
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Tokenizing */
+/* ---------------------------------------------------------------------------------------------- */
+
+ZyanStatus ZydisFormatterTokenizeInstruction(const ZydisFormatter* formatter,
+ const ZydisDecodedInstruction* instruction, void* buffer, ZyanUSize length,
+ ZyanU64 runtime_address, ZydisFormatterTokenConst** token)
+{
+ return ZydisFormatterTokenizeInstructionEx(formatter, instruction, buffer, length,
+ runtime_address, token, ZYAN_NULL);
+}
+
+ZyanStatus ZydisFormatterTokenizeInstructionEx(const ZydisFormatter* formatter,
+ const ZydisDecodedInstruction* instruction, void* buffer, ZyanUSize length,
+ ZyanU64 runtime_address, ZydisFormatterTokenConst** token, void* user_data)
+{
+ if (!formatter || !instruction || !buffer || (length <= sizeof(ZydisFormatterToken)) || !token)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZydisFormatterBuffer formatter_buffer;
+ ZydisFormatterToken* first_token;
+ ZydisFormatterBufferInitTokenized(&formatter_buffer, &first_token, buffer, length);
+
+ ZydisFormatterContext context;
+ context.instruction = instruction;
+ context.runtime_address = runtime_address;
+ context.operand = ZYAN_NULL;
+ context.user_data = user_data;
+
+ if (formatter->func_pre_instruction)
+ {
+ ZYAN_CHECK(formatter->func_pre_instruction(formatter, &formatter_buffer, &context));
+ }
+
+ ZYAN_CHECK(formatter->func_format_instruction(formatter, &formatter_buffer, &context));
+
+ if (formatter->func_post_instruction)
+ {
+ ZYAN_CHECK(formatter->func_post_instruction(formatter, &formatter_buffer, &context));
+ }
+
+ if (first_token->next)
+ {
+ *token = (ZydisFormatterTokenConst*)((ZyanU8*)first_token + sizeof(ZydisFormatterToken) +
+ first_token->next);
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ *token = first_token;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZydisFormatterTokenizeOperand(const ZydisFormatter* formatter,
+ const ZydisDecodedInstruction* instruction, ZyanU8 index, void* buffer, ZyanUSize length,
+ ZyanU64 runtime_address, ZydisFormatterTokenConst** token)
+{
+ return ZydisFormatterTokenizeOperandEx(formatter, instruction, index, buffer, length,
+ runtime_address, token, ZYAN_NULL);
+}
+
+ZyanStatus ZydisFormatterTokenizeOperandEx(const ZydisFormatter* formatter,
+ const ZydisDecodedInstruction* instruction, ZyanU8 index, void* buffer, ZyanUSize length,
+ ZyanU64 runtime_address, ZydisFormatterTokenConst** token, void* user_data)
+{
+ if (!formatter || !instruction || (index >= instruction->operand_count) || !buffer ||
+ (length <= sizeof(ZydisFormatterToken)) || !token)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ ZydisFormatterToken* first_token;
+ ZydisFormatterBuffer formatter_buffer;
+ ZydisFormatterBufferInitTokenized(&formatter_buffer, &first_token, buffer, length);
+
+ ZydisFormatterContext context;
+ context.instruction = instruction;
+ context.runtime_address = runtime_address;
+ context.operand = &instruction->operands[index];
+ context.user_data = user_data;
+
+ // We ignore `ZYDIS_STATUS_SKIP_TOKEN` for all operand-functions as it does not make any sense
+ // to skip the only operand printed by this function
+
+ if (formatter->func_pre_operand)
+ {
+ ZYAN_CHECK(formatter->func_pre_operand(formatter, &formatter_buffer, &context));
+ }
+
+ switch (context.operand->type)
+ {
+ case ZYDIS_OPERAND_TYPE_REGISTER:
+ ZYAN_CHECK(formatter->func_format_operand_reg(formatter, &formatter_buffer, &context));
+ break;
+ case ZYDIS_OPERAND_TYPE_MEMORY:
+ ZYAN_CHECK(formatter->func_format_operand_mem(formatter, &formatter_buffer, &context));
+ break;
+ case ZYDIS_OPERAND_TYPE_IMMEDIATE:
+ ZYAN_CHECK(formatter->func_format_operand_imm(formatter, &formatter_buffer, &context));
+ break;
+ case ZYDIS_OPERAND_TYPE_POINTER:
+ ZYAN_CHECK(formatter->func_format_operand_ptr(formatter, &formatter_buffer, &context));
+ break;
+ default:
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (formatter->func_post_operand)
+ {
+ ZYAN_CHECK(formatter->func_post_operand(formatter, &formatter_buffer, &context));
+ }
+
+ if (first_token->next)
+ {
+ *token = (ZydisFormatterTokenConst*)((ZyanU8*)first_token + sizeof(ZydisFormatterToken) +
+ first_token->next);
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ *token = first_token;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ============================================================================================== */
+
+/* ============================================================================================== */