From 7cefc3564d8e4613294fb0f4371df815b57a3d52 Mon Sep 17 00:00:00 2001 From: omicron Date: Thu, 24 Apr 2025 13:30:17 +0200 Subject: [PATCH] Implement one immediate label reference operand Also adds opcode data for jmp and call --- src/data/opcodes.c | 120 ++++++++++++++++++++++++++++++++++++++++++ src/encoder/encoder.c | 35 ++++++++---- 2 files changed, 145 insertions(+), 10 deletions(-) diff --git a/src/data/opcodes.c b/src/data/opcodes.c index f74f68a..d793d69 100644 --- a/src/data/opcodes.c +++ b/src/data/opcodes.c @@ -138,8 +138,128 @@ opcode_data_t *const opcodes[] = { { .kind = OPERAND_REGISTER, .size = OPERAND_SIZE_64 }, }, }, + // CALL rel32 + &(opcode_data_t) { + .mnemonic = "call", + .opcode = 0xE8, + .opcode_extension = opcode_extension_none, + .encoding_class = ENCODING_DEFAULT, + .operand_count = 1, + .operands = { + { .kind = OPERAND_IMMEDIATE, .size = OPERAND_SIZE_32 }, + }, + }, + // CALL reg64 + &(opcode_data_t) { + .mnemonic = "call", + .opcode = 0xFF, + .opcode_extension = 2, + .encoding_class = ENCODING_DEFAULT, + .rex_w_prefix = true, + .operand_count = 1, + .operands = { + { .kind = OPERAND_REGISTER, .size = OPERAND_SIZE_64 }, + }, + }, + // CALL mem64 + &(opcode_data_t) { + .mnemonic = "call", + .opcode = 0xFF, + .opcode_extension = 2, + .encoding_class = ENCODING_DEFAULT, + .rex_w_prefix = true, + .operand_count = 1, + .operands = { + { .kind = OPERAND_MEMORY, .size = OPERAND_SIZE_64 }, + }, + }, + // JMP rel8 (short jump) + &(opcode_data_t) { + .mnemonic = "jmp", + .opcode = 0xEB, + .opcode_extension = opcode_extension_none, + .encoding_class = ENCODING_DEFAULT, + .operand_count = 1, + .operands = { + { .kind = OPERAND_IMMEDIATE, .size = OPERAND_SIZE_8 }, + }, + }, + // JMP rel16 + &(opcode_data_t) { + .mnemonic = "jmp", + .opcode = 0xE9, + .opcode_extension = opcode_extension_none, + .encoding_class = ENCODING_DEFAULT, + .operand_size_prefix = true, + .operand_count = 1, + .operands = { + { .kind = OPERAND_IMMEDIATE, .size = OPERAND_SIZE_16 }, + }, + }, + // JMP reg16 + &(opcode_data_t) { + .mnemonic = "jmp", + .opcode = 0xFF, + .opcode_extension = 4, + .encoding_class = ENCODING_DEFAULT, + .operand_size_prefix = true, + .operand_count = 1, + .operands = { + { .kind = OPERAND_REGISTER, .size = OPERAND_SIZE_16 }, + }, + }, + + // JMP rel32 (near jump) + &(opcode_data_t) { + .mnemonic = "jmp", + .opcode = 0xE9, + .opcode_extension = opcode_extension_none, + .encoding_class = ENCODING_DEFAULT, + .operand_count = 1, + .operands = { + { .kind = OPERAND_IMMEDIATE, .size = OPERAND_SIZE_32 }, + }, + }, + + // JMP reg32 + &(opcode_data_t) { + .mnemonic = "jmp", + .opcode = 0xFF, + .opcode_extension = 4, + .encoding_class = ENCODING_DEFAULT, + .operand_count = 1, + .operands = { + { .kind = OPERAND_REGISTER, .size = OPERAND_SIZE_32 }, + }, + }, + + // JMP reg64 + &(opcode_data_t) { + .mnemonic = "jmp", + .opcode = 0xFF, + .opcode_extension = 4, + .encoding_class = ENCODING_DEFAULT, + .rex_w_prefix = true, + .operand_count = 1, + .operands = { + { .kind = OPERAND_REGISTER, .size = OPERAND_SIZE_64 }, + }, + }, + + // JMP mem64 + &(opcode_data_t) { + .mnemonic = "jmp", + .opcode = 0xFF, + .opcode_extension = 4, + .encoding_class = ENCODING_DEFAULT, + .rex_w_prefix = true, + .operand_count = 1, + .operands = { + { .kind = OPERAND_MEMORY, .size = OPERAND_SIZE_64 }, + }, + }, nullptr, }; diff --git a/src/encoder/encoder.c b/src/encoder/encoder.c index 0a3a199..ea462c4 100644 --- a/src/encoder/encoder.c +++ b/src/encoder/encoder.c @@ -279,12 +279,9 @@ bool is_operand_match(operand_info_t *info, ast_node_t *operand) { if (child->id == NODE_NUMBER) return (ast_node_number_value(child)->size & info->size) > 0; - else if (child->id == NODE_LABEL_REFERENCE) - return info->size == OPERAND_SIZE_32; - // FIXME: first pass should give us information about the distance of - // the label reference so we can pick a size more appropriately instead - // of just defaulting to 32 bits - break; + else if (child->id == NODE_LABEL_REFERENCE) { + return info->size &= ast_node_reference_value(child)->size; + } } // end OPERAND_IMMEDIATE case } assert(false && "unreachable"); @@ -389,9 +386,9 @@ error_t *encode_one_immediate(encoder_t *encoder, opcode_data_t *opcode, assert(immediate->id == NODE_NUMBER || immediate->id == NODE_LABEL_REFERENCE); + operand_size_t size = opcode->operands[0].size; if (immediate->id == NODE_NUMBER) { uint64_t value = ast_node_number_value(immediate)->value; - operand_size_t size = opcode->operands[0].size; error_t *err = nullptr; switch (size) { case OPERAND_SIZE_8: @@ -411,10 +408,21 @@ error_t *encode_one_immediate(encoder_t *encoder, opcode_data_t *opcode, } return err; } else { - // FIXME: this still assumes references are always 32 bit - uint32_t value = 0xDEADBEEF; - return bytes_append_uint32(encoding, value); + reference_t *reference = ast_node_reference_value(immediate); + switch (size) { + case OPERAND_SIZE_64: + return bytes_append_uint64(encoding, reference->address); + case OPERAND_SIZE_32: + return bytes_append_uint32(encoding, reference->offset); + case OPERAND_SIZE_16: + return bytes_append_uint16(encoding, reference->offset); + case OPERAND_SIZE_8: + return bytes_append_uint8(encoding, reference->offset); + default: + assert(false && "intentionally unhandled"); + } } + __builtin_unreachable(); } error_t *encode_one_memory(encoder_t *encoder, opcode_data_t *opcode, @@ -603,6 +611,13 @@ error_t *encoder_collect_reference_info(encoder_t *encoder, ast_node_t *node, node->value.reference.size = size; } + for (size_t i = 0; i < node->len; ++i) { + error_t *err = encoder_collect_reference_info( + encoder, node->children[i], statement); + if (err) + return err; + } + return nullptr; }